Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

X:/dropbox/visualizationlibrary/src/vlGraphics/Camera.cpp

Go to the documentation of this file.
00001 /**************************************************************************************/
00002 /*                                                                                    */
00003 /*  Visualization Library                                                             */
00004 /*  http://www.visualizationlibrary.org                                               */
00005 /*                                                                                    */
00006 /*  Copyright (c) 2005-2010, Michele Bosi                                             */
00007 /*  All rights reserved.                                                              */
00008 /*                                                                                    */
00009 /*  Redistribution and use in source and binary forms, with or without modification,  */
00010 /*  are permitted provided that the following conditions are met:                     */
00011 /*                                                                                    */
00012 /*  - Redistributions of source code must retain the above copyright notice, this     */
00013 /*  list of conditions and the following disclaimer.                                  */
00014 /*                                                                                    */
00015 /*  - Redistributions in binary form must reproduce the above copyright notice, this  */
00016 /*  list of conditions and the following disclaimer in the documentation and/or       */
00017 /*  other materials provided with the distribution.                                   */
00018 /*                                                                                    */
00019 /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
00020 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
00021 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
00022 /*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
00023 /*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
00024 /*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
00025 /*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
00026 /*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
00027 /*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
00028 /*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
00029 /*                                                                                    */
00030 /**************************************************************************************/
00031 
00032 #include <vlGraphics/Camera.hpp>
00033 #include <vlGraphics/OpenGL.hpp>
00034 #include <vlCore/AABB.hpp>
00035 #include <vlCore/Log.hpp>
00036 #include <vlCore/Say.hpp>
00037 
00038 #undef near
00039 #undef far
00040 
00041 using namespace vl;
00042 
00043 //-----------------------------------------------------------------------------
00044 // Camera
00045 //-----------------------------------------------------------------------------
00046 Camera::Camera()
00047 {
00048   VL_DEBUG_SET_OBJECT_NAME()
00049   mFrustum.planes().resize(6);
00050   mFOV = 60.0;
00051   mNearPlane = (real)0.05;
00052   mFarPlane  = (real)10000.0;
00053   mLeft = mRight = mTop = mBottom = -1;
00054   mViewport = new Viewport;
00055 
00056   mProjectionMatrix  = mat4::getPerspective(fov(), 640.0f/480.0f, nearPlane(), farPlane());
00057   mProjectionType = PMT_PerspectiveProjection;
00058 }
00059 //-----------------------------------------------------------------------------
00060 void Camera::applyModelViewMatrix(const mat4& model_matrix) const
00061 {
00062   /* some OpenGL drivers (ATI) require this instead of the more general (and mathematically correct) viewMatrix() */
00063   mat4 viewm = viewMatrix();
00064   viewm.e(3,0) = 0.0;
00065   viewm.e(3,1) = 0.0;
00066   viewm.e(3,2) = 0.0;
00067   viewm.e(3,3) = 1.0;
00068 
00069   glMatrixMode(GL_MODELVIEW);
00070 #if 0
00071   VL_glLoadMatrix( viewm.ptr() );
00072   VL_glMultMatrix( matrix.ptr() );
00073 #elif 0
00074   viewm = viewm * matrix;
00075   VL_glLoadMatrix( viewm.ptr() );
00076 #else
00077   VL_glLoadMatrix( (viewm * model_matrix).ptr() );
00078 #endif
00079 }
00080 //-----------------------------------------------------------------------------
00081 void Camera::applyProjMatrix() const
00082 {
00083   // projection matrix
00084   glMatrixMode( GL_PROJECTION );
00085   VL_glLoadMatrix( projectionMatrix().ptr() );
00086 }
00087 //-----------------------------------------------------------------------------
00088 void Camera::applyViewMatrix() const
00089 {
00090   /* some OpenGL drivers (ATI) require this instead of the more general (and mathematically correct) viewMatrix() */
00091   mat4 viewm = viewMatrix();
00092   viewm.e(3,0) = 0.0;
00093   viewm.e(3,1) = 0.0;
00094   viewm.e(3,2) = 0.0;
00095   viewm.e(3,3) = 1.0;
00096   glMatrixMode(GL_MODELVIEW);
00097   VL_glLoadMatrix( viewm.ptr() );
00098 }
00099 //-----------------------------------------------------------------------------
00100 void Camera::computeNearFarOptimizedProjMatrix(const Sphere& scene_bounding_sphere)
00101 {
00102   // near/far clipping planes optimization
00103   if (!scene_bounding_sphere.isNull())
00104   {
00105     // transform the sphere in camera coordinates
00106     Sphere camera_sphere;
00107     scene_bounding_sphere.transformed(camera_sphere, viewMatrix());
00108 
00109     // visible objects are in the negative z, but we need a positive distance for the near and far clipping planes
00110     mNearPlane = -(camera_sphere.center().z() + camera_sphere.radius());
00111     mFarPlane  = -(camera_sphere.center().z() - camera_sphere.radius());
00112 
00113     // clamp to positive epsilon: can't let near and far clipping planes go behind the camera!
00114     real epsilon = camera_sphere.radius() / 1000.0f;
00115     mFarPlane  = max(mFarPlane,  epsilon * 2); // alway more than the near
00116     mNearPlane = max(mNearPlane, epsilon * 1);
00117 
00118     switch(projectionMatrixType())
00119     {
00120     case PMT_OrthographicProjection: setProjectionOrtho(mLeft, mRight, mBottom, mTop, mNearPlane, mFarPlane);
00121       break;
00122     case PMT_PerspectiveProjection:  setProjectionPerspective(); 
00123       break;
00124 
00125     // we cannot do this: if we change the near plane we have to recompute also left, right, bottom and top!
00126     // case PMT_PerspectiveProjectionFrustum: setProjectionFrustum(mLeft, mRight, mBottom, mTop, mNearPlane, mFarPlane); 
00127     //   break;
00128 
00129     default:
00130       Log::bug("Camera::computeNearFarOptimizedProjMatrix() called on unsupported projection type.\n");
00131     }
00132   }
00133 }
00134 //-----------------------------------------------------------------------------
00135 void Camera::adjustView(const AABB& aabb, const vec3& dir, const vec3& up, real bias)
00136 {
00137   VL_CHECK(bias >= 0)
00138   VL_CHECK(!aabb.isNull())
00139   if (bias < 0)
00140     vl::Log::bug("Camera::adjustView(): 'bias' must be >= 0.\n");
00141 
00142   vec3 center = aabb.center();
00143 
00144   Sphere sphere(aabb);
00145   const vec3& C = modelingMatrix().getT();
00146   const vec3& V = -modelingMatrix().getZ();
00147   const real  R = sphere.radius();
00148 
00149   // extract the frustum planes based on the current view and projection matrices
00150   mat4 viewproj = projectionMatrix() * viewMatrix();
00151   Frustum frustum; frustum.planes().resize(6);
00152   extractPlanes( &frustum.planes()[0], viewproj );
00153   // iterate over left/right/top/bottom clipping planes. the planes are in world coords.
00154   real max_t = 0;
00155   for(int i=0; i<4; ++i)
00156   {
00157     const vec3& O = frustum.plane(i).origin() * frustum.plane(i).normal();
00158     const vec3& N = frustum.plane(i).normal();
00159     real t = - (R + dot(O,N) - dot(C,N)) / dot(N,V);
00160     VL_CHECK(t>=0)
00161     if (t > max_t)
00162       max_t = t;
00163   }
00164   real dist = max_t;
00165   mat4 m = mat4::getLookAt(center+dir*dist*bias,center,up);
00166   setViewMatrix(m);
00167 }
00168 //-----------------------------------------------------------------------------
00169 void Camera::computeFrustumPlanes()
00170 {
00171   // build modelview matrix
00172   mat4 viewproj = projectionMatrix() * viewMatrix();
00173   // frustum plane extraction
00174   mFrustum.planes().resize(6);
00175   extractPlanes( &mFrustum.planes()[0], viewproj );
00176 }
00177 //-----------------------------------------------------------------------------
00178 void Camera::setProjectionFrustum(real left, real right, real bottom, real top, real near, real far)
00179 {
00180   // see http://www.opengl.org/resources/faq/technical/transformations.htm
00181   setFOV( 2.0f*atan((top-bottom)*0.5f/near) );
00182   setNearPlane(near);
00183   setFarPlane(far);
00184   setProjectionMatrix(mat4::getFrustum(left, right, bottom, top, near, far), PMT_PerspectiveProjectionFrustum);
00185 }
00186 //-----------------------------------------------------------------------------
00187 void Camera::setProjectionPerspective(real fov, real near, real far)
00188 {
00189   setFOV(fov);
00190   setNearPlane(near);
00191   setFarPlane(far);
00192   setProjectionMatrix(mat4::getPerspective(fov, aspectRatio(), near, far), PMT_PerspectiveProjection);
00193 }
00194 //-----------------------------------------------------------------------------
00195 void Camera::setProjectionPerspective()
00196 {
00197   setProjectionMatrix(mat4::getPerspective(fov(), aspectRatio(), nearPlane(), farPlane()), PMT_PerspectiveProjection);
00198 }
00199 //-----------------------------------------------------------------------------
00200 void Camera::setProjectionOrtho()
00201 {
00202   mLeft   = 0;
00203   mRight  = (real)mViewport->width();
00204   mBottom = 0;
00205   mTop    = (real)mViewport->height();
00206   mFOV = -1;
00207   setProjectionMatrix( mat4::getOrtho( mLeft, mRight, mBottom, mTop, mNearPlane, mFarPlane), PMT_OrthographicProjection );
00208 }
00209 //-----------------------------------------------------------------------------
00210 void Camera::setProjectionOrtho(real left, real right, real bottom, real top, real znear, real zfar)
00211 {
00212   mLeft   = left;
00213   mRight  = right;
00214   mBottom = bottom;
00215   mTop    = top;
00216   mFOV = -1;
00217   mNearPlane = znear;
00218   mFarPlane  = zfar;
00219   setProjectionMatrix( mat4::getOrtho( mLeft, mRight, mBottom, mTop, mNearPlane, mFarPlane), PMT_OrthographicProjection );
00220 }
00221 //-----------------------------------------------------------------------------
00222 void Camera::setProjectionOrtho(real offset)
00223 {
00224   mLeft   = offset;
00225   mRight  = viewport()->width() + offset;
00226   mBottom = offset;
00227   mTop    = viewport()->height() + offset;
00228   mFOV = -1;
00229   mNearPlane = -1;
00230   mFarPlane  = +1;
00231   setProjectionMatrix( mat4::getOrtho( mLeft, mRight, mBottom, mTop, mNearPlane, mFarPlane), PMT_OrthographicProjection );
00232 }
00233 //-----------------------------------------------------------------------------
00234 void Camera::setViewMatrixLookAt( const vec3& eye, const vec3& at, const vec3& up)
00235 {
00236   // note: this sets both the local matrix and the view matrix
00237   setViewMatrix( mat4::getLookAt(eye, at, up) );
00238 }
00239 //-----------------------------------------------------------------------------
00240 void Camera::getViewMatrixAsLookAt( vec3& eye, vec3& at, vec3& up, vec3& right) const
00241 {
00242   mModelingMatrix.getAsLookAtModeling(eye, at, up, right);
00243 }
00244 //-----------------------------------------------------------------------------
00245 bool Camera::project(const vec4& in, vec4& out) const
00246 {
00247   out = mProjectionMatrix * mViewMatrix * in;
00248 
00249   if (out.w() == 0.0f)
00250     return false;
00251 
00252   out.x() /= out.w();
00253   out.y() /= out.w();
00254   out.z() /= out.w();
00255 
00256   // map to range 0-1
00257   out.x() = out.x() * 0.5f + 0.5f;
00258   out.y() = out.y() * 0.5f + 0.5f;
00259   out.z() = out.z() * 0.5f + 0.5f;
00260 
00261   // map to viewport
00262   out.x() = out.x() * mViewport->width()  + mViewport->x();
00263   out.y() = out.y() * mViewport->height() + mViewport->y();
00264   return true;
00265 }
00266 //-----------------------------------------------------------------------------
00267 bool Camera::unproject(const vec3& win, vec4& out) const
00268 {
00269     vec4 v;
00270     v.x() = win.x();
00271     v.y() = win.y();
00272     v.z() = win.z();
00273     v.w() = 1.0;
00274 
00275     // map from viewport to 0-1
00276     v.x() = (v.x() - mViewport->x()) / mViewport->width();
00277     v.y() = (v.y() - mViewport->y()) / mViewport->height();
00278 
00279     // map to range -1 to 1
00280     v.x() = v.x() * 2.0f - 1.0f;
00281     v.y() = v.y() * 2.0f - 1.0f;
00282     v.z() = v.z() * 2.0f - 1.0f;
00283 
00284     real det=0;
00285     mat4 inverse = (mProjectionMatrix * mViewMatrix).getInverse(&det);
00286     if (!det)
00287       return false;
00288 
00289     v = inverse * v;
00290     if (v.w() == 0.0)
00291       return false;
00292 
00293     out = v / v.w();
00294     return true;
00295 }
00296 //-----------------------------------------------------------------------------
00297 bool Camera::unproject(std::vector<vec3>& win) const
00298 {
00299   real det=0;
00300   mat4 inverse = (mProjectionMatrix * mViewMatrix).getInverse(&det);
00301   if (!det)
00302     return false;
00303 
00304   bool ok = true;
00305   for(unsigned i=0; i<win.size(); ++i)
00306   {
00307     vec4 v;
00308     v = vec4( win[i], 1.0 );
00309 
00310     // map from viewport to 0-1
00311     v.x() = (v.x() - mViewport->x()) / mViewport->width();
00312     v.y() = (v.y() - mViewport->y()) / mViewport->height();
00313 
00314     // map to range -1 to 1
00315     v.x() = v.x() * 2.0f - 1.0f;
00316     v.y() = v.y() * 2.0f - 1.0f;
00317     v.z() = v.z() * 2.0f - 1.0f;
00318 
00319     v = inverse * v;
00320     if (v.w() == 0.0)
00321     {
00322       ok = false;
00323       continue;
00324     }
00325 
00326     v = v / v.w();
00327     win[i] = v.xyz();
00328   }
00329   return ok;
00330 }
00331 //-----------------------------------------------------------------------------
00332 Ray Camera::computeRay(int winx, int winy)
00333 {
00334   vl::vec4 out;
00335   if (!unproject( vl::vec3((real)winx,(real)winy,0), out ))
00336     return Ray();
00337   else
00338   {
00339     vl::Ray ray;
00340     ray.setOrigin(out.xyz());
00341     ray.setDirection( (out.xyz() - modelingMatrix().getT()).normalize() );
00342     return ray;
00343   }
00344 }
00345 //-----------------------------------------------------------------------------
00346 Frustum Camera::computeRayFrustum(int winx, int winy)
00347 {
00348   /*
00349       n3
00350     D-----C
00351     |     |
00352   n4|  O  |n2
00353     |     |
00354     A-----B
00355       n1
00356   */
00357   // compute the frustum passing through the adjacent pixels
00358   vl::vec4 A1,B1,C1,D1;
00359   vl::vec4 A2,B2,C2,D2;
00360   unproject( vl::vec3((real)winx-1,(real)winy-1,0),    A1 );
00361   unproject( vl::vec3((real)winx+1,(real)winy-1,0),    B1 );
00362   unproject( vl::vec3((real)winx+1,(real)winy+1,0),    C1 );
00363   unproject( vl::vec3((real)winx-1,(real)winy+1,0),    D1 );
00364   unproject( vl::vec3((real)winx-1,(real)winy-1,0.1f), A2 );
00365   unproject( vl::vec3((real)winx+1,(real)winy-1,0.1f), B2 );
00366   unproject( vl::vec3((real)winx+1,(real)winy+1,0.1f), C2 );
00367   unproject( vl::vec3((real)winx-1,(real)winy+1,0.1f), D2 );
00368 
00369   vec3 n1 = -cross(A2.xyz()-A1.xyz(),B1.xyz()-A1.xyz());
00370   vec3 n2 = -cross(B2.xyz()-B1.xyz(),C1.xyz()-B1.xyz());
00371   vec3 n3 = -cross(C2.xyz()-C1.xyz(),D1.xyz()-C1.xyz());
00372   vec3 n4 = -cross(D2.xyz()-D1.xyz(),A1.xyz()-D1.xyz());
00373   Frustum frustum;
00374   frustum.planes().push_back( Plane( A1.xyz(), n1 ) );
00375   frustum.planes().push_back( Plane( B1.xyz(), n2 ) );
00376   frustum.planes().push_back( Plane( C1.xyz(), n3 ) );
00377   frustum.planes().push_back( Plane( D1.xyz(), n4 ) );
00378   return frustum;
00379 }
00380 //-----------------------------------------------------------------------------

Visualization Library 2011.09.1160 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu May 2 2013 13:40:35.
Permission is granted to use this page to write and publish articles regarding Visualization Library.