camera.cpp
1 /****************************************************************************
2 
3  Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.3.10.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "domUtils.h"
24 #include "camera.h"
25 #include "qglviewer.h"
26 
27 using namespace std;
28 using namespace qglviewer;
29 
37 Camera::Camera()
38  : fieldOfView_(M_PI/4.0f)
39 {
40  // #CONNECTION# Camera copy constructor
41  interpolationKfi_ = new KeyFrameInterpolator;
42  // Requires the interpolationKfi_
44 
45  // #CONNECTION# All these default values identical in initFromDOMElement.
46 
47  // Requires fieldOfView() to define focusDistance()
48  setSceneRadius(1.0);
49 
50  // Initial value (only scaled after this)
51  orthoCoef_ = tan(fieldOfView()/2.0);
52 
53  // Also defines the revolveAroundPoint(), which changes orthoCoef_. Requires a frame().
54  setSceneCenter(Vec(0.0, 0.0, 0.0));
55 
56  // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to projectionMatrix_ below.
57  setType(PERSPECTIVE);
58 
59  // #CONNECTION# initFromDOMElement default values
60  setZNearCoefficient(0.005f);
61  setZClippingCoefficient(sqrt(3.0));
62 
63  // Dummy values
64  setScreenWidthAndHeight(600, 400);
65 
66  // Stereo parameters
67  setIODistance(0.062f);
70  // focusDistance is set from setFieldOfView()
71 
72  // #CONNECTION# Camera copy constructor
73  for (unsigned short j=0; j<16; ++j)
74  {
75  modelViewMatrix_[j] = ((j%5 == 0) ? 1.0 : 0.0);
76  // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost everywhere.
77  projectionMatrix_[j] = 0.0;
78  }
80 }
81 
87 {
88  delete frame_;
89  delete interpolationKfi_;
90 }
91 
92 
94 Camera::Camera(const Camera& camera)
95  : QObject()
96 {
97  // #CONNECTION# Camera constructor
98  interpolationKfi_ = new KeyFrameInterpolator;
99  // Requires the interpolationKfi_
101 
102  for (unsigned short j=0; j<16; ++j)
103  {
104  modelViewMatrix_[j] = ((j%5 == 0) ? 1.0 : 0.0);
105  // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost everywhere.
106  projectionMatrix_[j] = 0.0;
107  }
108 
109  (*this)=camera;
110 }
111 
125 {
127  setFieldOfView(camera.fieldOfView());
128  setSceneRadius(camera.sceneRadius());
129  setSceneCenter(camera.sceneCenter());
132  setType(camera.type());
133 
134  // Stereo parameters
135  setIODistance(camera.IODistance());
139 
140  orthoCoef_ = camera.orthoCoef_;
141 
142  // frame_ and interpolationKfi_ pointers are not shared.
143  frame_->setReferenceFrame(NULL);
144  frame_->setPosition(camera.position());
145  frame_->setOrientation(camera.orientation());
146 
147  interpolationKfi_->resetInterpolation();
148 
149  kfi_ = camera.kfi_;
150 
153 
154  return *this;
155 }
156 
166 void Camera::setScreenWidthAndHeight(int width, int height)
167 {
168  // Prevent negative and zero dimensions that would cause divisions by zero.
169  screenWidth_ = width > 0 ? width : 1;
170  screenHeight_ = height > 0 ? height : 1;
171 }
172 
210 float Camera::zNear() const
211 {
213 
214  // Prevents negative or null zNear values.
215  const float zMin = zNearCoefficient() * zClippingCoefficient() * sceneRadius();
216  if (z < zMin)
217  switch (type())
218  {
219  case Camera::PERSPECTIVE : z = zMin; break;
220  case Camera::ORTHOGRAPHIC : z = 0.0; break;
221  }
222  return z;
223 }
224 
234 float Camera::zFar() const
235 {
237 }
238 
249 {
250  // make ORTHOGRAPHIC frustum fit PERSPECTIVE (at least in plane normal to viewDirection(), passing
251  // through RAP). Done only when CHANGING type since orthoCoef_ may have been changed with a
252  // setRevolveAroundPoint() in the meantime.
253  if ( (type == Camera::ORTHOGRAPHIC) && (type_ == Camera::PERSPECTIVE) )
254  orthoCoef_ = tan(fieldOfView()/2.0);
255  type_ = type;
256 }
257 
272 {
273  if (!mcf)
274  return;
275 
276  frame_ = mcf;
277  interpolationKfi_->setFrame(frame());
278 }
279 
283 {
284  return fabs((frame()->coordinatesOf(sceneCenter())).z);
285 }
286 
287 
303 void Camera::getOrthoWidthHeight(GLdouble& halfWidth, GLdouble& halfHeight) const
304 {
305  const float dist = orthoCoef_ * fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
306  //#CONNECTION# fitScreenRegion
307  halfWidth = dist * ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
308  halfHeight = dist * ((aspectRatio() < 1.0) ? 1.0/aspectRatio() : 1.0);
309 }
310 
311 
330 {
331  const float ZNear = zNear();
332  const float ZFar = zFar();
333 
334  switch (type())
335  {
336  case Camera::PERSPECTIVE:
337  {
338  // #CONNECTION# all non null coefficients were set to 0.0 in constructor.
339  const float f = 1.0/tan(fieldOfView()/2.0);
340  projectionMatrix_[0] = f/aspectRatio();
341  projectionMatrix_[5] = f;
342  projectionMatrix_[10] = (ZNear + ZFar) / (ZNear - ZFar);
343  projectionMatrix_[11] = -1.0;
344  projectionMatrix_[14] = 2.0 * ZNear * ZFar / (ZNear - ZFar);
345  projectionMatrix_[15] = 0.0;
346  // same as gluPerspective( 180.0*fieldOfView()/M_PI, aspectRatio(), zNear(), zFar() );
347  break;
348  }
349  case Camera::ORTHOGRAPHIC:
350  {
351  GLdouble w, h;
352  getOrthoWidthHeight(w,h);
353  projectionMatrix_[0] = 1.0/w;
354  projectionMatrix_[5] = 1.0/h;
355  projectionMatrix_[10] = -2.0/(ZFar - ZNear);
356  projectionMatrix_[11] = 0.0;
357  projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear);
358  projectionMatrix_[15] = 1.0;
359  // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
360  break;
361  }
362  }
363 }
364 
376 {
377  const Quaternion q = frame()->orientation();
378 
379  const double q00 = 2.0l * q[0] * q[0];
380  const double q11 = 2.0l * q[1] * q[1];
381  const double q22 = 2.0l * q[2] * q[2];
382 
383  const double q01 = 2.0l * q[0] * q[1];
384  const double q02 = 2.0l * q[0] * q[2];
385  const double q03 = 2.0l * q[0] * q[3];
386 
387  const double q12 = 2.0l * q[1] * q[2];
388  const double q13 = 2.0l * q[1] * q[3];
389 
390  const double q23 = 2.0l * q[2] * q[3];
391 
392  modelViewMatrix_[0] = 1.0l - q11 - q22;
393  modelViewMatrix_[1] = q01 - q23;
394  modelViewMatrix_[2] = q02 + q13;
395  modelViewMatrix_[3] = 0.0l;
396 
397  modelViewMatrix_[4] = q01 + q23;
398  modelViewMatrix_[5] = 1.0l - q22 - q00;
399  modelViewMatrix_[6] = q12 - q03;
400  modelViewMatrix_[7] = 0.0l;
401 
402  modelViewMatrix_[8] = q02 - q13;
403  modelViewMatrix_[9] = q12 + q03;
404  modelViewMatrix_[10] = 1.0l - q11 - q00;
405  modelViewMatrix_[11] = 0.0l;
406 
407  const Vec t = q.inverseRotate(frame()->position());
408 
409  modelViewMatrix_[12] = -t.x;
410  modelViewMatrix_[13] = -t.y;
411  modelViewMatrix_[14] = -t.z;
412  modelViewMatrix_[15] = 1.0l;
413 }
414 
415 
434 void Camera::loadProjectionMatrix(bool reset) const
435 {
436  // WARNING: makeCurrent must be called by every calling method
437  glMatrixMode(GL_PROJECTION);
438 
439  if (reset)
440  glLoadIdentity();
441 
443 
444  glMultMatrixd(projectionMatrix_);
445 }
446 
471 void Camera::loadModelViewMatrix(bool reset) const
472 {
473  // WARNING: makeCurrent must be called by every calling method
474  glMatrixMode(GL_MODELVIEW);
476  if (reset)
477  glLoadMatrixd(modelViewMatrix_);
478  else
479  glMultMatrixd(modelViewMatrix_);
480 }
481 
507 void Camera::loadProjectionMatrixStereo(bool leftBuffer) const
508 {
509  float left, right, bottom, top;
510  float screenHalfWidth, halfWidth, side, shift, delta;
511 
512  glMatrixMode(GL_PROJECTION);
513  glLoadIdentity();
514 
515  switch (type())
516  {
517  case Camera::PERSPECTIVE:
518  // compute half width of screen,
519  // corresponding to zero parallax plane to deduce decay of cameras
520  screenHalfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0);
521  shift = screenHalfWidth * IODistance() / physicalScreenWidth();
522  // should be * current y / y total
523  // to take into account that the window doesn't cover the entire screen
524 
525  // compute half width of "view" at znear and the delta corresponding to
526  // the shifted camera to deduce what to set for asymmetric frustums
527  halfWidth = zNear() * tan(horizontalFieldOfView() / 2.0);
528  delta = shift * zNear() / focusDistance();
529  side = leftBuffer ? -1.0 : 1.0;
530 
531  left = -halfWidth + side * delta;
532  right = halfWidth + side * delta;
533  top = halfWidth / aspectRatio();
534  bottom = -top;
535  glFrustum(left, right, bottom, top, zNear(), zFar() );
536  break;
537 
538  case Camera::ORTHOGRAPHIC:
539  qWarning("Camera::setProjectionMatrixStereo: Stereo not available with Ortho mode");
540  break;
541  }
542 }
543 
562 void Camera::loadModelViewMatrixStereo(bool leftBuffer) const
563 {
564  // WARNING: makeCurrent must be called by every calling method
565  glMatrixMode(GL_MODELVIEW);
566 
567  float halfWidth = focusDistance() * tan(horizontalFieldOfView() / 2.0);
568  float shift = halfWidth * IODistance() / physicalScreenWidth(); // * current window width / full screen width
569 
571  if (leftBuffer)
572  modelViewMatrix_[12] -= shift;
573  else
574  modelViewMatrix_[12] += shift;
575  glLoadMatrixd(modelViewMatrix_);
576 }
577 
592 void Camera::getProjectionMatrix(GLdouble m[16]) const
593 {
594  // May not be needed, but easier and more robust like this.
596  for (unsigned short i=0; i<16; ++i)
597  m[i] = projectionMatrix_[i];
598 }
599 
614 void Camera::getModelViewMatrix(GLdouble m[16]) const
615 {
616  // May not be needed, but easier like this.
617  // Prevents from retrieving matrix in stereo mode -> overwrites shifted value.
619  for (unsigned short i=0; i<16; ++i)
620  m[i] = modelViewMatrix_[i];
621 }
622 
626 void Camera::getModelViewProjectionMatrix(GLdouble m[16]) const
627 {
628  GLdouble mv[16];
629  GLdouble proj[16];
630  getModelViewMatrix(mv);
631  getProjectionMatrix(proj);
632 
633  for (unsigned short i=0; i<4; ++i)
634  {
635  for (unsigned short j=0; j<4; ++j)
636  {
637  double sum = 0.0;
638  for (unsigned short k=0; k<4; ++k)
639  sum += proj[i+4*k]*mv[k+4*j];
640  m[i+4*j] = sum;
641  }
642  }
643 }
644 
645 #ifndef DOXYGEN
646 void Camera::getProjectionMatrix(GLfloat m[16]) const
647 {
648  qWarning("Warning : Camera::getProjectionMatrix requires a GLdouble matrix array");
649  static GLdouble mat[16];
650  getProjectionMatrix(mat);
651  for (int i=0; i<16; ++i)
652  m[i] = float(mat[i]);
653 }
654 
655 void Camera::getModelViewMatrix(GLfloat m[16]) const
656 {
657  qWarning("Warning : Camera::getModelViewMatrix requires a GLdouble matrix array");
658  static GLdouble mat[16];
659  getModelViewMatrix(mat);
660  for (int i=0; i<16; ++i)
661  m[i] = float(mat[i]);
662 }
663 #endif
664 
669 void Camera::setSceneRadius(float radius)
670 {
671  if (radius <= 0.0)
672  {
673  qWarning("Scene radius must be positive - Ignoring value");
674  return;
675  }
676 
677  sceneRadius_ = radius;
678 
679  setFocusDistance(sceneRadius() / tan(fieldOfView()/2.0));
680 
681  frame()->setFlySpeed(0.01*sceneRadius());
682 }
683 
687 {
688  setSceneCenter((min+max)/2.0);
689  setSceneRadius(0.5*(max-min).norm());
690 }
691 
692 
696 void Camera::setSceneCenter(const Vec& center)
697 {
698  sceneCenter_ = center;
700 }
701 
707 bool Camera::setSceneCenterFromPixel(const QPoint& pixel)
708 {
709  bool found;
710  Vec point = pointUnderPixel(pixel, found);
711  if (found)
712  setSceneCenter(point);
713  return found;
714 }
715 
718 {
719  const float prevDist = fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
720 
722 
723  // orthoCoef_ is used to compensate for changes of the revolveAroundPoint, so that the image does
724  // not change when the revolveAroundPoint is changed in ORTHOGRAPHIC mode.
725  const float newDist = fabs(cameraCoordinatesOf(revolveAroundPoint()).z);
726  // Prevents division by zero when rap is set to camera position
727  if ((prevDist > 1E-9) && (newDist > 1E-9))
728  orthoCoef_ *= prevDist / newDist;
729 }
730 
740 bool Camera::setRevolveAroundPointFromPixel(const QPoint& pixel)
741 {
742  bool found;
743  Vec point = pointUnderPixel(pixel, found);
744  if (found)
745  setRevolveAroundPoint(point);
746  return found;
747 }
748 
763 float Camera::pixelGLRatio(const Vec& position) const
764 {
765  switch (type())
766  {
767  case Camera::PERSPECTIVE :
768  return 2.0 * fabs((frame()->coordinatesOf(position)).z) * tan(fieldOfView()/2.0) / screenHeight();
769  case Camera::ORTHOGRAPHIC :
770  {
771  GLdouble w, h;
772  getOrthoWidthHeight(w,h);
773  return 2.0 * h / screenHeight();
774  }
775  }
776  // Bad compilers complain
777  return 1.0;
778 }
779 
806 {
807  if (distanceToSceneCenter() > sqrt(2.0)*sceneRadius())
809  else
810  setFieldOfView(M_PI / 2.0f);
811 }
812 
819 void Camera::interpolateToZoomOnPixel(const QPoint& pixel)
820 {
821  const float coef = 0.1f;
822 
823  bool found;
824  Vec target = pointUnderPixel(pixel, found);
825 
826  if (!found)
827  return;
828 
829  if (interpolationKfi_->interpolationIsStarted())
830  interpolationKfi_->stopInterpolation();
831 
832  interpolationKfi_->deletePath();
833  interpolationKfi_->addKeyFrame(*(frame()));
834 
835  interpolationKfi_->addKeyFrame(Frame(0.3f*frame()->position() + 0.7f*target, frame()->orientation()), 0.4f);
836 
837  // Small hack: attach a temporary frame to take advantage of lookAt without modifying frame
838  static ManipulatedCameraFrame* tempFrame = new ManipulatedCameraFrame();
839  ManipulatedCameraFrame* const originalFrame = frame();
840  tempFrame->setPosition(coef*frame()->position() + (1.0-coef)*target);
841  tempFrame->setOrientation(frame()->orientation());
842  setFrame(tempFrame);
843  lookAt(target);
844  setFrame(originalFrame);
845 
846  interpolationKfi_->addKeyFrame(*(tempFrame), 1.0);
847 
848  interpolationKfi_->startInterpolation();
849 }
850 
858 {
859  if (interpolationKfi_->interpolationIsStarted())
860  interpolationKfi_->stopInterpolation();
861 
862  interpolationKfi_->deletePath();
863  interpolationKfi_->addKeyFrame(*(frame()));
864 
865  // Small hack: attach a temporary frame to take advantage of lookAt without modifying frame
866  static ManipulatedCameraFrame* tempFrame = new ManipulatedCameraFrame();
867  ManipulatedCameraFrame* const originalFrame = frame();
868  tempFrame->setPosition(frame()->position());
869  tempFrame->setOrientation(frame()->orientation());
870  setFrame(tempFrame);
871  showEntireScene();
872  setFrame(originalFrame);
873 
874  interpolationKfi_->addKeyFrame(*(tempFrame));
875 
876  interpolationKfi_->startInterpolation();
877 }
878 
879 
886 void Camera::interpolateTo(const Frame& fr, float duration)
887 {
888  if (interpolationKfi_->interpolationIsStarted())
889  interpolationKfi_->stopInterpolation();
890 
891  interpolationKfi_->deletePath();
892  interpolationKfi_->addKeyFrame(*(frame()));
893  interpolationKfi_->addKeyFrame(fr, duration);
894 
895  interpolationKfi_->startInterpolation();
896 }
897 
898 
915 Vec Camera::pointUnderPixel(const QPoint& pixel, bool& found) const
916 {
917  float depth;
918  // Qt uses upper corner for its origin while GL uses the lower corner.
919  glReadPixels(pixel.x(), screenHeight()-1-pixel.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
920  found = depth < 1.0;
921  Vec point(pixel.x(), pixel.y(), depth);
922  point = unprojectedCoordinatesOf(point);
923  return point;
924 }
925 
932 {
934 }
935 
942 {
944 }
945 
952 void Camera::lookAt(const Vec& target)
953 {
954  setViewDirection(target - position());
955 }
956 
964 void Camera::fitSphere(const Vec& center, float radius)
965 {
966  float distance = 0.0f;
967  switch (type())
968  {
969  case Camera::PERSPECTIVE :
970  {
971  const float yview = radius / sin(fieldOfView()/2.0);
972  const float xview = radius / sin(horizontalFieldOfView()/2.0);
973  distance = qMax(xview,yview);
974  break;
975  }
976  case Camera::ORTHOGRAPHIC :
977  {
978  distance = ((center-revolveAroundPoint()) * viewDirection()) + (radius / orthoCoef_);
979  break;
980  }
981  }
982  Vec newPos(center - distance * viewDirection());
983  frame()->setPositionWithConstraint(newPos);
984 }
985 
988 void Camera::fitBoundingBox(const Vec& min, const Vec& max)
989 {
990  float diameter = qMax(fabs(max[1]-min[1]), fabs(max[0]-min[0]));
991  diameter = qMax(fabsf(max[2]-min[2]), diameter);
992  fitSphere(0.5*(min+max), 0.5*diameter);
993 }
994 
1002 void Camera::fitScreenRegion(const QRect& rectangle)
1003 {
1004  const Vec vd = viewDirection();
1005  const float distToPlane = distanceToSceneCenter();
1006  const QPoint center = rectangle.center();
1007 
1008  Vec orig, dir;
1009  convertClickToLine( center, orig, dir );
1010  Vec newCenter = orig + distToPlane / (dir*vd) * dir;
1011 
1012  convertClickToLine( QPoint(rectangle.x(), center.y()), orig, dir );
1013  const Vec pointX = orig + distToPlane / (dir*vd) * dir;
1014 
1015  convertClickToLine( QPoint(center.x(), rectangle.y()), orig, dir );
1016  const Vec pointY = orig + distToPlane / (dir*vd) * dir;
1017 
1018  float distance = 0.0f;
1019  switch (type())
1020  {
1021  case Camera::PERSPECTIVE :
1022  {
1023  const float distX = (pointX-newCenter).norm() / sin(horizontalFieldOfView()/2.0);
1024  const float distY = (pointY-newCenter).norm() / sin(fieldOfView()/2.0);
1025  distance = qMax(distX, distY);
1026  break;
1027  }
1028  case Camera::ORTHOGRAPHIC :
1029  {
1030  const float dist = ((newCenter-revolveAroundPoint()) * vd);
1031  //#CONNECTION# getOrthoWidthHeight
1032  const float distX = (pointX-newCenter).norm() / orthoCoef_ / ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
1033  const float distY = (pointY-newCenter).norm() / orthoCoef_ / ((aspectRatio() < 1.0) ? 1.0/aspectRatio() : 1.0);
1034  distance = dist + qMax(distX, distY);
1035  break;
1036  }
1037  }
1038 
1039  Vec newPos(newCenter - distance * vd);
1040  frame()->setPositionWithConstraint(newPos);
1041 }
1042 
1058 void Camera::setUpVector(const Vec& up, bool noMove)
1059 {
1060  Quaternion q(Vec(0.0, 1.0, 0.0), frame()->transformOf(up));
1061 
1062  if (!noMove)
1063  frame()->setPosition(revolveAroundPoint() - (frame()->orientation()*q).rotate(frame()->coordinatesOf(revolveAroundPoint())));
1064 
1065  frame()->rotate(q);
1066 
1067  // Useful in fly mode to keep the horizontal direction.
1068  frame()->updateFlyUpVector();
1069 }
1070 
1082 void Camera::setOrientation(float theta, float phi)
1083 {
1084  Vec axis(0.0, 1.0, 0.0);
1085  const Quaternion rot1(axis, theta);
1086  axis = Vec(-cos(theta), 0., sin(theta));
1087  const Quaternion rot2(axis, phi);
1088  setOrientation(rot1 * rot2);
1089 }
1090 
1093 {
1094  frame()->setOrientation(q);
1095  frame()->updateFlyUpVector();
1096 }
1097 
1103 void Camera::setViewDirection(const Vec& direction)
1104 {
1105  if (direction.squaredNorm() < 1E-10)
1106  return;
1107 
1108  Vec xAxis = direction ^ upVector();
1109  if (xAxis.squaredNorm() < 1E-10)
1110  {
1111  // target is aligned with upVector, this means a rotation around X axis
1112  // X axis is then unchanged, let's keep it !
1113  xAxis = frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0));
1114  }
1115 
1116  Quaternion q;
1117  q.setFromRotatedBasis(xAxis, xAxis^direction, -direction);
1119 }
1120 
1121 // Compute a 3 by 3 determinant.
1122 static float det(float m00,float m01,float m02,
1123  float m10,float m11,float m12,
1124  float m20,float m21,float m22)
1125 {
1126  return m00*m11*m22 + m01*m12*m20 + m02*m10*m21 - m20*m11*m02 - m10*m01*m22 - m00*m21*m12;
1127 }
1128 
1129 // Computes the index of element [i][j] in a \c float matrix[3][4].
1130 static inline unsigned int ind(unsigned int i, unsigned int j)
1131 {
1132  return (i*4+j);
1133 }
1134 
1135 
1153 void Camera::setFromModelViewMatrix(const GLdouble* const modelViewMatrix)
1154 {
1155  // Get upper left (rotation) matrix
1156  double upperLeft[3][3];
1157  for (int i=0; i<3; ++i)
1158  for (int j=0; j<3; ++j)
1159  upperLeft[i][j] = modelViewMatrix[i*4+j];
1160 
1161  // Transform upperLeft into the associated Quaternion
1162  Quaternion q;
1163  q.setFromRotationMatrix(upperLeft);
1164 
1165  setOrientation(q);
1166  setPosition(-q.rotate(Vec(modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14])));
1167 }
1168 
1191 void Camera::setFromProjectionMatrix(const float matrix[12])
1192 {
1193  // The 3 lines of the matrix are the normals to the planes x=0, y=0, z=0
1194  // in the camera CS. As we normalize them, we do not need the 4th coordinate.
1195  Vec line_0(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,2)]);
1196  Vec line_1(matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,2)]);
1197  Vec line_2(matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,2)]);
1198 
1199  line_0.normalize();
1200  line_1.normalize();
1201  line_2.normalize();
1202 
1203  // The camera position is at (0,0,0) in the camera CS so it is the
1204  // intersection of the 3 planes. It can be seen as the kernel
1205  // of the 3x4 projection matrix. We calculate it through 4 dimensional
1206  // vectorial product. We go directly into 3D that is to say we directly
1207  // divide the first 3 coordinates by the 4th one.
1208 
1209  // We derive the 4 dimensional vectorial product formula from the
1210  // computation of a 4x4 determinant that is developped according to
1211  // its 4th column. This implies some 3x3 determinants.
1212  const Vec cam_pos = Vec(det(matrix[ind(0,1)],matrix[ind(0,2)],matrix[ind(0,3)],
1213  matrix[ind(1,1)],matrix[ind(1,2)],matrix[ind(1,3)],
1214  matrix[ind(2,1)],matrix[ind(2,2)],matrix[ind(2,3)]),
1215 
1216  -det(matrix[ind(0,0)],matrix[ind(0,2)],matrix[ind(0,3)],
1217  matrix[ind(1,0)],matrix[ind(1,2)],matrix[ind(1,3)],
1218  matrix[ind(2,0)],matrix[ind(2,2)],matrix[ind(2,3)]),
1219 
1220  det(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,3)],
1221  matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,3)],
1222  matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,3)])) /
1223 
1224  (-det(matrix[ind(0,0)],matrix[ind(0,1)],matrix[ind(0,2)],
1225  matrix[ind(1,0)],matrix[ind(1,1)],matrix[ind(1,2)],
1226  matrix[ind(2,0)],matrix[ind(2,1)],matrix[ind(2,2)]));
1227 
1228  // We compute the rotation matrix column by column.
1229 
1230  // GL Z axis is front facing.
1231  Vec column_2 = -line_2;
1232 
1233  // X-axis is almost like line_0 but should be orthogonal to the Z axis.
1234  Vec column_0 = ((column_2^line_0)^column_2);
1235  column_0.normalize();
1236 
1237  // Y-axis is almost like line_1 but should be orthogonal to the Z axis.
1238  // Moreover line_1 is downward oriented as the screen CS.
1239  Vec column_1 = -((column_2^line_1)^column_2);
1240  column_1.normalize();
1241 
1242  double rot[3][3];
1243  rot[0][0] = column_0[0];
1244  rot[1][0] = column_0[1];
1245  rot[2][0] = column_0[2];
1246 
1247  rot[0][1] = column_1[0];
1248  rot[1][1] = column_1[1];
1249  rot[2][1] = column_1[2];
1250 
1251  rot[0][2] = column_2[0];
1252  rot[1][2] = column_2[1];
1253  rot[2][2] = column_2[2];
1254 
1255  // We compute the field of view
1256 
1257  // line_1^column_0 -> vector of intersection line between
1258  // y_screen=0 and x_camera=0 plane.
1259  // column_2*(...) -> cos of the angle between Z vector et y_screen=0 plane
1260  // * 2 -> field of view = 2 * half angle
1261 
1262  // We need some intermediate values.
1263  Vec dummy = line_1^column_0;
1264  dummy.normalize();
1265  float fov = acos(column_2*dummy) * 2.0;
1266 
1267  // We set the camera.
1268  Quaternion q;
1269  q.setFromRotationMatrix(rot);
1270  setOrientation(q);
1271  setPosition(cam_pos);
1272  setFieldOfView(fov);
1273 }
1274 
1275 
1276 /*
1277  // persp : projectionMatrix_[0] = f/aspectRatio();
1278 void Camera::setFromProjectionMatrix(const GLdouble* projectionMatrix)
1279 {
1280  QString message;
1281  if ((fabs(projectionMatrix[1]) > 1E-3) ||
1282  (fabs(projectionMatrix[2]) > 1E-3) ||
1283  (fabs(projectionMatrix[3]) > 1E-3) ||
1284  (fabs(projectionMatrix[4]) > 1E-3) ||
1285  (fabs(projectionMatrix[6]) > 1E-3) ||
1286  (fabs(projectionMatrix[7]) > 1E-3) ||
1287  (fabs(projectionMatrix[8]) > 1E-3) ||
1288  (fabs(projectionMatrix[9]) > 1E-3))
1289  message = "Non null coefficient in projection matrix - Aborting";
1290  else
1291  if ((fabs(projectionMatrix[11]+1.0) < 1E-5) && (fabs(projectionMatrix[15]) < 1E-5))
1292  {
1293  if (projectionMatrix[5] < 1E-4)
1294  message="Negative field of view in Camera::setFromProjectionMatrix";
1295  else
1296  setType(Camera::PERSPECTIVE);
1297  }
1298  else
1299  if ((fabs(projectionMatrix[11]) < 1E-5) && (fabs(projectionMatrix[15]-1.0) < 1E-5))
1300  setType(Camera::ORTHOGRAPHIC);
1301  else
1302  message = "Unable to determine camera type in setFromProjectionMatrix - Aborting";
1303 
1304  if (!message.isEmpty())
1305  {
1306  qWarning(message);
1307  return;
1308  }
1309 
1310  switch (type())
1311  {
1312  case Camera::PERSPECTIVE:
1313  {
1314  setFieldOfView(2.0 * atan(1.0/projectionMatrix[5]));
1315  const float far = projectionMatrix[14] / (2.0 * (1.0 + projectionMatrix[10]));
1316  const float near = (projectionMatrix[10]+1.0) / (projectionMatrix[10]-1.0) * far;
1317  setSceneRadius((far-near)/2.0);
1318  setSceneCenter(position() + (near + sceneRadius())*viewDirection());
1319  break;
1320  }
1321  case Camera::ORTHOGRAPHIC:
1322  {
1323  GLdouble w, h;
1324  getOrthoWidthHeight(w,h);
1325  projectionMatrix_[0] = 1.0/w;
1326  projectionMatrix_[5] = 1.0/h;
1327  projectionMatrix_[10] = -2.0/(ZFar - ZNear);
1328  projectionMatrix_[11] = 0.0;
1329  projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear);
1330  projectionMatrix_[15] = 1.0;
1331  // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
1332  break;
1333  }
1334  }
1335 }
1336 */
1337 
1339 
1341 void Camera::getCameraCoordinatesOf(const float src[3], float res[3]) const
1342 {
1343  Vec r = cameraCoordinatesOf(Vec(src));
1344  for (int i=0; i<3; ++i)
1345  res[i] = r[i];
1346 }
1347 
1349 void Camera::getWorldCoordinatesOf(const float src[3], float res[3]) const
1350 {
1351  Vec r = worldCoordinatesOf(Vec(src));
1352  for (int i=0; i<3; ++i)
1353  res[i] = r[i];
1354 }
1355 
1361 void Camera::getViewport(GLint viewport[4]) const
1362 {
1363  viewport[0] = 0;
1364  viewport[1] = screenHeight();
1365  viewport[2] = screenWidth();
1366  viewport[3] = -screenHeight();
1367 }
1368 
1443 Vec Camera::projectedCoordinatesOf(const Vec& src, const Frame* frame) const
1444 {
1445  GLdouble x,y,z;
1446  static GLint viewport[4];
1447  getViewport(viewport);
1448 
1449  if (frame)
1450  {
1451  const Vec tmp = frame->inverseCoordinatesOf(src);
1452  gluProject(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1453  }
1454  else
1455  gluProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1456 
1457  return Vec(x,y,z);
1458 }
1459 
1485 Vec Camera::unprojectedCoordinatesOf(const Vec& src, const Frame* frame) const
1486 {
1487  GLdouble x,y,z;
1488  static GLint viewport[4];
1489  getViewport(viewport);
1490  gluUnProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1491  if (frame)
1492  return frame->coordinatesOf(Vec(x,y,z));
1493  else
1494  return Vec(x,y,z);
1495 }
1496 
1498 void Camera::getProjectedCoordinatesOf(const float src[3], float res[3], const Frame* frame) const
1499 {
1500  Vec r = projectedCoordinatesOf(Vec(src), frame);
1501  for (int i=0; i<3; ++i)
1502  res[i] = r[i];
1503 }
1504 
1506 void Camera::getUnprojectedCoordinatesOf(const float src[3], float res[3], const Frame* frame) const
1507 {
1508  Vec r = unprojectedCoordinatesOf(Vec(src), frame);
1509  for (int i=0; i<3; ++i)
1510  res[i] = r[i];
1511 }
1512 
1514 
1519 {
1520  if (kfi_.contains(i))
1521  return kfi_[i];
1522  else
1523  return NULL;
1524 }
1525 
1545 {
1546  if (kfi)
1547  kfi_[i] = kfi;
1548  else
1549  kfi_.remove(i);
1550 }
1551 
1565 {
1566  if (!kfi_.contains(i))
1568 
1569  kfi_[i]->addKeyFrame(*(frame()));
1570 }
1571 
1580 void Camera::playPath(int i)
1581 {
1582  if (kfi_.contains(i)) {
1583  if (kfi_[i]->interpolationIsStarted())
1584  kfi_[i]->stopInterpolation();
1585  else
1586  kfi_[i]->startInterpolation();
1587  }
1588 }
1589 
1596 {
1597  if (kfi_.contains(i)) {
1598  if ((kfi_[i]->interpolationIsStarted()))
1599  kfi_[i]->stopInterpolation();
1600  else
1601  {
1602  kfi_[i]->resetInterpolation();
1603  kfi_[i]->interpolateAtTime(kfi_[i]->interpolationTime());
1604  }
1605  }
1606 }
1607 
1617 {
1618  if (kfi_.contains(i))
1619  {
1620  kfi_[i]->stopInterpolation();
1621  delete kfi_[i];
1622  kfi_.remove(i);
1623  }
1624 }
1625 
1633 {
1634  for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it)
1635 #if QT_VERSION >= 0x040000
1636  (it.value())->drawPath(3, 5, sceneRadius());
1637 #else
1638  (it.data())->drawPath(3, 5, sceneRadius());
1639 #endif
1640 }
1641 
1643 
1669 QDomElement Camera::domElement(const QString& name, QDomDocument& document) const
1670 {
1671  QDomElement de = document.createElement(name);
1672  QDomElement paramNode = document.createElement("Parameters");
1673  paramNode.setAttribute("fieldOfView", QString::number(fieldOfView()));
1674  paramNode.setAttribute("zNearCoefficient", QString::number(zNearCoefficient()));
1675  paramNode.setAttribute("zClippingCoefficient", QString::number(zClippingCoefficient()));
1676  paramNode.setAttribute("orthoCoef", QString::number(orthoCoef_));
1677  paramNode.setAttribute("sceneRadius", QString::number(sceneRadius()));
1678  paramNode.appendChild(sceneCenter().domElement("SceneCenter", document));
1679 
1680  switch (type())
1681  {
1682  case Camera::PERSPECTIVE : paramNode.setAttribute("Type", "PERSPECTIVE"); break;
1683  case Camera::ORTHOGRAPHIC : paramNode.setAttribute("Type", "ORTHOGRAPHIC"); break;
1684  }
1685  de.appendChild(paramNode);
1686 
1687  QDomElement stereoNode = document.createElement("Stereo");
1688  stereoNode.setAttribute("IODist", QString::number(IODistance()));
1689  stereoNode.setAttribute("distToScreen", QString::number(physicalDistanceToScreen()));
1690  stereoNode.setAttribute("focusDistance", QString::number(focusDistance()));
1691  stereoNode.setAttribute("physScreenWidth", QString::number(physicalScreenWidth()));
1692  de.appendChild(stereoNode);
1693 
1694  de.appendChild(frame()->domElement("ManipulatedCameraFrame", document));
1695 
1696  // KeyFrame paths
1697  for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it)
1698  {
1699 #if QT_VERSION >= 0x040000
1700  QDomElement kfNode = (it.value())->domElement("KeyFrameInterpolator", document);
1701 #else
1702  QDomElement kfNode = (it.data())->domElement("KeyFrameInterpolator", document);
1703 #endif
1704  kfNode.setAttribute("index", QString::number(it.key()));
1705  de.appendChild(kfNode);
1706  }
1707 
1708  return de;
1709 }
1710 
1732 void Camera::initFromDOMElement(const QDomElement& element)
1733 {
1734  QDomElement child=element.firstChild().toElement();
1735 
1736 #if QT_VERSION >= 0x040000
1737  QMutableMapIterator<int, KeyFrameInterpolator*> it(kfi_);
1738  while (it.hasNext()) {
1739  it.next();
1740 #else
1741  for (QMap<int, KeyFrameInterpolator*>::Iterator it = kfi_.begin(), end=kfi_.end(); it != end; ++it) {
1742 #endif
1743  deletePath(it.key());
1744  }
1745 
1746  while (!child.isNull())
1747  {
1748  if (child.tagName() == "Parameters")
1749  {
1750  // #CONNECTION# Default values set in constructor
1751  setFieldOfView(DomUtils::floatFromDom(child, "fieldOfView", M_PI/4.0f));
1752  setZNearCoefficient(DomUtils::floatFromDom(child, "zNearCoefficient", 0.005f));
1753  setZClippingCoefficient(DomUtils::floatFromDom(child, "zClippingCoefficient", sqrt(3.0)));
1754  orthoCoef_ = DomUtils::floatFromDom(child, "orthoCoef", tan(fieldOfView()/2.0));
1755  setSceneRadius(DomUtils::floatFromDom(child, "sceneRadius", sceneRadius()));
1756 
1757  setType(PERSPECTIVE);
1758  QString type = child.attribute("Type", "PERSPECTIVE");
1759  if (type == "PERSPECTIVE") setType(Camera::PERSPECTIVE);
1760  if (type == "ORTHOGRAPHIC") setType(Camera::ORTHOGRAPHIC);
1761 
1762  QDomElement child2=child.firstChild().toElement();
1763  while (!child2.isNull())
1764  {
1765  /* Although the scene does not change when a camera is loaded, restore the saved center and radius values.
1766  Mainly useful when a the viewer is restored on startup, with possible additional cameras. */
1767  if (child2.tagName() == "SceneCenter")
1768  setSceneCenter(Vec(child2));
1769 
1770  child2 = child2.nextSibling().toElement();
1771  }
1772  }
1773 
1774  if (child.tagName() == "ManipulatedCameraFrame")
1775  frame()->initFromDOMElement(child);
1776 
1777  if (child.tagName() == "Stereo")
1778  {
1779  setIODistance(DomUtils::floatFromDom(child, "IODist", 0.062f));
1780  setPhysicalDistanceToScreen(DomUtils::floatFromDom(child, "distToScreen", 0.5f));
1781  setFocusDistance(DomUtils::floatFromDom(child, "focusDistance", focusDistance()));
1782  setPhysicalScreenWidth(DomUtils::floatFromDom(child, "physScreenWidth", 0.4f));
1783  }
1784 
1785  if (child.tagName() == "KeyFrameInterpolator")
1786  {
1787  int index = DomUtils::intFromDom(child, "index", 0);
1789  if (keyFrameInterpolator(index))
1791  }
1792 
1793  child = child.nextSibling().toElement();
1794  }
1795 }
1796 
1808 void Camera::convertClickToLine(const QPoint& pixel, Vec& orig, Vec& dir) const
1809 {
1810  switch (type())
1811  {
1812  case Camera::PERSPECTIVE:
1813  orig = position();
1814  dir = Vec( ((2.0 * pixel.x() / screenWidth()) - 1.0) * tan(fieldOfView()/2.0) * aspectRatio(),
1815  ((2.0 * (screenHeight()-pixel.y()) / screenHeight()) - 1.0) * tan(fieldOfView()/2.0),
1816  -1.0 );
1817  dir = worldCoordinatesOf(dir) - orig;
1818  dir.normalize();
1819  break;
1820 
1821  case Camera::ORTHOGRAPHIC:
1822  {
1823  GLdouble w,h;
1824  getOrthoWidthHeight(w,h);
1825  orig = Vec((2.0 * pixel.x() / screenWidth() - 1.0)*w, -(2.0 * pixel.y() / screenHeight() - 1.0)*h, 0.0);
1826  orig = worldCoordinatesOf(orig);
1827  dir = viewDirection();
1828  break;
1829  }
1830  }
1831 }
1832 
1833 #ifndef DOXYGEN
1834 
1835 void Camera::drawCamera(float, float, float)
1836 {
1837  qWarning("drawCamera is deprecated. Use Camera::draw() instead.");
1838 }
1839 #endif
1840 
1861 void Camera::draw(bool drawFarPlane, float scale) const
1862 {
1863  glPushMatrix();
1864  glMultMatrixd(frame()->worldMatrix());
1865 
1866  // 0 is the upper left coordinates of the near corner, 1 for the far one
1867  Vec points[2];
1868 
1869  points[0].z = scale * zNear();
1870  points[1].z = scale * zFar();
1871 
1872  switch (type())
1873  {
1874  case Camera::PERSPECTIVE:
1875  {
1876  points[0].y = points[0].z * tan(fieldOfView()/2.0);
1877  points[0].x = points[0].y * aspectRatio();
1878 
1879  const float ratio = points[1].z / points[0].z;
1880 
1881  points[1].y = ratio * points[0].y;
1882  points[1].x = ratio * points[0].x;
1883  break;
1884  }
1885  case Camera::ORTHOGRAPHIC:
1886  {
1887  GLdouble hw, hh;
1888  getOrthoWidthHeight(hw, hh);
1889  points[0].x = points[1].x = scale * float(hw);
1890  points[0].y = points[1].y = scale * float(hh);
1891  break;
1892  }
1893  }
1894 
1895  const int farIndex = drawFarPlane?1:0;
1896 
1897  // Near and (optionally) far plane(s)
1898  glBegin(GL_QUADS);
1899  for (int i=farIndex; i>=0; --i)
1900  {
1901  glNormal3f(0.0, 0.0, (i==0)?1.0:-1.0);
1902  glVertex3f( points[i].x, points[i].y, -points[i].z);
1903  glVertex3f(-points[i].x, points[i].y, -points[i].z);
1904  glVertex3f(-points[i].x, -points[i].y, -points[i].z);
1905  glVertex3f( points[i].x, -points[i].y, -points[i].z);
1906  }
1907  glEnd();
1908 
1909  // Up arrow
1910  const float arrowHeight = 1.5f * points[0].y;
1911  const float baseHeight = 1.2f * points[0].y;
1912  const float arrowHalfWidth = 0.5f * points[0].x;
1913  const float baseHalfWidth = 0.3f * points[0].x;
1914 
1915  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1916  // Base
1917  glBegin(GL_QUADS);
1918  glVertex3f(-baseHalfWidth, points[0].y, -points[0].z);
1919  glVertex3f( baseHalfWidth, points[0].y, -points[0].z);
1920  glVertex3f( baseHalfWidth, baseHeight, -points[0].z);
1921  glVertex3f(-baseHalfWidth, baseHeight, -points[0].z);
1922  glEnd();
1923 
1924  // Arrow
1925  glBegin(GL_TRIANGLES);
1926  glVertex3f( 0.0f, arrowHeight, -points[0].z);
1927  glVertex3f(-arrowHalfWidth, baseHeight, -points[0].z);
1928  glVertex3f( arrowHalfWidth, baseHeight, -points[0].z);
1929  glEnd();
1930 
1931  // Frustum lines
1932  switch (type())
1933  {
1934  case Camera::PERSPECTIVE :
1935  glBegin(GL_LINES);
1936  glVertex3f(0.0f, 0.0f, 0.0f);
1937  glVertex3f( points[farIndex].x, points[farIndex].y, -points[farIndex].z);
1938  glVertex3f(0.0f, 0.0f, 0.0f);
1939  glVertex3f(-points[farIndex].x, points[farIndex].y, -points[farIndex].z);
1940  glVertex3f(0.0f, 0.0f, 0.0f);
1941  glVertex3f(-points[farIndex].x, -points[farIndex].y, -points[farIndex].z);
1942  glVertex3f(0.0f, 0.0f, 0.0f);
1943  glVertex3f( points[farIndex].x, -points[farIndex].y, -points[farIndex].z);
1944  glEnd();
1945  break;
1946  case Camera::ORTHOGRAPHIC :
1947  if (drawFarPlane)
1948  {
1949  glBegin(GL_LINES);
1950  glVertex3f( points[0].x, points[0].y, -points[0].z);
1951  glVertex3f( points[1].x, points[1].y, -points[1].z);
1952  glVertex3f(-points[0].x, points[0].y, -points[0].z);
1953  glVertex3f(-points[1].x, points[1].y, -points[1].z);
1954  glVertex3f(-points[0].x, -points[0].y, -points[0].z);
1955  glVertex3f(-points[1].x, -points[1].y, -points[1].z);
1956  glVertex3f( points[0].x, -points[0].y, -points[0].z);
1957  glVertex3f( points[1].x, -points[1].y, -points[1].z);
1958  glEnd();
1959  }
1960  }
1961 
1962  glPopMatrix();
1963 }
1964 
1965 
1989 void Camera::getFrustumPlanesCoefficients(GLdouble coef[6][4]) const
1990 {
1991  // Computed once and for all
1992  const Vec pos = position();
1993  const Vec viewDir = viewDirection();
1994  const Vec up = upVector();
1995  const Vec right = rightVector();
1996  const float posViewDir = pos * viewDir;
1997 
1998  static Vec normal[6];
1999  static GLdouble dist[6];
2000 
2001  switch (type())
2002  {
2003  case Camera::PERSPECTIVE :
2004  {
2005  const float hhfov = horizontalFieldOfView() / 2.0;
2006  const float chhfov = cos(hhfov);
2007  const float shhfov = sin(hhfov);
2008  normal[0] = - shhfov * viewDir;
2009  normal[1] = normal[0] + chhfov * right;
2010  normal[0] = normal[0] - chhfov * right;
2011 
2012  normal[2] = -viewDir;
2013  normal[3] = viewDir;
2014 
2015  const float hfov = fieldOfView() / 2.0;
2016  const float chfov = cos(hfov);
2017  const float shfov = sin(hfov);
2018  normal[4] = - shfov * viewDir;
2019  normal[5] = normal[4] - chfov * up;
2020  normal[4] = normal[4] + chfov * up;
2021 
2022  for (int i=0; i<2; ++i)
2023  dist[i] = pos * normal[i];
2024  for (int j=4; j<6; ++j)
2025  dist[j] = pos * normal[j];
2026 
2027  // Natural equations are:
2028  // dist[0,1,4,5] = pos * normal[0,1,4,5];
2029  // dist[2] = (pos + zNear() * viewDir) * normal[2];
2030  // dist[3] = (pos + zFar() * viewDir) * normal[3];
2031 
2032  // 2 times less computations using expanded/merged equations. Dir vectors are normalized.
2033  const float posRightCosHH = chhfov * pos * right;
2034  dist[0] = -shhfov * posViewDir;
2035  dist[1] = dist[0] + posRightCosHH;
2036  dist[0] = dist[0] - posRightCosHH;
2037  const float posUpCosH = chfov * pos * up;
2038  dist[4] = - shfov * posViewDir;
2039  dist[5] = dist[4] - posUpCosH;
2040  dist[4] = dist[4] + posUpCosH;
2041 
2042  break;
2043  }
2044  case Camera::ORTHOGRAPHIC :
2045  normal[0] = -right;
2046  normal[1] = right;
2047  normal[4] = up;
2048  normal[5] = -up;
2049 
2050  GLdouble hw, hh;
2051  getOrthoWidthHeight(hw, hh);
2052  dist[0] = (pos - hw * right) * normal[0];
2053  dist[1] = (pos + hw * right) * normal[1];
2054  dist[4] = (pos + hh * up) * normal[4];
2055  dist[5] = (pos - hh * up) * normal[5];
2056  break;
2057  }
2058 
2059  // Front and far planes are identical for both camera types.
2060  normal[2] = -viewDir;
2061  normal[3] = viewDir;
2062  dist[2] = -posViewDir - zNear();
2063  dist[3] = posViewDir + zFar();
2064 
2065  for (int i=0; i<6; ++i)
2066  {
2067  coef[i][0] = GLdouble(normal[i].x);
2068  coef[i][1] = GLdouble(normal[i].y);
2069  coef[i][2] = GLdouble(normal[i].z);
2070  coef[i][3] = dist[i];
2071  }
2072 }