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"
16using namespace Eigen;
17
18Camera::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
34Camera& 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
55Camera::Camera(const Camera& other)
56{
57    *this = other;
58}
59
60Camera::~Camera()
61{
62}
63
64
65void 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
75void Camera::setViewport(uint width, uint height)
76{
77    mVpWidth = width;
78    mVpHeight = height;
79
80    mProjIsUptodate = false;
81}
82
83void Camera::setFovY(float value)
84{
85    mFovY = value;
86    mProjIsUptodate = false;
87}
88
89Vector3f Camera::direction(void) const
90{
91    return - (orientation() * Vector3f::UnitZ());
92}
93Vector3f Camera::up(void) const
94{
95    return orientation() * Vector3f::UnitY();
96}
97Vector3f Camera::right(void) const
98{
99    return orientation() * Vector3f::UnitX();
100}
101
102void 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
117void 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
127void Camera::setPosition(const Vector3f& p)
128{
129    mFrame.position = p;
130    mViewIsUptodate = false;
131}
132
133void Camera::setOrientation(const Quaternionf& q)
134{
135    mFrame.orientation = q;
136    mViewIsUptodate = false;
137}
138
139void Camera::setFrame(const Frame& f)
140{
141  mFrame = f;
142  mViewIsUptodate = false;
143}
144
145void 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
166void 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
174void 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
184void 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
193void 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
205const Affine3f& Camera::viewMatrix(void) const
206{
207  updateViewMatrix();
208  return mViewMatrix;
209}
210
211void 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
232const Matrix4f& Camera::projectionMatrix(void) const
233{
234  updateProjectionMatrix();
235  return mProjectionMatrix;
236}
237
238void 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
246Vector3f Camera::unProject(const Vector2f& uv, float depth) const
247{
248    Matrix4f inv = mViewMatrix.inverse().matrix();
249    return unProject(uv, depth, inv);
250}
251
252Vector3f 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