Camera.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include <Eigen/Geometry>
  2. #include <Eigen/Core>
  3. class Camera
  4. {
  5. public:
  6. // m_zoom Zoom of camera lens {1}
  7. // m_angle Field of view angle in degrees {45}
  8. // m_aspect Aspect ratio {1}
  9. // m_near near clipping plane {1e-2}
  10. // m_far far clipping plane {100}
  11. // m_at_dist distance of looking at point {1}
  12. // m_rotation Rotation part of rigid transformation of camera {identity}.
  13. // Note that this seems to the inverse of what's stored in the old
  14. // igl::Camera class.
  15. // m_translation Translation part of rigid transformation of camera
  16. // {(0,0,1)}
  17. double m_zoom, m_angle, m_aspect, m_near, m_far, m_at_dist;
  18. Eigen::Quaterniond m_rotation;
  19. Eigen::Vector3d m_translation;
  20. Camera();
  21. // Return projection matrix that takes relative camera coordinates and
  22. // transforms it to viewport coordinates
  23. //
  24. // Note:
  25. //
  26. // gluPerspective(m_angle,m_aspect,m_near,m_far);
  27. //
  28. // Is equivalent to
  29. //
  30. // glMultMatrixd(v_camera.projection().data());
  31. //
  32. Eigen::Matrix4d projection() const;
  33. // Return an Affine transformation (rigid actually) that takes a world 3d coordinate and
  34. // transforms it into the relative camera coordinates.
  35. Eigen::Affine3d affine() const;
  36. // Return an Affine transformation (rigid actually) that takes relative
  37. // coordinates and tramsforms them into world 3d coordinates.
  38. //
  39. // Note:
  40. //
  41. // gluLookAt(
  42. // eye()(0), eye()(1), eye()(2),
  43. // at()(0), at()(1), at()(2),
  44. // up()(0), up()(1), up()(2));
  45. //
  46. // Is equivalent to
  47. //
  48. // glMultMatrixd(camera.affine().matrix().data());
  49. //
  50. // See also: affine, eye, at, up
  51. Eigen::Affine3d inverse() const;
  52. // Returns world coordinates position of center or "eye" of camera.
  53. Eigen::Vector3d eye() const;
  54. // Returns world coordinate position of a point "eye" is looking at.
  55. Eigen::Vector3d at() const;
  56. // Returns world coordinate unit vector of "up" vector
  57. Eigen::Vector3d up() const;
  58. // Return top right corner of unit plane in relative coordinates, that is
  59. // (w/2,h/2,1)
  60. Eigen::Vector3d unit_plane() const;
  61. // Move dv in the relative coordinate frame of the camera (move the FPS)
  62. //
  63. // Inputs:
  64. // dv (x,y,z) displacement vector
  65. //
  66. void dolly(const Eigen::Vector3d & dv);
  67. // "Scale zoom": Move `eye`, but leave `at`
  68. //
  69. // Input:
  70. // s amount to scale distance to at
  71. void push_away(const double s);
  72. // Aka "Hitchcock", "Vertigo", "Spielberg" or "Trombone" zoom:
  73. // simultaneously dolly while changing angle so that `at` not only stays
  74. // put in relative coordinates but also projected coordinates. That is
  75. //
  76. // Inputs:
  77. // da change in angle in degrees
  78. void dolly_zoom(const double da);
  79. // Turn around eye so that rotation is now q
  80. //
  81. // Inputs:
  82. // q new rotation as quaternion
  83. void turn_eye(const Eigen::Quaterniond & q);
  84. // Orbit around at so that rotation is now q
  85. //
  86. // Inputs:
  87. // q new rotation as quaternion
  88. void orbit(const Eigen::Quaterniond & q);
  89. // Rotate and translate so that camera is situated at "eye" looking at "at"
  90. // with "up" pointing up.
  91. //
  92. // Inputs:
  93. // eye (x,y,z) coordinates of eye position
  94. // at (x,y,z) coordinates of at position
  95. // up (x,y,z) coordinates of up vector
  96. void look_at(
  97. const Eigen::Vector3d & eye,
  98. const Eigen::Vector3d & at,
  99. const Eigen::Vector3d & up);
  100. };
  101. // Implementation
  102. #include <igl/PI.h>
  103. #include <igl/EPS.h>
  104. #include <cmath>
  105. #include <cassert>
  106. Camera::Camera():
  107. m_zoom(1),m_angle(45.0),m_aspect(1),m_near(1e-2),m_far(100),m_at_dist(1),
  108. m_rotation(1,0,0,0),
  109. m_translation(0,0,1)
  110. {
  111. }
  112. Eigen::Matrix4d Camera::projection() const
  113. {
  114. Eigen::Matrix4d P;
  115. using namespace std;
  116. using namespace igl;
  117. // http://stackoverflow.com/a/3738696/148668
  118. const double yScale = tan(PI*0.5 - 0.5*m_angle*PI/180.);
  119. // http://stackoverflow.com/a/14975139/148668
  120. const double xScale = yScale/m_aspect;
  121. P<<
  122. xScale, 0, 0, 0,
  123. 0, yScale, 0, 0,
  124. 0, 0, -(m_far+m_near)/(m_far-m_near), -1,
  125. 0, 0, -2.*m_near*m_far/(m_far-m_near), 0;
  126. return P.transpose();
  127. }
  128. Eigen::Affine3d Camera::affine() const
  129. {
  130. using namespace Eigen;
  131. Affine3d t = Affine3d::Identity();
  132. t.rotate(m_rotation);
  133. t.translate(m_translation);
  134. return t;
  135. }
  136. Eigen::Affine3d Camera::inverse() const
  137. {
  138. using namespace Eigen;
  139. Affine3d t = Affine3d::Identity();
  140. t.translate(-m_translation);
  141. t.rotate(m_rotation.conjugate());
  142. return t;
  143. }
  144. Eigen::Vector3d Camera::eye() const
  145. {
  146. using namespace Eigen;
  147. return affine() * Vector3d(0,0,0);
  148. }
  149. Eigen::Vector3d Camera::at() const
  150. {
  151. using namespace Eigen;
  152. return affine() * (Vector3d(0,0,-1)*m_at_dist);
  153. }
  154. Eigen::Vector3d Camera::up() const
  155. {
  156. using namespace Eigen;
  157. Affine3d t = Affine3d::Identity();
  158. t.rotate(m_rotation);
  159. return t * Vector3d(0,1,0);
  160. }
  161. Eigen::Vector3d Camera::unit_plane() const
  162. {
  163. using namespace igl;
  164. // Distance of center pixel to eye
  165. const double d = 1.0;
  166. const double a = m_aspect;
  167. const double theta = m_angle*PI/180.;
  168. const double w =
  169. 2.*sqrt(-d*d/(a*a*pow(tan(0.5*theta),2.)-1.))*a*tan(0.5*theta);
  170. const double h = w/a;
  171. return Eigen::Vector3d(w*0.5,h*0.5,-d);
  172. }
  173. void Camera::dolly(const Eigen::Vector3d & dv)
  174. {
  175. m_translation += dv;
  176. }
  177. void Camera::push_away(const double s)
  178. {
  179. using namespace Eigen;
  180. using namespace igl;
  181. #ifndef NDEBUG
  182. Vector3d old_at = at();
  183. #endif
  184. const double old_at_dist = m_at_dist;
  185. m_at_dist = old_at_dist * s;
  186. dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
  187. assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
  188. }
  189. void Camera::dolly_zoom(const double da)
  190. {
  191. using namespace std;
  192. using namespace igl;
  193. using namespace Eigen;
  194. #ifndef NDEBUG
  195. Vector3d old_at = at();
  196. #endif
  197. const double old_angle = m_angle;
  198. m_angle += da;
  199. m_angle = min(89.,max(5.,m_angle));
  200. const double s =
  201. (2.*tan(old_angle/2./180.*M_PI)) /
  202. (2.*tan(m_angle/2./180.*M_PI)) ;
  203. const double old_at_dist = m_at_dist;
  204. m_at_dist = old_at_dist * s;
  205. dolly(Vector3d(0,0,1)*(m_at_dist - old_at_dist));
  206. assert((old_at-at()).squaredNorm() < DOUBLE_EPS);
  207. }
  208. void Camera::turn_eye(const Eigen::Quaterniond & q)
  209. {
  210. using namespace Eigen;
  211. using namespace igl;
  212. Vector3d old_eye = eye();
  213. // eye should be fixed
  214. //
  215. // eye_1 = R_1 * t_1 = eye_0
  216. // t_1 = R_1' * eye_0
  217. m_rotation = q;
  218. m_translation = m_rotation.conjugate() * old_eye;
  219. assert((old_eye - eye()).squaredNorm() < DOUBLE_EPS);
  220. }
  221. void Camera::orbit(const Eigen::Quaterniond & q)
  222. {
  223. using namespace Eigen;
  224. using namespace igl;
  225. #ifndef NDEBUG
  226. Vector3d old_at = at();
  227. #endif
  228. // at should be fixed
  229. //
  230. // at_1 = R_1 * t_1 - R_1 * z = at_0
  231. // t_1 = R_1' * (at_0 + R_1 * z)
  232. m_rotation = q;
  233. m_translation =
  234. m_rotation.conjugate() *
  235. (old_at +
  236. m_rotation * Vector3d(0,0,1) * m_at_dist);
  237. assert((old_at - at()).squaredNorm() < DOUBLE_EPS);
  238. }
  239. void Camera::look_at(
  240. const Eigen::Vector3d & eye,
  241. const Eigen::Vector3d & at,
  242. const Eigen::Vector3d & up)
  243. {
  244. using namespace Eigen;
  245. using namespace std;
  246. using namespace igl;
  247. // http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
  248. // Normalize vector from at to eye
  249. Vector3d F = eye-at;
  250. m_at_dist = F.norm();
  251. F.normalize();
  252. // Project up onto plane orthogonal to F and normalize
  253. const Vector3d proj_up = (up-(up.dot(F))*F).normalized();
  254. Quaterniond a,b;
  255. a.setFromTwoVectors(Vector3d(0,0,-1),-F);
  256. b.setFromTwoVectors(a*Vector3d(0,1,0),proj_up);
  257. m_rotation = a*b;
  258. m_translation = m_rotation.conjugate() * eye;
  259. assert( (eye-this->eye()).squaredNorm() < DOUBLE_EPS);
  260. assert((F-(this->eye()-this->at()).normalized()).squaredNorm() <
  261. DOUBLE_EPS);
  262. assert( (at-this->at()).squaredNorm() < DOUBLE_EPS);
  263. assert( (proj_up-this->up()).squaredNorm() < DOUBLE_EPS);
  264. }