| // 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()); |
| } |