// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include "camera.h"
#include "gpuhelper.h"
#include <GL/glu.h>
#include "Eigen/LU"
using namespace Eigen;
Camera::Camera()
: mViewIsUptodate(false), mProjIsUptodate(false)
{
mViewMatrix.setIdentity();
mFovY = M_PI/3.;
mNearDist = 1.;
mFarDist = 50000.;
mVpX = 0;
mVpY = 0;
setPosition(Vector3f::Constant(100.));
setTarget(Vector3f::Zero());
}
Camera& Camera::operator=(const Camera& other)
{
mViewIsUptodate = false;
mProjIsUptodate = false;
mVpX = other.mVpX;
mVpY = other.mVpY;
mVpWidth = other.mVpWidth;
mVpHeight = other.mVpHeight;
mTarget = other.mTarget;
mFovY = other.mFovY;
mNearDist = other.mNearDist;
mFarDist = other.mFarDist;
mViewMatrix = other.mViewMatrix;
mProjectionMatrix = other.mProjectionMatrix;
return *this;
}
Camera::Camera(const Camera& other)
{
*this = other;
}
Camera::~Camera()
{
}
void Camera::setViewport(uint offsetx, uint offsety, uint width, uint height)
{
mVpX = offsetx;
mVpY = offsety;
mVpWidth = width;
mVpHeight = height;
mProjIsUptodate = false;
}
void Camera::setViewport(uint width, uint height)
{
mVpWidth = width;
mVpHeight = height;
mProjIsUptodate = false;
}
void Camera::setFovY(float value)
{
mFovY = value;
mProjIsUptodate = false;
}
Vector3f Camera::direction(void) const
{
return - (orientation() * Vector3f::UnitZ());
}
Vector3f Camera::up(void) const
{
return orientation() * Vector3f::UnitY();
}
Vector3f Camera::right(void) const
{
return orientation() * Vector3f::UnitX();
}
void Camera::setDirection(const Vector3f& newDirection)
{
// TODO implement it computing the rotation between newDirection and current dir ?
Vector3f up = this->up();
Matrix3f camAxes;
camAxes.col(2) = (-newDirection).normalized();
camAxes.col(0) = up.cross( camAxes.col(2) ).normalized();
camAxes.col(1) = camAxes.col(2).cross( camAxes.col(0) ).normalized();
setOrientation(Quaternionf(camAxes));
mViewIsUptodate = false;
}
void Camera::setTarget(const Vector3f& target)
{
mTarget = target;
if (!mTarget.isApprox(position()))
{
Vector3f newDirection = mTarget - position();
setDirection(newDirection.normalized());
}
}
void Camera::setPosition(const Vector3f& p)
{
mFrame.position = p;
mViewIsUptodate = false;
}
void Camera::setOrientation(const Quaternionf& q)
{
mFrame.orientation = q;
mViewIsUptodate = false;
}
void Camera::setFrame(const Frame& f)
{
mFrame = f;
mViewIsUptodate = false;
}
void Camera::rotateAroundTarget(const Quaternionf& q)
{
Matrix4f mrot, mt, mtm;
// update the transform matrix
updateViewMatrix();
Vector3f t = mViewMatrix * mTarget;
mViewMatrix = Translation3f(t)
* q
* Translation3f(-t)
* mViewMatrix;
Quaternionf qa(mViewMatrix.linear());
qa = qa.conjugate();
setOrientation(qa);
setPosition(- (qa * mViewMatrix.translation()) );
mViewIsUptodate = true;
}
void Camera::localRotate(const Quaternionf& q)
{
float dist = (position() - mTarget).norm();
setOrientation(orientation() * q);
mTarget = position() + dist * direction();
mViewIsUptodate = false;
}
void Camera::zoom(float d)
{
float dist = (position() - mTarget).norm();
if(dist > d)
{
setPosition(position() + direction() * d);
mViewIsUptodate = false;
}
}
void Camera::localTranslate(const Vector3f& t)
{
Vector3f trans = orientation() * t;
setPosition( position() + trans );
setTarget( mTarget + trans );
mViewIsUptodate = false;
}
void Camera::updateViewMatrix(void) const
{
if(!mViewIsUptodate)
{
Quaternionf q = orientation().conjugate();
mViewMatrix.linear() = q.toRotationMatrix();
mViewMatrix.translation() = - (mViewMatrix.linear() * position());
mViewIsUptodate = true;
}
}
const Affine3f& Camera::viewMatrix(void) const
{
updateViewMatrix();
return mViewMatrix;
}
void Camera::updateProjectionMatrix(void) const
{
if(!mProjIsUptodate)
{
mProjectionMatrix.setIdentity();
float aspect = float(mVpWidth)/float(mVpHeight);
float theta = mFovY*0.5;
float range = mFarDist - mNearDist;
float invtan = 1./tan(theta);
mProjectionMatrix(0,0) = invtan / aspect;
mProjectionMatrix(1,1) = invtan;
mProjectionMatrix(2,2) = -(mNearDist + mFarDist) / range;
mProjectionMatrix(3,2) = -1;
mProjectionMatrix(2,3) = -2 * mNearDist * mFarDist / range;
mProjectionMatrix(3,3) = 0;
mProjIsUptodate = true;
}
}
const Matrix4f& Camera::projectionMatrix(void) const
{
updateProjectionMatrix();
return mProjectionMatrix;
}
void Camera::activateGL(void)
{
glViewport(vpX(), vpY(), vpWidth(), vpHeight());
gpu.loadMatrix(projectionMatrix(),GL_PROJECTION);
gpu.loadMatrix(viewMatrix().matrix(),GL_MODELVIEW);
}
Vector3f Camera::unProject(const Vector2f& uv, float depth) const
{
Matrix4f inv = mViewMatrix.inverse().matrix();
return unProject(uv, depth, inv);
}
Vector3f Camera::unProject(const Vector2f& uv, float depth, const Matrix4f& invModelview) const
{
updateViewMatrix();
updateProjectionMatrix();
Vector3f a(2.*uv.x()/float(mVpWidth)-1., 2.*uv.y()/float(mVpHeight)-1., 1.);
a.x() *= depth/mProjectionMatrix(0,0);
a.y() *= depth/mProjectionMatrix(1,1);
a.z() = -depth;
// FIXME /\/|
Vector4f b = invModelview * Vector4f(a.x(), a.y(), a.z(), 1.);
return Vector3f(b.x(), b.y(), b.z());
}