worldsim/3rdParts/qglviewer/camera.cpp

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