I want to make a camera class with help of the Eigen library. The camera will be used to perform certain calculations or rather the values used to setup the camera will be used later on in multiple matrix calculations. The camera can be defined by the 9D vector and the near/far plane and width/height values alone.
Redundant definitions like a and c are just for convenience as those are the mathematical symbols we use for those vectors.
Main point to be reviewed:
I am not sure if and how to store dependent values that can be derived from other data but will be accessed frequently.
Context:
Derived values like position, up vector, forward vector etc will be needed often, so I figured I store them as well. But whenever one wants to change one of those, the others will have to be recalculated. If I don't store them they will have to be constructed every time I need them. In the big picture, there will be an optimization problem to solve, so the camera will change often.
In this case, would it be more efficient to store them or construct them if needed? If stored, might it be better to use pointers?
I have programming experience in C# but C++ is something I struggle with, especially when to use a pointer, references or value. I am also not sure how to use the Eigen types in an efficient way. I read a little in the documentation but am still unsure.
It currently works as intended but I have concerns it will be either slow or memory consuming if used intensively.
I am using g++ 7.4.0, C++11, Eigen (master branch, as I like the new matrix slicing syntax and the 3.4 seems not so far in the future any more)
Any general advice and references are appreciated as well. Thanks :)
Edit1: Added clarity Edit2: Added example
matrix_types.h
#ifndef MATRIX_TYPES_H_ #define MATRIX_TYPES_H_ #include<Eigen/Dense> typedef Eigen::Matrix<double, 9, 1> Vector9d; typedef Eigen::Matrix<double, 9, 2> Matrix92d; typedef Eigen::Matrix<double, 2, 9> Matrix29d; typedef Eigen::Matrix<double, 9, 9> Matrix9d; typedef Eigen::Matrix<double, 3, 2> Matrix32d; typedef Eigen::Matrix<double, 2, 3> Matrix23d; #endif Camera.h
#ifndef CAMERA_H_ #define CAMERA_H_ #include <Eigen/Dense> #include <Eigen/Geometry> #include "matrix_types.h" class Camera { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW typedef Eigen::Vector3d Vector3d; Camera( const Vector9d& camVector, float width, float height, float near, float far ) : m_width(width), m_height(height), m_near(near), m_far(far) { m_setCamVec(camVector); } Camera( const Vector3d& pos, const Vector3d& forward, const Vector3d& up, float width, float height, float near, float far ) : m_width(width), m_height(height), m_near(near), m_far(far) { m_setCamVec(pos, forward, up); } const Vector3d& Position(){ return m_position; }; const Vector3d& c(){ return Position(); } const Vector3d& Forward(){ return m_forward; }; const Vector3d& a(){ return Forward(); } const Vector3d& up(){ return m_up; }; Matrix32d A(){ Matrix32d n_A; n_A << m_forward, m_forward; return n_A; } Matrix32d R11(){ Matrix32d n_R11; n_R11 << m_r1, m_r1; return n_R11; } Matrix32d R12(){ Matrix32d n_R12; n_R12 << m_r1, m_r2; return n_R12; } float AspectRatio(){ return m_width/m_height; } float r(){ return AspectRatio(); } /// ()-operator so one can do camera() and get the Vector9 of its components const Vector9d& operator () (){ return m_camVec; } /// assignment operator Camera& operator = (const Vector9d& camVec){ m_setCamVec(camVec); return *this; } Camera& operator += (const Vector9d& camOffset){ m_setCamVec(m_camVec + camOffset); return *this; } Camera& operator -= (const Vector9d& camOffset){ m_setCamVec(m_camVec - camOffset); return *this; } void SetPosition(const Vector3d& pos){ m_position = pos; m_camVec(Eigen::seq(0,2)) = pos; } void SetForward(const Vector3d& forw){ m_forward = forw; m_camVec(Eigen::seq(3,5)) = forw; m_recalculateRs(); } void SetUp(const Vector3d& up){ m_up = up; m_camVec(Eigen::seq(6,8)) = up; m_recalculateRs(); } private: Vector9d m_camVec; Vector3d m_position; Vector3d m_forward; Vector3d m_up; Vector3d m_r1; Vector3d m_r2; float m_width; float m_height; float m_near; float m_far; void m_setCamVec(const Vector9d& camVec){ m_camVec = camVec; m_position = m_camVec(Eigen::seq(0,2)); m_forward = m_camVec(Eigen::seq(3,5)); m_up = m_camVec(Eigen::seq(6,8)); m_recalculateRs(); } void m_setCamVec(const Vector3d& pos, const Vector3d& forw, const Vector3d& up){ m_camVec << pos, forw, up; m_position = pos; m_forward = forw; m_up = up; m_recalculateRs(); } void m_recalculateRs(){ m_r1 = (m_forward.cross(m_up)).normalized(); m_r2 = (m_r1.cross(m_forward)).normalized(); } }; // end Camera #endif main.cpp
/* The actual calculations here are nonsensical. Still, the use of the camera here is similar to the way it will be used later on. Meaning, matrices and camera vectors will be used for calculations. The camera will be part of a bigger project and I want to avoid rewriting everything because it was set up stupidly to begin with. */ #include <iostream> #include <cmath> #include <Eigen/Dense> #include <limits> #include "camera.h" int main() { Vector9d camVec {{1.0},{1.0},{5.0},{0.0},{1.0},{0.2},{0.0},{0.0},{1.0}}; Camera cam(camVec, 12.3f, 5.4f, 1.0f, 300.0f); double epsilon = 0.0001; Vector9d bestCamVec = camVec; double opt = std::numeric_limits<double>::max(); for(int j = 0; j < 100; ++j){ Matrix9d P = Matrix9d::Constant(0); Vector9d camEps = Vector9d::Random() * epsilon; cam += camEps; for(int i = 0; i < 1000; ++i){ Eigen::Vector2d p = Eigen::Vector2d::Random(); double d = p.norm(); double alpha = std::atan2(cam.a().cross(cam.up()).norm(), cam.a().dot(cam.up())); double a = cam.a().norm(); double b = (cam.a() + cam.R12()*p).norm(); Eigen::Matrix2d Y = p.asDiagonal(); Eigen::Matrix2d Y_ = Y.inverse(); Matrix92d B; B << b/(d*a*a)*cam.A()*Y_, -std::tan(alpha)/a*cam.R11()*Y, cam.R12()*Y*std::sin(alpha); P += B*B.transpose(); } double det = std::abs(P.determinant()); if(opt > det){ opt = det; bestCamVec = cam(); } } std::cout << "Best camera vector is \n" << bestCamVec << "\n with " << opt; } ```
m_nearandm_far(turn on-Wall-Wextra). I have had a look at how Eigen stores these vectors. They seem to be just "value types", ie anEigen::Vector3dis literally 3doubles (ie 24 bytes) on the stack. If, from my cursory glance, i have understood correctly, then you are worried about recomputing/storingm_r1&m_r2. Don't be, they are only of type Vector3d. \$\endgroup\$main()which shows how you intend to useCameraand I can give a fuller answer. I suspect the answer is: you are prematurely worrying about copying/pointers etc. C++ is extremely good and fast at "value types". So at 2x 24bytes I wouldn't worry too much at all until you have a real example to test with. Why arem_widthandm_heightand the resultingAspectRatio()of typefloat? When everything else is adouble? I would just stick to double. With H/W acceleration they are almost the same speed and there is probably no issue with storage here. \$\endgroup\$constand[[nodiscard]]below, just for style. \$\endgroup\$