1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #include "camera.h"
11 
12 #include "gpuhelper.h"
13 #include <GL/glu.h>
14 
15 #include "Eigen/LU"
16 using namespace Eigen;
17 
Camera()18 Camera::Camera()
19     : mViewIsUptodate(false), mProjIsUptodate(false)
20 {
21     mViewMatrix.setIdentity();
22 
23     mFovY = M_PI/3.;
24     mNearDist = 1.;
25     mFarDist = 50000.;
26 
27     mVpX = 0;
28     mVpY = 0;
29 
30     setPosition(Vector3f::Constant(100.));
31     setTarget(Vector3f::Zero());
32 }
33 
operator =(const Camera & other)34 Camera& Camera::operator=(const Camera& other)
35 {
36     mViewIsUptodate = false;
37     mProjIsUptodate = false;
38 
39     mVpX = other.mVpX;
40     mVpY = other.mVpY;
41     mVpWidth = other.mVpWidth;
42     mVpHeight = other.mVpHeight;
43 
44     mTarget = other.mTarget;
45     mFovY = other.mFovY;
46     mNearDist = other.mNearDist;
47     mFarDist = other.mFarDist;
48 
49     mViewMatrix = other.mViewMatrix;
50     mProjectionMatrix = other.mProjectionMatrix;
51 
52     return *this;
53 }
54 
Camera(const Camera & other)55 Camera::Camera(const Camera& other)
56 {
57     *this = other;
58 }
59 
~Camera()60 Camera::~Camera()
61 {
62 }
63 
64 
setViewport(uint offsetx,uint offsety,uint width,uint height)65 void Camera::setViewport(uint offsetx, uint offsety, uint width, uint height)
66 {
67     mVpX = offsetx;
68     mVpY = offsety;
69     mVpWidth = width;
70     mVpHeight = height;
71 
72     mProjIsUptodate = false;
73 }
74 
setViewport(uint width,uint height)75 void Camera::setViewport(uint width, uint height)
76 {
77     mVpWidth = width;
78     mVpHeight = height;
79 
80     mProjIsUptodate = false;
81 }
82 
setFovY(float value)83 void Camera::setFovY(float value)
84 {
85     mFovY = value;
86     mProjIsUptodate = false;
87 }
88 
direction(void) const89 Vector3f Camera::direction(void) const
90 {
91     return - (orientation() * Vector3f::UnitZ());
92 }
up(void) const93 Vector3f Camera::up(void) const
94 {
95     return orientation() * Vector3f::UnitY();
96 }
right(void) const97 Vector3f Camera::right(void) const
98 {
99     return orientation() * Vector3f::UnitX();
100 }
101 
setDirection(const Vector3f & newDirection)102 void Camera::setDirection(const Vector3f& newDirection)
103 {
104     // TODO implement it computing the rotation between newDirection and current dir ?
105     Vector3f up = this->up();
106 
107     Matrix3f camAxes;
108 
109     camAxes.col(2) = (-newDirection).normalized();
110     camAxes.col(0) = up.cross( camAxes.col(2) ).normalized();
111     camAxes.col(1) = camAxes.col(2).cross( camAxes.col(0) ).normalized();
112     setOrientation(Quaternionf(camAxes));
113 
114     mViewIsUptodate = false;
115 }
116 
setTarget(const Vector3f & target)117 void Camera::setTarget(const Vector3f& target)
118 {
119     mTarget = target;
120     if (!mTarget.isApprox(position()))
121     {
122         Vector3f newDirection = mTarget - position();
123         setDirection(newDirection.normalized());
124     }
125 }
126 
setPosition(const Vector3f & p)127 void Camera::setPosition(const Vector3f& p)
128 {
129     mFrame.position = p;
130     mViewIsUptodate = false;
131 }
132 
setOrientation(const Quaternionf & q)133 void Camera::setOrientation(const Quaternionf& q)
134 {
135     mFrame.orientation = q;
136     mViewIsUptodate = false;
137 }
138 
setFrame(const Frame & f)139 void Camera::setFrame(const Frame& f)
140 {
141   mFrame = f;
142   mViewIsUptodate = false;
143 }
144 
rotateAroundTarget(const Quaternionf & q)145 void Camera::rotateAroundTarget(const Quaternionf& q)
146 {
147     Matrix4f mrot, mt, mtm;
148 
149     // update the transform matrix
150     updateViewMatrix();
151     Vector3f t = mViewMatrix * mTarget;
152 
153     mViewMatrix = Translation3f(t)
154                 * q
155                 * Translation3f(-t)
156                 * mViewMatrix;
157 
158     Quaternionf qa(mViewMatrix.linear());
159     qa = qa.conjugate();
160     setOrientation(qa);
161     setPosition(- (qa * mViewMatrix.translation()) );
162 
163     mViewIsUptodate = true;
164 }
165 
localRotate(const Quaternionf & q)166 void Camera::localRotate(const Quaternionf& q)
167 {
168     float dist = (position() - mTarget).norm();
169     setOrientation(orientation() * q);
170     mTarget = position() + dist * direction();
171     mViewIsUptodate = false;
172 }
173 
zoom(float d)174 void Camera::zoom(float d)
175 {
176     float dist = (position() - mTarget).norm();
177     if(dist > d)
178     {
179         setPosition(position() + direction() * d);
180         mViewIsUptodate = false;
181     }
182 }
183 
localTranslate(const Vector3f & t)184 void Camera::localTranslate(const Vector3f& t)
185 {
186   Vector3f trans = orientation() * t;
187   setPosition( position() + trans );
188   setTarget( mTarget + trans );
189 
190   mViewIsUptodate = false;
191 }
192 
updateViewMatrix(void) const193 void Camera::updateViewMatrix(void) const
194 {
195     if(!mViewIsUptodate)
196     {
197         Quaternionf q = orientation().conjugate();
198         mViewMatrix.linear() = q.toRotationMatrix();
199         mViewMatrix.translation() = - (mViewMatrix.linear() * position());
200 
201         mViewIsUptodate = true;
202     }
203 }
204 
viewMatrix(void) const205 const Affine3f& Camera::viewMatrix(void) const
206 {
207   updateViewMatrix();
208   return mViewMatrix;
209 }
210 
updateProjectionMatrix(void) const211 void Camera::updateProjectionMatrix(void) const
212 {
213   if(!mProjIsUptodate)
214   {
215     mProjectionMatrix.setIdentity();
216     float aspect = float(mVpWidth)/float(mVpHeight);
217     float theta = mFovY*0.5;
218     float range = mFarDist - mNearDist;
219     float invtan = 1./tan(theta);
220 
221     mProjectionMatrix(0,0) = invtan / aspect;
222     mProjectionMatrix(1,1) = invtan;
223     mProjectionMatrix(2,2) = -(mNearDist + mFarDist) / range;
224     mProjectionMatrix(3,2) = -1;
225     mProjectionMatrix(2,3) = -2 * mNearDist * mFarDist / range;
226     mProjectionMatrix(3,3) = 0;
227 
228     mProjIsUptodate = true;
229   }
230 }
231 
projectionMatrix(void) const232 const Matrix4f& Camera::projectionMatrix(void) const
233 {
234   updateProjectionMatrix();
235   return mProjectionMatrix;
236 }
237 
activateGL(void)238 void Camera::activateGL(void)
239 {
240   glViewport(vpX(), vpY(), vpWidth(), vpHeight());
241   gpu.loadMatrix(projectionMatrix(),GL_PROJECTION);
242   gpu.loadMatrix(viewMatrix().matrix(),GL_MODELVIEW);
243 }
244 
245 
unProject(const Vector2f & uv,float depth) const246 Vector3f Camera::unProject(const Vector2f& uv, float depth) const
247 {
248     Matrix4f inv = mViewMatrix.inverse().matrix();
249     return unProject(uv, depth, inv);
250 }
251 
unProject(const Vector2f & uv,float depth,const Matrix4f & invModelview) const252 Vector3f Camera::unProject(const Vector2f& uv, float depth, const Matrix4f& invModelview) const
253 {
254     updateViewMatrix();
255     updateProjectionMatrix();
256 
257     Vector3f a(2.*uv.x()/float(mVpWidth)-1., 2.*uv.y()/float(mVpHeight)-1., 1.);
258     a.x() *= depth/mProjectionMatrix(0,0);
259     a.y() *= depth/mProjectionMatrix(1,1);
260     a.z() = -depth;
261     // FIXME /\/|
262     Vector4f b = invModelview * Vector4f(a.x(), a.y(), a.z(), 1.);
263     return Vector3f(b.x(), b.y(), b.z());
264 }
265