camera.cpp
Go to the documentation of this file.
00001 /****************************************************************************
00002 
00003  Copyright (C) 2002-2013 Gilles Debunne. All rights reserved.
00004 
00005  This file is part of the QGLViewer library version 2.4.0.
00006 
00007  http://www.libqglviewer.com - contact@libqglviewer.com
00008 
00009  This file may be used under the terms of the GNU General Public License 
00010  versions 2.0 or 3.0 as published by the Free Software Foundation and
00011  appearing in the LICENSE file included in the packaging of this file.
00012  In addition, as a special exception, Gilles Debunne gives you certain 
00013  additional rights, described in the file GPL_EXCEPTION in this package.
00014 
00015  libQGLViewer uses dual licensing. Commercial/proprietary software must
00016  purchase a libQGLViewer Commercial License.
00017 
00018  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00019  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 *****************************************************************************/
00022 
00023 #include "domUtils.h"
00024 #include "camera.h"
00025 #include "qglviewer.h"
00026 
00027 using namespace std;
00028 using namespace qglviewer;
00029 
00037 Camera::Camera()
00038   : fieldOfView_(M_PI/4.0f)
00039 {
00040   // #CONNECTION# Camera copy constructor
00041   interpolationKfi_ = new KeyFrameInterpolator;
00042   // Requires the interpolationKfi_
00043   setFrame(new ManipulatedCameraFrame());
00044 
00045   // #CONNECTION# All these default values identical in initFromDOMElement.
00046 
00047   // Requires fieldOfView() to define focusDistance()
00048   setSceneRadius(1.0);
00049 
00050   // Initial value (only scaled after this)
00051   orthoCoef_ = tan(fieldOfView()/2.0);
00052 
00053   // Also defines the revolveAroundPoint(), which changes orthoCoef_. Requires a frame().
00054   setSceneCenter(Vec(0.0, 0.0, 0.0));
00055 
00056   // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to projectionMatrix_ below.
00057   setType(PERSPECTIVE);
00058 
00059   // #CONNECTION# initFromDOMElement default values
00060   setZNearCoefficient(0.005f);
00061   setZClippingCoefficient(sqrt(3.0));
00062 
00063   // Dummy values
00064   setScreenWidthAndHeight(600, 400);
00065 
00066   // Stereo parameters
00067   setIODistance(0.062f);
00068   setPhysicalScreenWidth(0.5f);
00069   // focusDistance is set from setFieldOfView()
00070 
00071   // #CONNECTION# Camera copy constructor
00072   for (unsigned short j=0; j<16; ++j)
00073     {
00074       modelViewMatrix_[j] = ((j%5 == 0) ? 1.0 : 0.0);
00075       // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost everywhere.
00076       projectionMatrix_[j] = 0.0;
00077     }
00078   computeProjectionMatrix();
00079 }
00080 
00085 Camera::~Camera()
00086 {
00087   delete frame_;
00088   delete interpolationKfi_;
00089 }
00090 
00091 
00093 Camera::Camera(const Camera& camera)
00094  : QObject()
00095 {
00096   // #CONNECTION# Camera constructor
00097   interpolationKfi_ = new KeyFrameInterpolator;
00098   // Requires the interpolationKfi_
00099   setFrame(new ManipulatedCameraFrame());
00100 
00101   for (unsigned short j=0; j<16; ++j)
00102     {
00103       modelViewMatrix_[j] = ((j%5 == 0) ? 1.0 : 0.0);
00104       // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost everywhere.
00105       projectionMatrix_[j] = 0.0;
00106     }
00107 
00108   (*this)=camera;
00109 }
00110 
00123 Camera& Camera::operator=(const Camera& camera)
00124 {
00125   setScreenWidthAndHeight(camera.screenWidth(), camera.screenHeight());
00126   setFieldOfView(camera.fieldOfView());
00127   setSceneRadius(camera.sceneRadius());
00128   setSceneCenter(camera.sceneCenter());
00129   setZNearCoefficient(camera.zNearCoefficient());
00130   setZClippingCoefficient(camera.zClippingCoefficient());
00131   setType(camera.type());
00132 
00133   // Stereo parameters
00134   setIODistance(camera.IODistance());
00135   setFocusDistance(camera.focusDistance());
00136   setPhysicalScreenWidth(camera.physicalScreenWidth());
00137 
00138   orthoCoef_ = camera.orthoCoef_;
00139 
00140   // frame_ and interpolationKfi_ pointers are not shared.
00141   frame_->setReferenceFrame(NULL);
00142   frame_->setPosition(camera.position());
00143   frame_->setOrientation(camera.orientation());
00144 
00145   interpolationKfi_->resetInterpolation();
00146 
00147   kfi_ = camera.kfi_;
00148 
00149   computeProjectionMatrix();
00150   computeModelViewMatrix();
00151 
00152   return *this;
00153 }
00154 
00164 void Camera::setScreenWidthAndHeight(int width, int height)
00165 {
00166   // Prevent negative and zero dimensions that would cause divisions by zero.
00167         screenWidth_  = width > 0 ? width : 1;
00168         screenHeight_ = height > 0 ? height : 1;
00169 }
00170 
00208 float Camera::zNear() const
00209 {
00210   float z = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius();
00211 
00212   // Prevents negative or null zNear values.
00213   const float zMin = zNearCoefficient() * zClippingCoefficient() * sceneRadius();
00214   if (z < zMin)
00215     switch (type())
00216       {
00217       case Camera::PERSPECTIVE  : z = zMin; break;
00218       case Camera::ORTHOGRAPHIC : z = 0.0;  break;
00219       }
00220   return z;
00221 }
00222 
00232 float Camera::zFar() const
00233 {
00234   return distanceToSceneCenter() + zClippingCoefficient()*sceneRadius();
00235 }
00236 
00246 void Camera::setType(Type type)
00247 {
00248   // make ORTHOGRAPHIC frustum fit PERSPECTIVE (at least in plane normal to viewDirection(), passing
00249   // through RAP). Done only when CHANGING type since orthoCoef_ may have been changed with a
00250   // setRevolveAroundPoint() in the meantime.
00251   if ( (type == Camera::ORTHOGRAPHIC) && (type_ == Camera::PERSPECTIVE) )
00252     orthoCoef_ = tan(fieldOfView()/2.0);
00253   type_ = type;
00254 }
00255 
00269 void Camera::setFrame(ManipulatedCameraFrame* const mcf)
00270 {
00271   if (!mcf)
00272     return;
00273 
00274   frame_ = mcf;
00275   interpolationKfi_->setFrame(frame());
00276 }
00277 
00280 float Camera::distanceToSceneCenter() const
00281 {
00282   return fabs((frame()->coordinatesOf(sceneCenter())).z);
00283 }
00284 
00285 
00301 void Camera::getOrthoWidthHeight(GLdouble& halfWidth, GLdouble& halfHeight) const
00302 {
00303   const float dist = orthoCoef_ * fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
00304   //#CONNECTION# fitScreenRegion
00305   halfWidth  = dist * ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
00306   halfHeight = dist * ((aspectRatio() < 1.0) ? 1.0/aspectRatio() : 1.0);
00307 }
00308 
00309 
00327 void Camera::computeProjectionMatrix() const
00328 {
00329   const float ZNear = zNear();
00330   const float ZFar  = zFar();
00331 
00332   switch (type())
00333     {
00334     case Camera::PERSPECTIVE:
00335       {
00336         // #CONNECTION# all non null coefficients were set to 0.0 in constructor.
00337         const float f = 1.0/tan(fieldOfView()/2.0);
00338         projectionMatrix_[0]  = f/aspectRatio();
00339         projectionMatrix_[5]  = f;
00340         projectionMatrix_[10] = (ZNear + ZFar) / (ZNear - ZFar);
00341         projectionMatrix_[11] = -1.0;
00342         projectionMatrix_[14] = 2.0 * ZNear * ZFar / (ZNear - ZFar);
00343         projectionMatrix_[15] = 0.0;
00344         // same as gluPerspective( 180.0*fieldOfView()/M_PI, aspectRatio(), zNear(), zFar() );
00345         break;
00346       }
00347     case Camera::ORTHOGRAPHIC:
00348       {
00349         GLdouble w, h;
00350         getOrthoWidthHeight(w,h);
00351         projectionMatrix_[0]  = 1.0/w;
00352         projectionMatrix_[5]  = 1.0/h;
00353         projectionMatrix_[10] = -2.0/(ZFar - ZNear);
00354         projectionMatrix_[11] = 0.0;
00355         projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear);
00356         projectionMatrix_[15] = 1.0;
00357         // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
00358         break;
00359       }
00360     }
00361 }
00362 
00373 void Camera::computeModelViewMatrix() const
00374 {
00375   const Quaternion q = frame()->orientation();
00376 
00377   const double q00 = 2.0l * q[0] * q[0];
00378   const double q11 = 2.0l * q[1] * q[1];
00379   const double q22 = 2.0l * q[2] * q[2];
00380 
00381   const double q01 = 2.0l * q[0] * q[1];
00382   const double q02 = 2.0l * q[0] * q[2];
00383   const double q03 = 2.0l * q[0] * q[3];
00384 
00385   const double q12 = 2.0l * q[1] * q[2];
00386   const double q13 = 2.0l * q[1] * q[3];
00387 
00388   const double q23 = 2.0l * q[2] * q[3];
00389 
00390   modelViewMatrix_[0] = 1.0l - q11 - q22;
00391   modelViewMatrix_[1] =        q01 - q23;
00392   modelViewMatrix_[2] =        q02 + q13;
00393   modelViewMatrix_[3] = 0.0l;
00394 
00395   modelViewMatrix_[4] =        q01 + q23;
00396   modelViewMatrix_[5] = 1.0l - q22 - q00;
00397   modelViewMatrix_[6] =        q12 - q03;
00398   modelViewMatrix_[7] = 0.0l;
00399 
00400   modelViewMatrix_[8] =        q02 - q13;
00401   modelViewMatrix_[9] =        q12 + q03;
00402   modelViewMatrix_[10] = 1.0l - q11 - q00;
00403   modelViewMatrix_[11] = 0.0l;
00404 
00405   const Vec t = q.inverseRotate(frame()->position());
00406 
00407   modelViewMatrix_[12] = -t.x;
00408   modelViewMatrix_[13] = -t.y;
00409   modelViewMatrix_[14] = -t.z;
00410   modelViewMatrix_[15] = 1.0l;
00411 }
00412 
00413 
00432 void Camera::loadProjectionMatrix(bool reset) const
00433 {
00434   // WARNING: makeCurrent must be called by every calling method
00435   glMatrixMode(GL_PROJECTION);
00436 
00437   if (reset)
00438     glLoadIdentity();
00439 
00440   computeProjectionMatrix();
00441 
00442   glMultMatrixd(projectionMatrix_);
00443 }
00444 
00469 void Camera::loadModelViewMatrix(bool reset) const
00470 {
00471   // WARNING: makeCurrent must be called by every calling method
00472   glMatrixMode(GL_MODELVIEW);
00473   computeModelViewMatrix();
00474   if (reset)
00475     glLoadMatrixd(modelViewMatrix_);
00476   else
00477     glMultMatrixd(modelViewMatrix_);
00478 }
00479 
00505 void Camera::loadProjectionMatrixStereo(bool leftBuffer) const
00506 {
00507   float left, right, bottom, top;
00508   float screenHalfWidth, halfWidth, side, shift, delta;
00509 
00510   glMatrixMode(GL_PROJECTION);
00511   glLoadIdentity();
00512 
00513   switch (type())
00514     {
00515     case Camera::PERSPECTIVE:
00516       // compute half width of screen,
00517       // corresponding to zero parallax plane to deduce decay of cameras
00518       screenHalfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0);
00519       shift = screenHalfWidth * IODistance() / physicalScreenWidth();
00520       // should be * current y  / y total
00521       // to take into account that the window doesn't cover the entire screen
00522 
00523       // compute half width of "view" at znear and the delta corresponding to
00524       // the shifted camera to deduce what to set for asymmetric frustums
00525       halfWidth = zNear() * tan(horizontalFieldOfView() / 2.0);
00526       delta  = shift * zNear() / focusDistance();
00527       side   = leftBuffer ? -1.0 : 1.0;
00528 
00529       left   = -halfWidth + side * delta;
00530       right  =  halfWidth + side * delta;
00531       top    = halfWidth / aspectRatio();
00532       bottom = -top;
00533       glFrustum(left, right, bottom, top, zNear(), zFar() );
00534       break;
00535 
00536     case Camera::ORTHOGRAPHIC:
00537       qWarning("Camera::setProjectionMatrixStereo: Stereo not available with Ortho mode");
00538       break;
00539     }
00540 }
00541 
00560 void Camera::loadModelViewMatrixStereo(bool leftBuffer) const
00561 {
00562   // WARNING: makeCurrent must be called by every calling method
00563   glMatrixMode(GL_MODELVIEW);
00564 
00565   float halfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0);
00566   float shift     = halfWidth * IODistance() / physicalScreenWidth(); // * current window width / full screen width
00567 
00568   computeModelViewMatrix();
00569   if (leftBuffer)
00570     modelViewMatrix_[12] -= shift;
00571   else
00572     modelViewMatrix_[12] += shift;
00573   glLoadMatrixd(modelViewMatrix_);
00574 }
00575 
00590 void Camera::getProjectionMatrix(GLdouble m[16]) const
00591 {
00592   // May not be needed, but easier and more robust like this.
00593   computeProjectionMatrix();
00594   for (unsigned short i=0; i<16; ++i)
00595     m[i] = projectionMatrix_[i];
00596 }
00597 
00612 void Camera::getModelViewMatrix(GLdouble m[16]) const
00613 {
00614   // May not be needed, but easier like this.
00615   // Prevents from retrieving matrix in stereo mode -> overwrites shifted value.
00616   computeModelViewMatrix();
00617   for (unsigned short i=0; i<16; ++i)
00618     m[i] = modelViewMatrix_[i];
00619 }
00620 
00624 void Camera::getModelViewProjectionMatrix(GLdouble m[16]) const
00625 {
00626   GLdouble mv[16];
00627   GLdouble proj[16];
00628   getModelViewMatrix(mv);
00629   getProjectionMatrix(proj);
00630         
00631   for (unsigned short i=0; i<4; ++i)
00632   {
00633     for (unsigned short j=0; j<4; ++j)
00634     {
00635       double sum = 0.0;
00636       for (unsigned short k=0; k<4; ++k)
00637         sum += proj[i+4*k]*mv[k+4*j];
00638       m[i+4*j] = sum;
00639     }
00640   }
00641 }
00642 
00643 #ifndef DOXYGEN
00644 void Camera::getProjectionMatrix(GLfloat m[16]) const
00645 {
00646   qWarning("Warning : Camera::getProjectionMatrix requires a GLdouble matrix array");
00647   static GLdouble mat[16];
00648   getProjectionMatrix(mat);
00649   for (int i=0; i<16; ++i)
00650     m[i] = float(mat[i]);
00651 }
00652 
00653 void Camera::getModelViewMatrix(GLfloat m[16]) const
00654 {
00655   qWarning("Warning : Camera::getModelViewMatrix requires a GLdouble matrix array");
00656   static GLdouble mat[16];
00657   getModelViewMatrix(mat);
00658   for (int i=0; i<16; ++i)
00659     m[i] = float(mat[i]);
00660 }
00661 #endif
00662 
00667 void Camera::setSceneRadius(float radius)
00668 {
00669   if (radius <= 0.0)
00670     {
00671       qWarning("Scene radius must be positive - Ignoring value");
00672       return;
00673     }
00674 
00675   sceneRadius_ = radius;
00676 
00677   setFocusDistance(sceneRadius() / tan(fieldOfView()/2.0));
00678 
00679   frame()->setFlySpeed(0.01*sceneRadius());
00680 }
00681 
00684 void Camera::setSceneBoundingBox(const Vec& min, const Vec& max)
00685 {
00686   setSceneCenter((min+max)/2.0);
00687   setSceneRadius(0.5*(max-min).norm());
00688 }
00689 
00690 
00694 void Camera::setSceneCenter(const Vec& center)
00695 {
00696   sceneCenter_ = center;
00697   setRevolveAroundPoint(sceneCenter());
00698 }
00699 
00705 bool Camera::setSceneCenterFromPixel(const QPoint& pixel)
00706 {
00707   bool found;
00708   Vec point = pointUnderPixel(pixel, found);
00709   if (found)
00710     setSceneCenter(point);
00711   return found;
00712 }
00713 
00715 void Camera::setRevolveAroundPoint(const Vec& rap)
00716 {
00717   const float prevDist = fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
00718 
00719   frame()->setRevolveAroundPoint(rap);
00720 
00721   // orthoCoef_ is used to compensate for changes of the revolveAroundPoint, so that the image does
00722   // not change when the revolveAroundPoint is changed in ORTHOGRAPHIC mode.
00723   const float newDist = fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
00724   // Prevents division by zero when rap is set to camera position
00725   if ((prevDist > 1E-9) && (newDist > 1E-9))
00726     orthoCoef_ *= prevDist / newDist;
00727 }
00728 
00738 bool Camera::setRevolveAroundPointFromPixel(const QPoint& pixel)
00739 {
00740   bool found;
00741   Vec point = pointUnderPixel(pixel, found);
00742   if (found)
00743     setRevolveAroundPoint(point);
00744   return found;
00745 }
00746 
00761 float Camera::pixelGLRatio(const Vec& position) const
00762 {
00763   switch (type())
00764     {
00765     case Camera::PERSPECTIVE :
00766       return 2.0 * fabs((frame()->coordinatesOf(position)).z) * tan(fieldOfView()/2.0) / screenHeight();
00767     case Camera::ORTHOGRAPHIC :
00768       {
00769         GLdouble w, h;
00770         getOrthoWidthHeight(w,h);
00771         return 2.0 * h / screenHeight();
00772       }
00773     }
00774   // Bad compilers complain
00775   return 1.0;
00776 }
00777 
00803 void Camera::setFOVToFitScene()
00804 {
00805   if (distanceToSceneCenter() > sqrt(2.0)*sceneRadius())
00806     setFieldOfView(2.0 * asin(sceneRadius() / distanceToSceneCenter()));
00807   else
00808     setFieldOfView(M_PI / 2.0f);
00809 }
00810 
00817 void Camera::interpolateToZoomOnPixel(const QPoint& pixel)
00818 {
00819   const float coef = 0.1f;
00820 
00821   bool found;
00822   Vec target = pointUnderPixel(pixel, found);
00823 
00824   if (!found)
00825     return;
00826 
00827   if (interpolationKfi_->interpolationIsStarted())
00828     interpolationKfi_->stopInterpolation();
00829 
00830   interpolationKfi_->deletePath();
00831   interpolationKfi_->addKeyFrame(*(frame()));
00832 
00833   interpolationKfi_->addKeyFrame(Frame(0.3f*frame()->position() + 0.7f*target, frame()->orientation()), 0.4f);
00834 
00835   // Small hack: attach a temporary frame to take advantage of lookAt without modifying frame
00836   static ManipulatedCameraFrame* tempFrame = new ManipulatedCameraFrame();
00837   ManipulatedCameraFrame* const originalFrame = frame();
00838   tempFrame->setPosition(coef*frame()->position() + (1.0-coef)*target);
00839   tempFrame->setOrientation(frame()->orientation());
00840   setFrame(tempFrame);
00841   lookAt(target);
00842   setFrame(originalFrame);
00843 
00844   interpolationKfi_->addKeyFrame(*(tempFrame), 1.0);
00845 
00846   interpolationKfi_->startInterpolation();
00847 }
00848 
00855 void Camera::interpolateToFitScene()
00856 {
00857   if (interpolationKfi_->interpolationIsStarted())
00858     interpolationKfi_->stopInterpolation();
00859 
00860   interpolationKfi_->deletePath();
00861   interpolationKfi_->addKeyFrame(*(frame()));
00862 
00863   // Small hack:  attach a temporary frame to take advantage of lookAt without modifying frame
00864   static ManipulatedCameraFrame* tempFrame = new ManipulatedCameraFrame();
00865   ManipulatedCameraFrame* const originalFrame = frame();
00866   tempFrame->setPosition(frame()->position());
00867   tempFrame->setOrientation(frame()->orientation());
00868   setFrame(tempFrame);
00869   showEntireScene();
00870   setFrame(originalFrame);
00871 
00872   interpolationKfi_->addKeyFrame(*(tempFrame));
00873 
00874   interpolationKfi_->startInterpolation();
00875 }
00876 
00877 
00884 void Camera::interpolateTo(const Frame& fr, float duration)
00885 {
00886   if (interpolationKfi_->interpolationIsStarted())
00887     interpolationKfi_->stopInterpolation();
00888 
00889   interpolationKfi_->deletePath();
00890   interpolationKfi_->addKeyFrame(*(frame()));
00891   interpolationKfi_->addKeyFrame(fr, duration);
00892 
00893   interpolationKfi_->startInterpolation();
00894 }
00895 
00896 
00913 Vec Camera::pointUnderPixel(const QPoint& pixel, bool& found) const
00914 {
00915   float depth;
00916   // Qt uses upper corner for its origin while GL uses the lower corner.
00917   glReadPixels(pixel.x(), screenHeight()-1-pixel.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
00918   found = depth < 1.0;
00919   Vec point(pixel.x(), pixel.y(), depth);
00920   point = unprojectedCoordinatesOf(point);
00921   return point;
00922 }
00923 
00929 void Camera::showEntireScene()
00930 {
00931   fitSphere(sceneCenter(), sceneRadius());
00932 }
00933 
00939 void Camera::centerScene()
00940 {
00941   frame()->projectOnLine(sceneCenter(), viewDirection());
00942 }
00943 
00950 void Camera::lookAt(const Vec& target)
00951 {
00952   setViewDirection(target - position());
00953 }
00954 
00962 void Camera::fitSphere(const Vec& center, float radius)
00963 {
00964   float distance = 0.0f;
00965   switch (type())
00966     {
00967     case Camera::PERSPECTIVE :
00968       {
00969         const float yview = radius / sin(fieldOfView()/2.0);
00970         const float xview = radius / sin(horizontalFieldOfView()/2.0);
00971         distance = qMax(xview,yview);
00972         break;
00973       }
00974     case Camera::ORTHOGRAPHIC :
00975       {
00976         distance = ((center-revolveAroundPoint()) * viewDirection()) + (radius / orthoCoef_);
00977         break;
00978       }
00979     }
00980   Vec newPos(center - distance * viewDirection());
00981   frame()->setPositionWithConstraint(newPos);
00982 }
00983 
00986 void Camera::fitBoundingBox(const Vec& min, const Vec& max)
00987 {
00988   float diameter = qMax(fabs(max[1]-min[1]), fabs(max[0]-min[0]));
00989   diameter = qMax(fabsf(max[2]-min[2]), diameter);
00990   fitSphere(0.5*(min+max), 0.5*diameter);
00991 }
00992 
01000 void Camera::fitScreenRegion(const QRect& rectangle)
01001 {
01002   const Vec vd = viewDirection();
01003   const float distToPlane = distanceToSceneCenter();
01004   const QPoint center = rectangle.center();
01005 
01006   Vec orig, dir;
01007   convertClickToLine( center, orig, dir );
01008   Vec newCenter = orig + distToPlane / (dir*vd) * dir;
01009 
01010   convertClickToLine( QPoint(rectangle.x(), center.y()), orig, dir );
01011   const Vec pointX = orig + distToPlane / (dir*vd) * dir;
01012 
01013   convertClickToLine( QPoint(center.x(), rectangle.y()), orig, dir );
01014   const Vec pointY = orig + distToPlane / (dir*vd) * dir;
01015 
01016   float distance = 0.0f;
01017   switch (type())
01018     {
01019     case Camera::PERSPECTIVE :
01020       {
01021         const float distX = (pointX-newCenter).norm() / sin(horizontalFieldOfView()/2.0);
01022         const float distY = (pointY-newCenter).norm() / sin(fieldOfView()/2.0);
01023         distance = qMax(distX, distY);
01024         break;
01025       }
01026     case Camera::ORTHOGRAPHIC :
01027       {
01028         const float dist = ((newCenter-revolveAroundPoint()) * vd);
01029         //#CONNECTION# getOrthoWidthHeight
01030         const float distX = (pointX-newCenter).norm() / orthoCoef_ / ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
01031         const float distY = (pointY-newCenter).norm() / orthoCoef_ / ((aspectRatio() < 1.0) ? 1.0/aspectRatio() : 1.0);
01032         distance = dist + qMax(distX, distY);
01033         break;
01034       }
01035     }
01036 
01037   Vec newPos(newCenter - distance * vd);
01038   frame()->setPositionWithConstraint(newPos);
01039 }
01040 
01056 void Camera::setUpVector(const Vec& up, bool noMove)
01057 {
01058   Quaternion q(Vec(0.0, 1.0, 0.0), frame()->transformOf(up));
01059 
01060   if (!noMove)
01061     frame()->setPosition(revolveAroundPoint() - (frame()->orientation()*q).rotate(frame()->coordinatesOf(revolveAroundPoint())));
01062 
01063   frame()->rotate(q);
01064 
01065   // Useful in fly mode to keep the horizontal direction.
01066   frame()->updateFlyUpVector();
01067 }
01068 
01080 void Camera::setOrientation(float theta, float phi)
01081 {
01082   Vec axis(0.0, 1.0, 0.0);
01083   const Quaternion rot1(axis, theta);
01084   axis = Vec(-cos(theta), 0., sin(theta));
01085   const Quaternion rot2(axis, phi);
01086   setOrientation(rot1 * rot2);
01087 }
01088 
01090 void Camera::setOrientation(const Quaternion& q)
01091 {
01092   frame()->setOrientation(q);
01093   frame()->updateFlyUpVector();
01094 }
01095 
01101 void Camera::setViewDirection(const Vec& direction)
01102 {
01103   if (direction.squaredNorm() < 1E-10)
01104     return;
01105 
01106   Vec xAxis = direction ^ upVector();
01107   if (xAxis.squaredNorm() < 1E-10)
01108     {
01109       // target is aligned with upVector, this means a rotation around X axis
01110       // X axis is then unchanged, let's keep it !
01111       xAxis = frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0));
01112     }
01113 
01114   Quaternion q;
01115   q.setFromRotatedBasis(xAxis, xAxis^direction, -direction);
01116   frame()->setOrientationWithConstraint(q);
01117 }
01118 
01119 // Compute a 3 by 3 determinant.
01120 static float det(float m00,float m01,float m02,
01121                  float m10,float m11,float m12,
01122                  float m20,float m21,float m22)
01123 {
01124   return m00*m11*m22 + m01*m12*m20 + m02*m10*m21 - m20*m11*m02 - m10*m01*m22 - m00*m21*m12;
01125 }
01126 
01127 // Computes the index of element [i][j] in a \c float matrix[3][4].
01128 static inline unsigned int ind(unsigned int i, unsigned int j)
01129 {
01130   return (i*4+j);
01131 }
01132 
01133 
01151 void Camera::setFromModelViewMatrix(const GLdouble* const modelViewMatrix)
01152 {
01153   // Get upper left (rotation) matrix
01154   double upperLeft[3][3];
01155   for (int i=0; i<3; ++i)
01156     for (int j=0; j<3; ++j)
01157       upperLeft[i][j] = modelViewMatrix[i*4+j];
01158 
01159   // Transform upperLeft into the associated Quaternion
01160   Quaternion q;
01161   q.setFromRotationMatrix(upperLeft);
01162 
01163   setOrientation(q);
01164   setPosition(-q.rotate(Vec(modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14])));
01165 }
01166 
01189 void Camera::setFromProjectionMatrix(const float matrix[12])
01190 {
01191   // The 3 lines of the matrix are the normals to the planes x=0, y=0, z=0
01192   // in the camera CS. As we normalize them, we do not need the 4th coordinate.
01193   Vec line_0(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,2)]);
01194   Vec line_1(matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,2)]);
01195   Vec line_2(matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,2)]);
01196 
01197   line_0.normalize();
01198   line_1.normalize();
01199   line_2.normalize();
01200 
01201   // The camera position is at (0,0,0) in the camera CS so it is the
01202   // intersection of the 3 planes. It can be seen as the kernel
01203   // of the 3x4 projection matrix. We calculate it through 4 dimensional
01204   // vectorial product. We go directly into 3D that is to say we directly
01205   // divide the first 3 coordinates by the 4th one.
01206 
01207   // We derive the 4 dimensional vectorial product formula from the
01208   // computation of a 4x4 determinant that is developped according to
01209   // its 4th column. This implies some 3x3 determinants.
01210   const Vec cam_pos = Vec(det(matrix[ind(0,1)],matrix[ind(0,2)],matrix[ind(0,3)],
01211                               matrix[ind(1,1)],matrix[ind(1,2)],matrix[ind(1,3)],
01212                               matrix[ind(2,1)],matrix[ind(2,2)],matrix[ind(2,3)]),
01213 
01214                            -det(matrix[ind(0,0)],matrix[ind(0,2)],matrix[ind(0,3)],
01215                                 matrix[ind(1,0)],matrix[ind(1,2)],matrix[ind(1,3)],
01216                                 matrix[ind(2,0)],matrix[ind(2,2)],matrix[ind(2,3)]),
01217 
01218                            det(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,3)],
01219                                matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,3)],
01220                                matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,3)])) /
01221 
01222     (-det(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,2)],
01223           matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,2)],
01224           matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,2)]));
01225 
01226   // We compute the rotation matrix column by column.
01227 
01228   // GL Z axis is front facing.
01229   Vec column_2 = -line_2;
01230 
01231   // X-axis is almost like line_0 but should be orthogonal to the Z axis.
01232   Vec column_0 = ((column_2^line_0)^column_2);
01233   column_0.normalize();
01234 
01235   // Y-axis is almost like line_1 but should be orthogonal to the Z axis.
01236   // Moreover line_1 is downward oriented as the screen CS.
01237   Vec column_1 = -((column_2^line_1)^column_2);
01238   column_1.normalize();
01239 
01240   double rot[3][3];
01241   rot[0][0] = column_0[0];
01242   rot[1][0] = column_0[1];
01243   rot[2][0] = column_0[2];
01244 
01245   rot[0][1] = column_1[0];
01246   rot[1][1] = column_1[1];
01247   rot[2][1] = column_1[2];
01248 
01249   rot[0][2] = column_2[0];
01250   rot[1][2] = column_2[1];
01251   rot[2][2] = column_2[2];
01252 
01253   // We compute the field of view
01254 
01255   // line_1^column_0 -> vector of intersection line between
01256   // y_screen=0 and x_camera=0 plane.
01257   // column_2*(...)  -> cos of the angle between Z vector et y_screen=0 plane
01258   // * 2 -> field of view = 2 * half angle
01259 
01260   // We need some intermediate values.
01261   Vec dummy = line_1^column_0;
01262   dummy.normalize();
01263   float fov = acos(column_2*dummy) * 2.0;
01264 
01265   // We set the camera.
01266   Quaternion q;
01267   q.setFromRotationMatrix(rot);
01268   setOrientation(q);
01269   setPosition(cam_pos);
01270   setFieldOfView(fov);
01271 }
01272 
01273 
01274 /*
01275         // persp : projectionMatrix_[0]  = f/aspectRatio();
01276 void Camera::setFromProjectionMatrix(const GLdouble* projectionMatrix)
01277 {
01278   QString message;
01279   if ((fabs(projectionMatrix[1]) > 1E-3) ||
01280       (fabs(projectionMatrix[2]) > 1E-3) ||
01281       (fabs(projectionMatrix[3]) > 1E-3) ||
01282       (fabs(projectionMatrix[4]) > 1E-3) ||
01283       (fabs(projectionMatrix[6]) > 1E-3) ||
01284       (fabs(projectionMatrix[7]) > 1E-3) ||
01285       (fabs(projectionMatrix[8]) > 1E-3) ||
01286       (fabs(projectionMatrix[9]) > 1E-3))
01287     message = "Non null coefficient in projection matrix - Aborting";
01288   else
01289     if ((fabs(projectionMatrix[11]+1.0) < 1E-5) && (fabs(projectionMatrix[15]) < 1E-5))
01290       {
01291         if (projectionMatrix[5] < 1E-4)
01292           message="Negative field of view in Camera::setFromProjectionMatrix";
01293         else
01294           setType(Camera::PERSPECTIVE);
01295       }
01296     else
01297       if ((fabs(projectionMatrix[11]) < 1E-5) && (fabs(projectionMatrix[15]-1.0) < 1E-5))
01298         setType(Camera::ORTHOGRAPHIC);
01299       else
01300         message = "Unable to determine camera type in setFromProjectionMatrix - Aborting";
01301 
01302   if (!message.isEmpty())
01303     {
01304       qWarning(message);
01305       return;
01306     }
01307 
01308   switch (type())
01309     {
01310     case Camera::PERSPECTIVE:
01311       {
01312         setFieldOfView(2.0 * atan(1.0/projectionMatrix[5]));
01313         const float far = projectionMatrix[14] / (2.0 * (1.0 + projectionMatrix[10]));
01314         const float near = (projectionMatrix[10]+1.0) / (projectionMatrix[10]-1.0) * far;
01315         setSceneRadius((far-near)/2.0);
01316         setSceneCenter(position() + (near + sceneRadius())*viewDirection());
01317         break;
01318       }
01319     case Camera::ORTHOGRAPHIC:
01320       {
01321         GLdouble w, h;
01322         getOrthoWidthHeight(w,h);
01323         projectionMatrix_[0]  = 1.0/w;
01324         projectionMatrix_[5]  = 1.0/h;
01325         projectionMatrix_[10] = -2.0/(ZFar - ZNear);
01326         projectionMatrix_[11] = 0.0;
01327         projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear);
01328         projectionMatrix_[15] = 1.0;
01329         // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
01330         break;
01331       }
01332     }
01333 }
01334 */
01335 
01337 
01339 void Camera::getCameraCoordinatesOf(const float src[3], float res[3]) const
01340 {
01341   Vec r = cameraCoordinatesOf(Vec(src));
01342   for (int i=0; i<3; ++i)
01343     res[i] = r[i];
01344 }
01345 
01347 void Camera::getWorldCoordinatesOf(const float src[3], float res[3]) const
01348 {
01349   Vec r = worldCoordinatesOf(Vec(src));
01350   for (int i=0; i<3; ++i)
01351     res[i] = r[i];
01352 }
01353 
01359 void Camera::getViewport(GLint viewport[4]) const
01360 {
01361   viewport[0] = 0;
01362   viewport[1] = screenHeight();
01363   viewport[2] = screenWidth();
01364   viewport[3] = -screenHeight();
01365 }
01366 
01441 Vec Camera::projectedCoordinatesOf(const Vec& src, const Frame* frame) const
01442 {
01443   GLdouble x,y,z;
01444   static GLint viewport[4];
01445   getViewport(viewport);
01446 
01447   if (frame)
01448     {
01449       const Vec tmp = frame->inverseCoordinatesOf(src);
01450       gluProject(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport,  &x,&y,&z);
01451     }
01452   else
01453     gluProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport,  &x,&y,&z);
01454 
01455   return Vec(x,y,z);
01456 }
01457 
01483 Vec Camera::unprojectedCoordinatesOf(const Vec& src, const Frame* frame) const
01484 {
01485   GLdouble x,y,z;
01486   static GLint viewport[4];
01487   getViewport(viewport);
01488   gluUnProject(src.x,src.y,src.z, modelViewMatrix_,  projectionMatrix_,  viewport,  &x,&y,&z);
01489   if (frame)
01490     return frame->coordinatesOf(Vec(x,y,z));
01491   else
01492     return Vec(x,y,z);
01493 }
01494 
01496 void Camera::getProjectedCoordinatesOf(const float src[3], float res[3], const Frame* frame) const
01497 {
01498   Vec r = projectedCoordinatesOf(Vec(src), frame);
01499   for (int i=0; i<3; ++i)
01500     res[i] = r[i];
01501 }
01502 
01504 void Camera::getUnprojectedCoordinatesOf(const float src[3], float res[3], const Frame* frame) const
01505 {
01506   Vec r = unprojectedCoordinatesOf(Vec(src), frame);
01507   for (int i=0; i<3; ++i)
01508     res[i] = r[i];
01509 }
01510 
01512 
01516 KeyFrameInterpolator* Camera::keyFrameInterpolator(int i) const
01517 {
01518   if (kfi_.contains(i))
01519     return kfi_[i];
01520   else
01521     return NULL;
01522 }
01523 
01542 void Camera::setKeyFrameInterpolator(int i, KeyFrameInterpolator* const kfi)
01543 {
01544   if (kfi)
01545     kfi_[i] = kfi;
01546   else
01547     kfi_.remove(i);
01548 }
01549 
01562 void Camera::addKeyFrameToPath(int i)
01563 {
01564   if (!kfi_.contains(i))
01565     setKeyFrameInterpolator(i, new KeyFrameInterpolator(frame()));
01566 
01567   kfi_[i]->addKeyFrame(*(frame()));
01568 }
01569 
01578 void Camera::playPath(int i)
01579 {
01580   if (kfi_.contains(i)) {
01581     if (kfi_[i]->interpolationIsStarted())
01582       kfi_[i]->stopInterpolation();
01583     else
01584       kfi_[i]->startInterpolation();
01585   }
01586 }
01587 
01593 void Camera::resetPath(int i)
01594 {
01595   if (kfi_.contains(i)) {
01596     if ((kfi_[i]->interpolationIsStarted()))
01597       kfi_[i]->stopInterpolation();
01598     else
01599       {
01600                 kfi_[i]->resetInterpolation();
01601                 kfi_[i]->interpolateAtTime(kfi_[i]->interpolationTime());
01602       }
01603   }
01604 }
01605 
01614 void Camera::deletePath(int i)
01615 {
01616   if (kfi_.contains(i))
01617     {
01618       kfi_[i]->stopInterpolation();
01619       delete kfi_[i];
01620       kfi_.remove(i);
01621     }
01622 }
01623 
01630 void Camera::drawAllPaths()
01631 {
01632   for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it)
01633 #if QT_VERSION >= 0x040000
01634     (it.value())->drawPath(3, 5, sceneRadius());
01635 #else
01636     (it.data())->drawPath(3, 5, sceneRadius());
01637 #endif
01638 }
01639 
01641 
01667 QDomElement Camera::domElement(const QString& name, QDomDocument& document) const
01668 {
01669   QDomElement de = document.createElement(name);
01670   QDomElement paramNode = document.createElement("Parameters");
01671   paramNode.setAttribute("fieldOfView", QString::number(fieldOfView()));
01672   paramNode.setAttribute("zNearCoefficient", QString::number(zNearCoefficient()));
01673   paramNode.setAttribute("zClippingCoefficient", QString::number(zClippingCoefficient()));
01674   paramNode.setAttribute("orthoCoef", QString::number(orthoCoef_));
01675   paramNode.setAttribute("sceneRadius", QString::number(sceneRadius()));
01676   paramNode.appendChild(sceneCenter().domElement("SceneCenter", document));
01677 
01678   switch (type())
01679     {
01680     case Camera::PERSPECTIVE  : paramNode.setAttribute("Type", "PERSPECTIVE"); break;
01681     case Camera::ORTHOGRAPHIC : paramNode.setAttribute("Type", "ORTHOGRAPHIC"); break;
01682     }
01683   de.appendChild(paramNode);
01684 
01685   QDomElement stereoNode = document.createElement("Stereo");
01686   stereoNode.setAttribute("IODist", QString::number(IODistance()));
01687   stereoNode.setAttribute("focusDistance", QString::number(focusDistance()));
01688   stereoNode.setAttribute("physScreenWidth", QString::number(physicalScreenWidth()));
01689   de.appendChild(stereoNode);
01690 
01691   de.appendChild(frame()->domElement("ManipulatedCameraFrame", document));
01692 
01693   // KeyFrame paths
01694   for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it)
01695     {
01696 #if QT_VERSION >= 0x040000
01697       QDomElement kfNode = (it.value())->domElement("KeyFrameInterpolator", document);
01698 #else
01699       QDomElement kfNode = (it.data())->domElement("KeyFrameInterpolator", document);
01700 #endif
01701       kfNode.setAttribute("index", QString::number(it.key()));
01702       de.appendChild(kfNode);
01703     }
01704 
01705   return de;
01706 }
01707 
01729 void Camera::initFromDOMElement(const QDomElement& element)
01730 {
01731   QDomElement child=element.firstChild().toElement();
01732 
01733 #if QT_VERSION >= 0x040000
01734   QMutableMapIterator<int, KeyFrameInterpolator*> it(kfi_);
01735   while (it.hasNext()) {
01736     it.next();
01737 #else
01738   for (QMap<int, KeyFrameInterpolator*>::Iterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it) {
01739 #endif
01740     deletePath(it.key());
01741   }
01742 
01743   while (!child.isNull())
01744     {
01745       if (child.tagName() == "Parameters")
01746         {
01747           // #CONNECTION# Default values set in constructor
01748           setFieldOfView(DomUtils::floatFromDom(child, "fieldOfView", M_PI/4.0f));
01749           setZNearCoefficient(DomUtils::floatFromDom(child, "zNearCoefficient", 0.005f));
01750           setZClippingCoefficient(DomUtils::floatFromDom(child, "zClippingCoefficient", sqrt(3.0)));
01751           orthoCoef_ = DomUtils::floatFromDom(child, "orthoCoef", tan(fieldOfView()/2.0));
01752           setSceneRadius(DomUtils::floatFromDom(child, "sceneRadius", sceneRadius()));
01753 
01754           setType(PERSPECTIVE);
01755           QString type = child.attribute("Type", "PERSPECTIVE");
01756           if (type == "PERSPECTIVE")  setType(Camera::PERSPECTIVE);
01757           if (type == "ORTHOGRAPHIC") setType(Camera::ORTHOGRAPHIC);
01758 
01759       QDomElement child2=child.firstChild().toElement();
01760       while (!child2.isNull())
01761           {
01762             /* Although the scene does not change when a camera is loaded, restore the saved center and radius values. 
01763                Mainly useful when a the viewer is restored on startup, with possible additional cameras. */
01764             if (child2.tagName() == "SceneCenter")
01765               setSceneCenter(Vec(child2));
01766 
01767             child2 = child2.nextSibling().toElement();
01768           }
01769         }
01770 
01771       if (child.tagName() == "ManipulatedCameraFrame")
01772         frame()->initFromDOMElement(child);
01773 
01774       if (child.tagName() == "Stereo")
01775         {
01776           setIODistance(DomUtils::floatFromDom(child, "IODist", 0.062f));
01777           setFocusDistance(DomUtils::floatFromDom(child, "focusDistance", focusDistance()));
01778           setPhysicalScreenWidth(DomUtils::floatFromDom(child, "physScreenWidth", 0.5f));
01779         }
01780 
01781       if (child.tagName() == "KeyFrameInterpolator")
01782         {
01783           int index = DomUtils::intFromDom(child, "index", 0);
01784           setKeyFrameInterpolator(index, new KeyFrameInterpolator(frame()));
01785           if (keyFrameInterpolator(index))
01786             keyFrameInterpolator(index)->initFromDOMElement(child);
01787         }
01788 
01789       child = child.nextSibling().toElement();
01790     }
01791 }
01792 
01804 void Camera::convertClickToLine(const QPoint& pixel, Vec& orig, Vec& dir) const
01805 {
01806   switch (type())
01807     {
01808     case Camera::PERSPECTIVE:
01809       orig = position();
01810       dir = Vec( ((2.0 * pixel.x() / screenWidth()) - 1.0) * tan(fieldOfView()/2.0) * aspectRatio(),
01811                  ((2.0 * (screenHeight()-pixel.y()) / screenHeight()) - 1.0) * tan(fieldOfView()/2.0),
01812                  -1.0 );
01813       dir = worldCoordinatesOf(dir) - orig;
01814       dir.normalize();
01815       break;
01816 
01817     case Camera::ORTHOGRAPHIC:
01818       {
01819         GLdouble w,h;
01820         getOrthoWidthHeight(w,h);
01821         orig = Vec((2.0 * pixel.x() / screenWidth() - 1.0)*w, -(2.0 * pixel.y() / screenHeight() - 1.0)*h, 0.0);
01822         orig = worldCoordinatesOf(orig);
01823         dir = viewDirection();
01824         break;
01825       }
01826     }
01827 }
01828 
01829 #ifndef DOXYGEN
01830 
01831 void Camera::drawCamera(float, float, float)
01832 {
01833   qWarning("drawCamera is deprecated. Use Camera::draw() instead.");
01834 }
01835 #endif
01836 
01857 void Camera::draw(bool drawFarPlane, float scale) const
01858 {
01859   glPushMatrix();
01860   glMultMatrixd(frame()->worldMatrix());
01861 
01862   // 0 is the upper left coordinates of the near corner, 1 for the far one
01863   Vec points[2];
01864 
01865   points[0].z = scale * zNear();
01866   points[1].z = scale * zFar();
01867 
01868   switch (type())
01869     {
01870     case Camera::PERSPECTIVE:
01871       {
01872         points[0].y = points[0].z * tan(fieldOfView()/2.0);
01873         points[0].x = points[0].y * aspectRatio();
01874 
01875         const float ratio = points[1].z / points[0].z;
01876 
01877         points[1].y = ratio * points[0].y;
01878         points[1].x = ratio * points[0].x;
01879         break;
01880       }
01881     case Camera::ORTHOGRAPHIC:
01882       {
01883         GLdouble hw, hh;
01884         getOrthoWidthHeight(hw, hh);
01885         points[0].x = points[1].x = scale * float(hw);
01886         points[0].y = points[1].y = scale * float(hh);
01887         break;
01888       }
01889     }
01890 
01891   const int farIndex = drawFarPlane?1:0;
01892 
01893   // Near and (optionally) far plane(s)
01894   glBegin(GL_QUADS);
01895   for (int i=farIndex; i>=0; --i)
01896     {
01897       glNormal3f(0.0, 0.0, (i==0)?1.0:-1.0);
01898       glVertex3f( points[i].x,  points[i].y, -points[i].z);
01899       glVertex3f(-points[i].x,  points[i].y, -points[i].z);
01900       glVertex3f(-points[i].x, -points[i].y, -points[i].z);
01901       glVertex3f( points[i].x, -points[i].y, -points[i].z);
01902     }
01903   glEnd();
01904 
01905   // Up arrow
01906   const float arrowHeight    = 1.5f * points[0].y;
01907   const float baseHeight     = 1.2f * points[0].y;
01908   const float arrowHalfWidth = 0.5f * points[0].x;
01909   const float baseHalfWidth  = 0.3f * points[0].x;
01910 
01911   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
01912   // Base
01913   glBegin(GL_QUADS);
01914   glVertex3f(-baseHalfWidth, points[0].y, -points[0].z);
01915   glVertex3f( baseHalfWidth, points[0].y, -points[0].z);
01916   glVertex3f( baseHalfWidth, baseHeight,  -points[0].z);
01917   glVertex3f(-baseHalfWidth, baseHeight,  -points[0].z);
01918   glEnd();
01919 
01920   // Arrow
01921   glBegin(GL_TRIANGLES);
01922   glVertex3f( 0.0f,           arrowHeight, -points[0].z);
01923   glVertex3f(-arrowHalfWidth, baseHeight,  -points[0].z);
01924   glVertex3f( arrowHalfWidth, baseHeight,  -points[0].z);
01925   glEnd();
01926   
01927   // Frustum lines
01928   switch (type())
01929     {
01930     case Camera::PERSPECTIVE :
01931       glBegin(GL_LINES);
01932       glVertex3f(0.0f, 0.0f, 0.0f);
01933       glVertex3f( points[farIndex].x,  points[farIndex].y, -points[farIndex].z);
01934       glVertex3f(0.0f, 0.0f, 0.0f);
01935       glVertex3f(-points[farIndex].x,  points[farIndex].y, -points[farIndex].z);
01936       glVertex3f(0.0f, 0.0f, 0.0f);
01937       glVertex3f(-points[farIndex].x, -points[farIndex].y, -points[farIndex].z);
01938       glVertex3f(0.0f, 0.0f, 0.0f);
01939       glVertex3f( points[farIndex].x, -points[farIndex].y, -points[farIndex].z);
01940       glEnd();
01941       break;
01942     case Camera::ORTHOGRAPHIC :
01943       if (drawFarPlane)
01944         {
01945           glBegin(GL_LINES);
01946           glVertex3f( points[0].x,  points[0].y, -points[0].z);
01947           glVertex3f( points[1].x,  points[1].y, -points[1].z);
01948           glVertex3f(-points[0].x,  points[0].y, -points[0].z);
01949           glVertex3f(-points[1].x,  points[1].y, -points[1].z);
01950           glVertex3f(-points[0].x, -points[0].y, -points[0].z);
01951           glVertex3f(-points[1].x, -points[1].y, -points[1].z);
01952           glVertex3f( points[0].x, -points[0].y, -points[0].z);
01953           glVertex3f( points[1].x, -points[1].y, -points[1].z);
01954           glEnd();
01955         }
01956     }
01957 
01958   glPopMatrix();
01959 }
01960 
01961 
01985 void Camera::getFrustumPlanesCoefficients(GLdouble coef[6][4]) const
01986 {
01987   // Computed once and for all
01988   const Vec pos          = position();
01989   const Vec viewDir      = viewDirection();
01990   const Vec up           = upVector();
01991   const Vec right        = rightVector();
01992   const float posViewDir = pos * viewDir;
01993 
01994   static Vec normal[6];
01995   static GLdouble dist[6];
01996   
01997   switch (type())
01998     {
01999     case Camera::PERSPECTIVE :
02000       {
02001         const float hhfov = horizontalFieldOfView() / 2.0;
02002         const float chhfov = cos(hhfov);
02003         const float shhfov = sin(hhfov);
02004         normal[0] = - shhfov * viewDir;
02005         normal[1] = normal[0] + chhfov * right;
02006         normal[0] = normal[0] - chhfov * right;
02007         
02008         normal[2] = -viewDir;
02009         normal[3] =  viewDir;
02010         
02011         const float hfov = fieldOfView() / 2.0;
02012         const float chfov = cos(hfov);
02013         const float shfov = sin(hfov);
02014         normal[4] = - shfov * viewDir;
02015         normal[5] = normal[4] - chfov * up;
02016         normal[4] = normal[4] + chfov * up;
02017 
02018         for (int i=0; i<2; ++i)
02019           dist[i] = pos * normal[i];
02020         for (int j=4; j<6; ++j)
02021           dist[j] = pos * normal[j];
02022 
02023         // Natural equations are:
02024         // dist[0,1,4,5] = pos * normal[0,1,4,5];
02025         // dist[2] = (pos + zNear() * viewDir) * normal[2];
02026         // dist[3] = (pos + zFar()  * viewDir) * normal[3];
02027 
02028         // 2 times less computations using expanded/merged equations. Dir vectors are normalized.
02029         const float posRightCosHH = chhfov * pos * right;
02030         dist[0] = -shhfov * posViewDir;
02031         dist[1] = dist[0] + posRightCosHH;
02032         dist[0] = dist[0] - posRightCosHH;
02033         const float posUpCosH = chfov * pos * up;
02034         dist[4] = - shfov * posViewDir;
02035         dist[5] = dist[4] - posUpCosH;
02036         dist[4] = dist[4] + posUpCosH;
02037         
02038         break;
02039       }
02040     case Camera::ORTHOGRAPHIC :
02041       normal[0] = -right;
02042       normal[1] =  right;
02043       normal[4] =  up;
02044       normal[5] = -up;
02045 
02046       GLdouble hw, hh;
02047       getOrthoWidthHeight(hw, hh);
02048       dist[0] = (pos - hw * right) * normal[0];
02049       dist[1] = (pos + hw * right) * normal[1];
02050       dist[4] = (pos + hh * up) * normal[4];
02051       dist[5] = (pos - hh * up) * normal[5];
02052       break;
02053     }
02054 
02055   // Front and far planes are identical for both camera types.
02056   normal[2] = -viewDir;
02057   normal[3] =  viewDir;
02058   dist[2] = -posViewDir - zNear();
02059   dist[3] =  posViewDir + zFar();
02060 
02061   for (int i=0; i<6; ++i)
02062     {
02063       coef[i][0] = GLdouble(normal[i].x);
02064       coef[i][1] = GLdouble(normal[i].y);
02065       coef[i][2] = GLdouble(normal[i].z);
02066       coef[i][3] = dist[i];
02067     }
02068 }


octovis
Author(s): Kai M. Wurm , Armin Hornung
autogenerated on Thu Feb 11 2016 23:51:19