1 /****************************************************************************
2
3 Copyright (c) 2018 GeometryFactory Sarl (France).
4 Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
5
6 This file is part of a fork of the QGLViewer library version 2.7.0.
7
8 *****************************************************************************/
9 // $URL: https://github.com/CGAL/cgal/blob/v5.3/GraphicsView/include/CGAL/Qt/camera_impl.h $
10 // $Id: camera_impl.h db338aa 2021-02-18T15:19:06+01:00 Laurent Rineau
11 // SPDX-License-Identifier: GPL-3.0-only
12
13 #ifdef CGAL_HEADER_ONLY
14 #define CGAL_INLINE_FUNCTION inline
15
16 #include <CGAL/license/GraphicsView.h>
17
18 #else
19 #define CGAL_INLINE_FUNCTION
20 #endif
21
22 #include <CGAL/Qt/camera.h>
23 #include <CGAL/Qt/manipulatedCameraFrame.h>
24 #include <CGAL/Qt/keyFrameInterpolator.h>
25
26 namespace CGAL{
27 namespace qglviewer{
28
29 /*! Default constructor.
30
31 sceneCenter() is set to (0,0,0) and sceneRadius() is set to 1.0. type() is
32 Camera::PERSPECTIVE, with a \c M_PI/4 fieldOfView().
33
34 See IODistance(), physicalDistanceToScreen(), physicalScreenWidth() and
35 focusDistance(). */
36 CGAL_INLINE_FUNCTION
Camera(QObject * parent)37 Camera::Camera(QObject *parent)
38 : frame_(nullptr), fieldOfView_(CGAL_PI / 4.0), modelViewMatrixIsUpToDate_(false),
39 projectionMatrixIsUpToDate_(false) {
40 m_zMin = 0;
41 setParent(parent);
42 // #CONNECTION# Camera copy constructor
43 interpolationKfi_ = new KeyFrameInterpolator;
44 // Requires the interpolationKfi_
45 setFrame(new ManipulatedCameraFrame());
46
47
48 // Requires fieldOfView() to define focusDistance()
49 setSceneRadius(1.0);
50
51 // Initial value (only scaled after this)
52 orthoCoef_ = tan(fieldOfView() / 2.0);
53
54 // Also defines the pivotPoint(), which changes orthoCoef_. Requires a
55 // frame().
56 setSceneCenter(Vec(0.0, 0.0, 0.0));
57
58 // Requires fieldOfView() when called with ORTHOGRAPHIC. Attention to
59 // projectionMatrix_ below.
60 setType(PERSPECTIVE);
61
62 setZNearCoefficient(0.005);
63 setZClippingCoefficient(sqrt(3.0));
64
65 // Dummy values
66 setScreenWidthAndHeight(600, 400);
67
68
69 // focusDistance is set from setFieldOfView()
70
71 // #CONNECTION# Camera copy constructor
72 for (unsigned short j = 0; j < 16; ++j) {
73 modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0);
74 // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost
75 // everywhere.
76 projectionMatrix_[j] = 0.0;
77 }
78 computeProjectionMatrix();
79 }
80
81 /*! Virtual destructor.
82
83 The frame() is deleted, but the different keyFrameInterpolator() are \e not
84 deleted (in case they are shared). */
85
86 CGAL_INLINE_FUNCTION
~Camera()87 Camera::~Camera() {
88 delete frame_;
89 delete interpolationKfi_;
90 }
91
92 /*! Copy constructor. Performs a deep copy using operator=(). */
93 CGAL_INLINE_FUNCTION
Camera(const Camera & camera)94 Camera::Camera(const Camera &camera) : QObject(), frame_(nullptr) {
95 // #CONNECTION# Camera constructor
96 interpolationKfi_ = new KeyFrameInterpolator;
97 // Requires the interpolationKfi_
98 setFrame(new ManipulatedCameraFrame(*camera.frame()));
99
100 for (unsigned short j = 0; j < 16; ++j) {
101 modelViewMatrix_[j] = ((j % 5 == 0) ? 1.0 : 0.0);
102 // #CONNECTION# computeProjectionMatrix() is lazy and assumes 0.0 almost
103 // everywhere.
104 projectionMatrix_[j] = 0.0;
105 }
106
107 (*this) = camera;
108 }
109
110 /*! Equal operator.
111
112 All the parameters of \p camera are copied. The frame() pointer is not
113 modified, but its Frame::position() and Frame::orientation() are set to those
114 of \p camera.
115
116 \attention The Camera screenWidth() and screenHeight() are set to those of \p
117 camera. If your Camera is associated with a CGAL::QGLViewer, you should update these
118 value after the call to this method: \code
119 *(camera()) = otherCamera;
120 camera()->setScreenWidthAndHeight(width(), height());
121 \endcode
122 The same applies to sceneCenter() and sceneRadius(), if needed. */
123 CGAL_INLINE_FUNCTION
124 Camera &Camera::operator=(const Camera &camera) {
125 setScreenWidthAndHeight(camera.screenWidth(), camera.screenHeight());
126 setFieldOfView(camera.fieldOfView());
127 setSceneRadius(camera.sceneRadius());
128 setSceneCenter(camera.sceneCenter());
129 setZNearCoefficient(camera.zNearCoefficient());
130 setZClippingCoefficient(camera.zClippingCoefficient());
131 setType(camera.type());
132
133 orthoCoef_ = camera.orthoCoef_;
134 projectionMatrixIsUpToDate_ = false;
135
136 // frame_ and interpolationKfi_ pointers are not shared.
137 frame_->setReferenceFrame(nullptr);
138 frame_->setPosition(camera.position());
139 frame_->setOrientation(camera.orientation());
140
141 interpolationKfi_->resetInterpolation();
142
143 kfi_ = camera.kfi_;
144
145 computeProjectionMatrix();
146 computeModelViewMatrix();
147
148 return *this;
149 }
150
151 /*! Sets Camera screenWidth() and screenHeight() (expressed in pixels).
152
153 You should not call this method when the Camera is associated with a CGAL::QGLViewer,
154 since the latter automatically updates these values when it is resized (hence
155 overwriting your values).
156
157 Non-positive dimension are silently replaced by a 1 pixel value to ensure
158 frustrum coherence.
159
160 If your Camera is used without a CGAL::QGLViewer (offscreen rendering, shadow maps),
161 use setAspectRatio() instead to define the projection matrix. */
162 CGAL_INLINE_FUNCTION
setScreenWidthAndHeight(int width,int height)163 void Camera::setScreenWidthAndHeight(int width, int height) {
164 // Prevent negative and zero dimensions that would cause divisions by zero.
165 screenWidth_ = width > 0 ? width : 1;
166 screenHeight_ = height > 0 ? height : 1;
167 projectionMatrixIsUpToDate_ = false;
168 }
169
170 /*! Returns the near clipping plane distance used by the Camera projection
171 matrix.
172
173 The clipping planes' positions depend on the sceneRadius() and sceneCenter()
174 rather than being fixed small-enough and large-enough values. A good scene
175 dimension approximation will hence result in an optimal precision of the
176 z-buffer.
177
178 The near clipping plane is positioned at a distance equal to
179 zClippingCoefficient() * sceneRadius() in front of the sceneCenter(): \code
180 zNear = distanceToSceneCenter() - zClippingCoefficient()*sceneRadius();
181 \endcode
182
183 In order to prevent negative or too small zNear() values (which would degrade
184 the z precision), zNearCoefficient() is used when the Camera is inside the
185 sceneRadius() sphere: \code const qreal zMin = zNearCoefficient() *
186 zClippingCoefficient() * sceneRadius(); if (zNear < zMin) zNear = zMin;
187 // With an ORTHOGRAPHIC type, the value is simply clamped to 0.0
188 \endcode
189
190 See also the zFar(), zClippingCoefficient() and zNearCoefficient()
191 documentations.
192
193 If you need a completely different zNear computation, overload the zNear() and
194 zFar() methods in a new class that publicly inherits from Camera and use
195 CGAL::QGLViewer::setCamera(): \code class myCamera :: public CGAL::qglviewer::Camera
196 {
197 virtual qreal Camera::zNear() const { return 0.001; };
198 virtual qreal Camera::zFar() const { return 100.0; };
199 }
200 \endcode
201
202 See the <a href="../examples/standardCamera.html">standardCamera example</a>
203 for an application.
204
205 \attention The value is always positive although the clipping plane is
206 positioned at a negative z value in the Camera coordinate system. This follows
207 the \c gluPerspective standard. */
208 CGAL_INLINE_FUNCTION
zNear()209 qreal Camera::zNear() const {
210 const qreal zNearScene = zClippingCoefficient() * sceneRadius();
211 qreal z = distanceToSceneCenter() - zNearScene;
212
213 // Prevents negative or null zNear values.
214 const qreal zMin = zNearCoefficient() * zNearScene;
215 if (z < zMin)
216 switch (type()) {
217 case Camera::PERSPECTIVE:
218 z = zMin;
219 break;
220 case Camera::ORTHOGRAPHIC:
221 z = m_zMin;
222 break;
223 }
224 return z;
225 }
226
227 /*! Returns the far clipping plane distance used by the Camera projection
228 matrix.
229
230 The far clipping plane is positioned at a distance equal to
231 zClippingCoefficient() * sceneRadius() behind the sceneCenter(): \code zFar =
232 distanceToSceneCenter() + zClippingCoefficient()*sceneRadius(); \endcode
233
234 See the zNear() documentation for details. */
235 CGAL_INLINE_FUNCTION
zFar()236 qreal Camera::zFar() const {
237 return distanceToSceneCenter() + zClippingCoefficient() * sceneRadius();
238 }
239
240 /*! Sets the vertical fieldOfView() of the Camera (in radians).
241
242 Note that focusDistance() is set to sceneRadius() / tan(fieldOfView()/2) by this
243 method. */
244 CGAL_INLINE_FUNCTION
setFieldOfView(qreal fov)245 void Camera::setFieldOfView(qreal fov) {
246 fieldOfView_ = fov;
247 projectionMatrixIsUpToDate_ = false;
248 }
249
250 /*! Defines the Camera type().
251
252 Changing the camera Type alters the viewport and the objects' sizes can be
253 changed. This method guarantees that the two frustum match in a plane normal to
254 viewDirection(), passing through the pivotPoint().
255
256 Prefix the type with \c Camera if needed, as in:
257 \code
258 camera()->setType(Camera::ORTHOGRAPHIC);
259 // or even CGAL::qglviewer::Camera::ORTHOGRAPHIC if you do not use namespace
260 \endcode */
261 CGAL_INLINE_FUNCTION
setType(Type type)262 void Camera::setType(Type type) {
263 // make ORTHOGRAPHIC frustum fit PERSPECTIVE (at least in plane normal to
264 // viewDirection(), passing through RAP). Done only when CHANGING type since
265 // orthoCoef_ may have been changed with a setPivotPoint() in the meantime.
266 if ((type == Camera::ORTHOGRAPHIC) && (type_ == Camera::PERSPECTIVE))
267 orthoCoef_ = tan(fieldOfView() / 2.0);
268 type_ = type;
269 projectionMatrixIsUpToDate_ = false;
270 }
271
272 /*! Sets the Camera frame().
273
274 If you want to move the Camera, use setPosition() and setOrientation() or one of
275 the Camera positioning methods (lookAt(), fitSphere(), showEntireScene()...)
276 instead.
277
278 If you want to save the Camera position(), there's no need to call this method
279 either. Use addKeyFrameToPath() and playPath() instead.
280
281 This method is actually mainly useful if you derive the ManipulatedCameraFrame
282 class and want to use an instance of your new class to move the Camera.
283
284 A \c nullptr \p mcf pointer will silently be ignored. The calling method is
285 responsible for deleting the previous frame() pointer if needed in order to
286 prevent memory leaks. */
287 CGAL_INLINE_FUNCTION
setFrame(ManipulatedCameraFrame * const mcf)288 void Camera::setFrame(ManipulatedCameraFrame *const mcf) {
289 if (!mcf)
290 return;
291
292 if (frame_) {
293 disconnect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified()));
294 }
295
296 frame_ = mcf;
297 interpolationKfi_->setFrame(frame());
298
299 connect(frame_, SIGNAL(modified()), this, SLOT(onFrameModified()));
300 onFrameModified();
301 }
302
303 /*! Returns the distance from the Camera center to sceneCenter(), projected
304 along the Camera Z axis. Used by zNear() and zFar() to optimize the Z range.
305 */
306 CGAL_INLINE_FUNCTION
distanceToSceneCenter()307 qreal Camera::distanceToSceneCenter() const {
308 return fabs((frame()->coordinatesOf(sceneCenter())).z);
309 }
310
311 /*! Returns the \p halfWidth and \p halfHeight of the Camera orthographic
312 frustum.
313
314 These values are only valid and used when the Camera is of type()
315 Camera::ORTHOGRAPHIC. They are expressed in OpenGL units and are used by
316 loadProjectionMatrix() to define the projection matrix using: \code glOrtho(
317 -halfWidth, halfWidth, -halfHeight, halfHeight, zNear(), zFar() ) \endcode
318
319 These values are proportional to the Camera (z projected) distance to the
320 pivotPoint(). When zooming on the object, the Camera is translated forward \e
321 and its frustum is narrowed, making the object appear bigger on screen, as
322 intuitively expected.
323
324 Overload this method to change this behavior if desired, as is done in the
325 <a href="../examples/standardCamera.html">standardCamera example</a>. */
326 CGAL_INLINE_FUNCTION
getOrthoWidthHeight(GLdouble & halfWidth,GLdouble & halfHeight)327 void Camera::getOrthoWidthHeight(GLdouble &halfWidth,
328 GLdouble &halfHeight) const {
329 const qreal dist = orthoCoef_ * fabs(cameraCoordinatesOf(pivotPoint()).z);
330 //#CONNECTION# fitScreenRegion
331 halfWidth = dist * ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
332 halfHeight = dist * ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0);
333 }
334
335 /*! Computes the projection matrix associated with the Camera.
336
337 If type() is Camera::PERSPECTIVE, defines a \c GL_PROJECTION matrix similar to
338 what would \c gluPerspective() do using the fieldOfView(), window
339 aspectRatio(), zNear() and zFar() parameters.
340
341 If type() is Camera::ORTHOGRAPHIC, the projection matrix is as what \c
342 glOrtho() would do. Frustum's width and height are set using
343 getOrthoWidthHeight().
344
345 Both types use zNear() and zFar() to place clipping planes. These values are
346 determined from sceneRadius() and sceneCenter() so that they best fit the scene
347 size.
348
349 Use getProjectionMatrix() to retrieve this matrix. Overload
350 loadProjectionMatrix() if you want your Camera to use an exotic projection
351 matrix.
352
353 \note You must call this method if your Camera is not associated with a
354 CGAL::QGLViewer and is used for offscreen computations (using
355 (un)projectedCoordinatesOf() for instance). loadProjectionMatrix() does it
356 otherwise. */
357 CGAL_INLINE_FUNCTION
computeProjectionMatrix()358 void Camera::computeProjectionMatrix() const {
359 if (projectionMatrixIsUpToDate_)
360 return;
361
362 const qreal ZNear = zNear();
363 const qreal ZFar = zFar();
364
365 switch (type()) {
366 case Camera::PERSPECTIVE: {
367 // #CONNECTION# all non null coefficients were set to 0.0 in constructor.
368 const qreal f = 1.0 / tan(fieldOfView() / 2.0);
369 projectionMatrix_[0] = f / aspectRatio();
370 projectionMatrix_[5] = f;
371 projectionMatrix_[10] = (ZNear + ZFar) / (ZNear - ZFar);
372 projectionMatrix_[11] = -1.0;
373 projectionMatrix_[14] = 2.0 * ZNear * ZFar / (ZNear - ZFar);
374 projectionMatrix_[15] = 0.0;
375 // same as gluPerspective( 180.0*fieldOfView()/CGAL_PI, aspectRatio(), zNear(),
376 // zFar() );
377 break;
378 }
379 case Camera::ORTHOGRAPHIC: {
380 GLdouble w, h;
381 getOrthoWidthHeight(w, h);
382 projectionMatrix_[0] = 1.0 / w;
383 projectionMatrix_[5] = 1.0 / h;
384 projectionMatrix_[10] = -2.0 / (ZFar - ZNear);
385 projectionMatrix_[11] = 0.0;
386 projectionMatrix_[14] = -(ZFar + ZNear) / (ZFar - ZNear);
387 projectionMatrix_[15] = 1.0;
388 // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
389 break;
390 }
391 }
392
393 projectionMatrixIsUpToDate_ = true;
394 }
395
396 /*! Computes the modelView matrix associated with the Camera's position() and
397 orientation().
398
399 This matrix converts from the world coordinates system to the Camera
400 coordinates system, so that coordinates can then be projected on screen using
401 the projection matrix (see computeProjectionMatrix()).
402
403 Use getModelViewMatrix() to retrieve this matrix.
404
405 \note You must call this method if your Camera is not associated with a
406 CGAL::QGLViewer and is used for offscreen computations (using
407 (un)projectedCoordinatesOf() for instance). loadModelViewMatrix() does it
408 otherwise. */
409 CGAL_INLINE_FUNCTION
computeModelViewMatrix()410 void Camera::computeModelViewMatrix() const {
411 if (modelViewMatrixIsUpToDate_)
412 return;
413
414 const Quaternion q = frame()->orientation();
415
416 const qreal q00 = 2.0 * q[0] * q[0];
417 const qreal q11 = 2.0 * q[1] * q[1];
418 const qreal q22 = 2.0 * q[2] * q[2];
419
420 const qreal q01 = 2.0 * q[0] * q[1];
421 const qreal q02 = 2.0 * q[0] * q[2];
422 const qreal q03 = 2.0 * q[0] * q[3];
423
424 const qreal q12 = 2.0 * q[1] * q[2];
425 const qreal q13 = 2.0 * q[1] * q[3];
426
427 const qreal q23 = 2.0 * q[2] * q[3];
428
429 modelViewMatrix_[0] = 1.0 - q11 - q22;
430 modelViewMatrix_[1] = q01 - q23;
431 modelViewMatrix_[2] = q02 + q13;
432 modelViewMatrix_[3] = 0.0;
433
434 modelViewMatrix_[4] = q01 + q23;
435 modelViewMatrix_[5] = 1.0 - q22 - q00;
436 modelViewMatrix_[6] = q12 - q03;
437 modelViewMatrix_[7] = 0.0;
438
439 modelViewMatrix_[8] = q02 - q13;
440 modelViewMatrix_[9] = q12 + q03;
441 modelViewMatrix_[10] = 1.0 - q11 - q00;
442 modelViewMatrix_[11] = 0.0;
443
444 const Vec t = q.inverseRotate(frame()->position());
445
446 modelViewMatrix_[12] = -t.x;
447 modelViewMatrix_[13] = -t.y;
448 modelViewMatrix_[14] = -t.z;
449 modelViewMatrix_[15] = 1.0;
450
451 modelViewMatrixIsUpToDate_ = true;
452 }
453
454 /*! Loads the OpenGL \c GL_PROJECTION matrix with the Camera projection matrix.
455
456 The Camera projection matrix is computed using computeProjectionMatrix().
457
458 When \p reset is \c true (default), the method clears the previous projection
459 matrix by calling \c glLoadIdentity before setting the matrix. Setting \p reset
460 to \c false is useful for \c GL_SELECT mode, to combine the pushed matrix with
461 a picking matrix. See CGAL::QGLViewer::beginSelection() for details.
462
463 This method is used by CGAL::QGLViewer::preDraw() (called before user's
464 CGAL::QGLViewer::draw() method) to set the \c GL_PROJECTION matrix according to the
465 viewer's CGAL::QGLViewer::camera() settings.
466
467 Use getProjectionMatrix() to retrieve this matrix. Overload this method if you
468 want your Camera to use an exotic projection matrix. See also
469 loadModelViewMatrix().
470
471 \attention \c glMatrixMode is set to \c GL_PROJECTION.
472
473 \attention If you use several OpenGL contexts and bypass the Qt main refresh
474 loop, you should call QOpenGLWidget::makeCurrent() before this method in order
475 to activate the right OpenGL context. */
476 CGAL_INLINE_FUNCTION
loadProjectionMatrix(bool)477 void Camera::loadProjectionMatrix(bool ) const {
478 // WARNING: makeCurrent must be called by every calling method
479 computeProjectionMatrix();
480 }
481
482 /*! Loads the OpenGL \c GL_MODELVIEW matrix with the modelView matrix
483 corresponding to the Camera.
484
485 Calls computeModelViewMatrix() to compute the Camera's modelView matrix.
486
487 This method is used by CGAL::QGLViewer::preDraw() (called before user's
488 CGAL::QGLViewer::draw() method) to set the \c GL_MODELVIEW matrix according to the
489 viewer's CGAL::QGLViewer::camera() position() and orientation().
490
491 As a result, the vertices used in CGAL::QGLViewer::draw() can be defined in the so
492 called world coordinate system. They are multiplied by this matrix to get
493 converted to the Camera coordinate system, before getting projected using the
494 \c GL_PROJECTION matrix (see loadProjectionMatrix()).
495
496 When \p reset is \c true (default), the method loads (overwrites) the \c
497 GL_MODELVIEW matrix. Setting \p reset to \c false simply calls \c glMultMatrixd
498 (might be useful for some applications).
499
500 Overload this method or simply call glLoadMatrixd() at the beginning of
501 CGAL::QGLViewer::draw() if you want your Camera to use an exotic modelView matrix.
502 See also loadProjectionMatrix().
503
504 getModelViewMatrix() returns the 4x4 modelView matrix.
505
506 \attention glMatrixMode is set to \c GL_MODELVIEW
507
508 \attention If you use several OpenGL contexts and bypass the Qt main refresh
509 loop, you should call QOpenGLWidget::makeCurrent() before this method in order
510 to activate the right OpenGL context. */
511 CGAL_INLINE_FUNCTION
loadModelViewMatrix(bool)512 void Camera::loadModelViewMatrix(bool ) const {
513 // WARNING: makeCurrent must be called by every calling method
514 computeModelViewMatrix();
515 }
516
517
518
519
520 /*! Fills \p m with the Camera projection matrix values.
521
522 Based on computeProjectionMatrix() to make sure the Camera projection matrix is
523 up to date.
524
525 This matrix only reflects the Camera's internal parameters and it may differ
526 from the \c GL_PROJECTION matrix retrieved using \c
527 glGetDoublev(GL_PROJECTION_MATRIX, m). It actually represents the state of the
528 \c GL_PROJECTION after CGAL::QGLViewer::preDraw(), at the beginning of
529 CGAL::QGLViewer::draw(). If you modified the \c GL_PROJECTION matrix (for instance
530 using CGAL::QGLViewer::startScreenCoordinatesSystem()), the two results differ.
531
532 The result is an OpenGL 4x4 matrix, which is given in \e column-major order
533 (see \c glMultMatrix man page for details).
534
535 See also getModelViewMatrix() and setFromProjectionMatrix(). */
536 CGAL_INLINE_FUNCTION
getProjectionMatrix(GLdouble m[16])537 void Camera::getProjectionMatrix(GLdouble m[16]) const {
538 computeProjectionMatrix();
539 for (unsigned short i = 0; i < 16; ++i)
540 m[i] = projectionMatrix_[i];
541 }
542
543 /*! Overloaded getProjectionMatrix(GLdouble m[16]) method using a \c GLfloat
544 * array instead. */
545 CGAL_INLINE_FUNCTION
getProjectionMatrix(GLfloat m[16])546 void Camera::getProjectionMatrix(GLfloat m[16]) const {
547 static GLdouble mat[16];
548 getProjectionMatrix(mat);
549 for (unsigned short i = 0; i < 16; ++i)
550 m[i] = float(mat[i]);
551 }
552
553 /*! Fills \p m with the Camera modelView matrix values.
554
555 First calls computeModelViewMatrix() to define the Camera modelView matrix.
556
557 Note that this matrix may \e not be the one you would get from a \c
558 glGetDoublev(GL_MODELVIEW_MATRIX, m). It actually represents the state of the
559 \c GL_MODELVIEW after CGAL::QGLViewer::preDraw(), at the \e beginning of
560 CGAL::QGLViewer::draw(). It converts from the world to the Camera coordinate system.
561 As soon as you modify the \c GL_MODELVIEW in your CGAL::QGLViewer::draw() method
562 (using glTranslate, glRotate... or similar methods), the two matrices differ.
563
564 The result is an OpenGL 4x4 matrix, which is given in \e column-major order
565 (see \c glMultMatrix man page for details).
566
567 See also getProjectionMatrix() and setFromModelViewMatrix(). */
568 CGAL_INLINE_FUNCTION
getModelViewMatrix(GLdouble m[16])569 void Camera::getModelViewMatrix(GLdouble m[16]) const {
570 // May not be needed, but easier like this.
571 computeModelViewMatrix();
572 for (unsigned short i = 0; i < 16; ++i)
573 m[i] = modelViewMatrix_[i];
574 }
575
576 /*! Overloaded getModelViewMatrix(GLdouble m[16]) method using a \c GLfloat
577 * array instead. */
578 CGAL_INLINE_FUNCTION
getModelViewMatrix(GLfloat m[16])579 void Camera::getModelViewMatrix(GLfloat m[16]) const {
580 static GLdouble mat[16];
581 getModelViewMatrix(mat);
582 for (unsigned short i = 0; i < 16; ++i)
583 m[i] = float(mat[i]);
584 }
585
586 /*! Fills \p m with the product of the ModelView and Projection matrices.
587
588 Calls getModelViewMatrix() and getProjectionMatrix() and then fills \p m with
589 the product of these two matrices. */
590 CGAL_INLINE_FUNCTION
getModelViewProjectionMatrix(GLdouble m[16])591 void Camera::getModelViewProjectionMatrix(GLdouble m[16]) const {
592 GLdouble mv[16];
593 GLdouble proj[16];
594 getModelViewMatrix(mv);
595 getProjectionMatrix(proj);
596
597 for (unsigned short i = 0; i < 4; ++i) {
598 for (unsigned short j = 0; j < 4; ++j) {
599 qreal sum = 0.0;
600 for (unsigned short k = 0; k < 4; ++k)
601 sum += proj[i + 4 * k] * mv[k + 4 * j];
602 m[i + 4 * j] = sum;
603 }
604 }
605 }
606
607 /*! Overloaded getModelViewProjectionMatrix(GLdouble m[16]) method using a \c
608 * GLfloat array instead. */
609 CGAL_INLINE_FUNCTION
getModelViewProjectionMatrix(GLfloat m[16])610 void Camera::getModelViewProjectionMatrix(GLfloat m[16]) const {
611 static GLdouble mat[16];
612 getModelViewProjectionMatrix(mat);
613 for (unsigned short i = 0; i < 16; ++i)
614 m[i] = float(mat[i]);
615 }
616
617 /*! Sets the sceneRadius() value. Negative values are ignored.
618
619 \attention This methods also sets focusDistance() to sceneRadius() /
620 tan(fieldOfView()/2) and flySpeed() to 1% of sceneRadius(). */
621 CGAL_INLINE_FUNCTION
setSceneRadius(qreal radius)622 void Camera::setSceneRadius(qreal radius) {
623 if (radius <= 0.0) {
624 qWarning("Scene radius must be positive - Ignoring value");
625 return;
626 }
627
628 sceneRadius_ = radius;
629 projectionMatrixIsUpToDate_ = false;
630
631 frame()->setFlySpeed(0.01 * sceneRadius());
632 }
633
634 /*! Similar to setSceneRadius() and setSceneCenter(), but the scene limits are
635 defined by a (world axis aligned) bounding box. */
636 CGAL_INLINE_FUNCTION
setSceneBoundingBox(const Vec & min,const Vec & max)637 void Camera::setSceneBoundingBox(const Vec &min, const Vec &max) {
638 setSceneCenter((min + max) / 2.0);
639 setSceneRadius(0.5 * (max - min).norm());
640 }
641
642 /*! Sets the sceneCenter().
643
644 \attention This method also sets the pivotPoint() to sceneCenter(). */
645 CGAL_INLINE_FUNCTION
setSceneCenter(const Vec & center)646 void Camera::setSceneCenter(const Vec ¢er) {
647 sceneCenter_ = center;
648 setPivotPoint(sceneCenter());
649 projectionMatrixIsUpToDate_ = false;
650 }
651
652 /*! setSceneCenter() to the result of pointUnderPixel(\p pixel).
653
654 Returns \c true if a pointUnderPixel() was found and sceneCenter() was
655 actually changed.
656
657 See also setPivotPointFromPixel(). See the pointUnderPixel() documentation. */
658 CGAL_INLINE_FUNCTION
setSceneCenterFromPixel(const QPoint & pixel)659 bool Camera::setSceneCenterFromPixel(const QPoint &pixel) {
660 bool found;
661 Vec point = pointUnderPixel(pixel, found);
662 if (found)
663 setSceneCenter(point);
664 return found;
665 }
666
667 /*! Changes the pivotPoint() to \p point (defined in the world coordinate
668 * system). */
669 CGAL_INLINE_FUNCTION
setPivotPoint(const Vec & point)670 void Camera::setPivotPoint(const Vec &point) {
671 const qreal prevDist = fabs(cameraCoordinatesOf(pivotPoint()).z);
672
673 // If frame's RAP is set directly, projectionMatrixIsUpToDate_ should also be
674 // set to false to ensure proper recomputation of the ORTHO projection matrix.
675 frame()->setPivotPoint(point);
676
677 // orthoCoef_ is used to compensate for changes of the pivotPoint, so that the
678 // image does not change when the pivotPoint is changed in ORTHOGRAPHIC mode.
679 const qreal newDist = fabs(cameraCoordinatesOf(pivotPoint()).z);
680 // Prevents division by zero when rap is set to camera position
681 if ((prevDist > 1E-9) && (newDist > 1E-9))
682 orthoCoef_ *= prevDist / newDist;
683 projectionMatrixIsUpToDate_ = false;
684 }
685
686 /*! The pivotPoint() is set to the point located under \p pixel on screen.
687
688 Returns \c true if a pointUnderPixel() was found. If no point was found under \p
689 pixel, the pivotPoint() is left unchanged.
690
691 \p pixel is expressed in Qt format (origin in the upper left corner of the
692 window). See pointUnderPixel().
693
694 See also setSceneCenterFromPixel(). */
695 CGAL_INLINE_FUNCTION
setPivotPointFromPixel(const QPoint & pixel)696 bool Camera::setPivotPointFromPixel(const QPoint &pixel) {
697 bool found;
698 Vec point = pointUnderPixel(pixel, found);
699 if (found)
700 setPivotPoint(point);
701 return found;
702 }
703
704 /*! Returns the ratio between pixel and OpenGL units at \p position.
705
706 A line of \c n * pixelGLRatio() OpenGL units, located at \p position in the
707 world coordinates system, will be projected with a length of \c n pixels on
708 screen.
709
710 Use this method to scale objects so that they have a constant pixel size on
711 screen. The following code will draw a 20 pixel line, starting at sceneCenter()
712 and always directed along the screen vertical direction: \code
713 glBegin(GL_LINES);
714 glVertex3fv(sceneCenter());
715 glVertex3fv(sceneCenter() + 20 * pixelGLRatio(sceneCenter()) *
716 camera()->upVector()); glEnd(); \endcode */
717 CGAL_INLINE_FUNCTION
pixelGLRatio(const Vec & position)718 qreal Camera::pixelGLRatio(const Vec &position) const {
719 switch (type()) {
720 case Camera::PERSPECTIVE:
721 return 2.0 * fabs((frame()->coordinatesOf(position)).z) *
722 tan(fieldOfView() / 2.0) / screenHeight();
723 case Camera::ORTHOGRAPHIC: {
724 GLdouble w, h;
725 getOrthoWidthHeight(w, h);
726 return 2.0 * h / screenHeight();
727 }
728 }
729 // Bad compilers complain
730 return 1.0;
731 }
732
733 /*! Changes the Camera fieldOfView() so that the entire scene (defined by
734 CGAL::QGLViewer::sceneCenter() and CGAL::QGLViewer::sceneRadius()) is visible from the
735 Camera position().
736
737 The position() and orientation() of the Camera are not modified and you first
738 have to orientate the Camera in order to actually see the scene (see lookAt(),
739 showEntireScene() or fitSphere()).
740
741 This method is especially useful for \e shadow \e maps computation. Use the
742 Camera positioning tools (setPosition(), lookAt()) to position a Camera at the
743 light position. Then use this method to define the fieldOfView() so that the
744 shadow map resolution is optimally used: \code
745 // The light camera needs size hints in order to optimize its fieldOfView
746 lightCamera->setSceneRadius(sceneRadius());
747 lightCamera->setSceneCenter(sceneCenter());
748
749 // Place the light camera.
750 lightCamera->setPosition(lightFrame->position());
751 lightCamera->lookAt(sceneCenter());
752 lightCamera->setFOVToFitScene();
753 \endcode
754
755 See the (soon available) shadowMap contribution example for a practical
756 implementation.
757
758 \attention The fieldOfView() is clamped to CGAL_PI/2.0. This happens when the
759 Camera is at a distance lower than sqrt(2.0) * sceneRadius() from the
760 sceneCenter(). It optimizes the shadow map resolution, although it may miss
761 some parts of the scene. */
762 CGAL_INLINE_FUNCTION
setFOVToFitScene()763 void Camera::setFOVToFitScene() {
764 if (distanceToSceneCenter() > sqrt(2.0) * sceneRadius())
765 setFieldOfView(2.0 * asin(sceneRadius() / distanceToSceneCenter()));
766 else
767 setFieldOfView(CGAL_PI / 2.0);
768 }
769
770 /*! Makes the Camera smoothly zoom on the pointUnderPixel() \p pixel.
771
772 Nothing happens if no pointUnderPixel() is found. Otherwise a
773 KeyFrameInterpolator is created that animates the Camera on a one second path
774 that brings the Camera closer to the point under \p pixel.
775
776 See also interpolateToFitScene(). */
777 CGAL_INLINE_FUNCTION
interpolateToZoomOnPixel(const QPoint & pixel)778 void Camera::interpolateToZoomOnPixel(const QPoint &pixel) {
779 const qreal coef = 0.1;
780
781 bool found;
782 Vec target = pointUnderPixel(pixel, found);
783
784 if (!found)
785 return;
786
787 if (interpolationKfi_->interpolationIsStarted())
788 interpolationKfi_->stopInterpolation();
789
790 interpolationKfi_->deletePath();
791 interpolationKfi_->addKeyFrame(*(frame()));
792
793 interpolationKfi_->addKeyFrame(
794 Frame(0.3 * frame()->position() + 0.7 * target, frame()->orientation()),
795 0.4);
796
797 // Small hack: attach a temporary frame to take advantage of lookAt without
798 // modifying frame
799 static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame();
800 ManipulatedCameraFrame *const originalFrame = frame();
801 tempFrame->setPosition(coef * frame()->position() + (1.0 - coef) * target);
802 tempFrame->setOrientation(frame()->orientation());
803 setFrame(tempFrame);
804 lookAt(target);
805 setFrame(originalFrame);
806
807 interpolationKfi_->addKeyFrame(*(tempFrame), 1.0);
808
809 interpolationKfi_->startInterpolation();
810 }
811
812 /*! Interpolates the Camera on a one second KeyFrameInterpolator path so that
813 the entire scene fits the screen at the end.
814
815 The scene is defined by its sceneCenter() and its sceneRadius(). See
816 showEntireScene().
817
818 The orientation() of the Camera is not modified. See also
819 interpolateToZoomOnPixel(). */
820 CGAL_INLINE_FUNCTION
interpolateToFitScene()821 void Camera::interpolateToFitScene() {
822 if (interpolationKfi_->interpolationIsStarted())
823 interpolationKfi_->stopInterpolation();
824
825 interpolationKfi_->deletePath();
826 interpolationKfi_->addKeyFrame(*(frame()));
827
828 // Small hack: attach a temporary frame to take advantage of lookAt without
829 // modifying frame
830 static ManipulatedCameraFrame *tempFrame = new ManipulatedCameraFrame();
831 ManipulatedCameraFrame *const originalFrame = frame();
832 tempFrame->setPosition(frame()->position());
833 tempFrame->setOrientation(frame()->orientation());
834 setFrame(tempFrame);
835 showEntireScene();
836 setFrame(originalFrame);
837
838 interpolationKfi_->addKeyFrame(*(tempFrame));
839
840 interpolationKfi_->startInterpolation();
841 }
842
843 /*! Smoothly interpolates the Camera on a KeyFrameInterpolator path so that it
844 goes to \p fr.
845
846 \p fr is expressed in world coordinates. \p duration tunes the interpolation
847 speed (default is 1 second).
848
849 See also interpolateToFitScene() and interpolateToZoomOnPixel(). */
850 CGAL_INLINE_FUNCTION
interpolateTo(const Frame & fr,qreal duration)851 void Camera::interpolateTo(const Frame &fr, qreal duration) {
852 if (interpolationKfi_->interpolationIsStarted())
853 interpolationKfi_->stopInterpolation();
854
855 interpolationKfi_->deletePath();
856 interpolationKfi_->addKeyFrame(*(frame()));
857 interpolationKfi_->addKeyFrame(fr, duration);
858
859 interpolationKfi_->startInterpolation();
860 }
861
862 /*! Returns the coordinates of the 3D point located at pixel (x,y) on screen.
863
864 Calls a \c glReadPixel to get the pixel depth and applies an
865 unprojectedCoordinatesOf() to the result. \p found indicates whether a point
866 was found or not (i.e. background pixel, result's depth is zFar() in that
867 case).
868
869 \p x and \p y are expressed in pixel units with an origin in the upper left
870 corner. Use screenHeight() - y to convert to OpenGL standard.
871
872 \attention This method assumes that a GL context is available, and that its
873 content was drawn using the Camera (i.e. using its projection and modelview
874 matrices). This method hence cannot be used for offscreen Camera computations.
875 Use cameraCoordinatesOf() and worldCoordinatesOf() to perform similar
876 operations in that case.
877
878 \note The precision of the z-Buffer highly depends on how the zNear() and
879 zFar() values are fitted to your scene. Loose boundaries will result in
880 imprecision along the viewing direction. */
881 CGAL_INLINE_FUNCTION
pointUnderPixel(const QPoint & pixel,bool & found)882 Vec Camera::pointUnderPixel(const QPoint &pixel, bool &found) const {
883 float depth = 2.0;
884 // Qt uses upper corner for its origin while GL uses the lower corner.
885 if(auto p = dynamic_cast<QOpenGLFunctions*>(parent()))
886 {
887 p->glReadPixels(pixel.x(), screenHeight() - 1 - pixel.y(), 1, 1,
888 GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
889 }
890 found = depth < 1.0;
891 Vec point(pixel.x(), pixel.y(), depth);
892 point = unprojectedCoordinatesOf(point);
893 return point;
894 }
895
896 /*! Moves the Camera so that the entire scene is visible.
897
898 Calls fitSphere() on a sphere defined by sceneCenter() and
899 sceneRadius(), and resets the default FOV.
900
901 You will typically use this method in CGAL::QGLViewer::init() after you defined a new
902 sceneRadius(). */
903 CGAL_INLINE_FUNCTION
showEntireScene()904 void Camera::showEntireScene()
905 {
906 setFieldOfView(CGAL_PI/4.0);
907 fitSphere(sceneCenter(), sceneRadius());
908 }
909
910 /*! Moves the Camera so that its sceneCenter() is projected on the center of the
911 window. The orientation() and fieldOfView() are unchanged.
912
913 Simply projects the current position on a line passing through sceneCenter().
914 See also showEntireScene().*/
915 CGAL_INLINE_FUNCTION
centerScene()916 void Camera::centerScene() {
917 frame()->projectOnLine(sceneCenter(), viewDirection());
918 }
919
920 /*! Sets the Camera orientation(), so that it looks at point \p target (defined
921 in the world coordinate system).
922
923 The Camera position() is not modified. Simply setViewDirection().
924
925 See also setUpVector(), setOrientation(), showEntireScene(), fitSphere() and
926 fitBoundingBox(). */
927 CGAL_INLINE_FUNCTION
lookAt(const Vec & target)928 void Camera::lookAt(const Vec &target) {
929 setViewDirection(target - position());
930 }
931
932 /*! Moves the Camera so that the sphere defined by (\p center, \p radius) is
933 visible and fits in the frustum.
934
935 The Camera is simply translated to center the sphere in the screen and make it
936 fit the frustum. Its orientation() and its fieldOfView() are unchanged.
937
938 You should therefore orientate the Camera before you call this method. See
939 lookAt(), setOrientation() and setUpVector(). */
940 CGAL_INLINE_FUNCTION
fitSphere(const Vec & center,qreal radius)941 void Camera::fitSphere(const Vec ¢er, qreal radius) {
942 qreal distance = 0.0;
943 switch (type()) {
944 case Camera::PERSPECTIVE: {
945 const qreal yview = radius / sin(fieldOfView() / 2.0);
946 const qreal xview = radius / sin(horizontalFieldOfView() / 2.0);
947 distance = qMax(xview, yview);
948 break;
949 }
950 case Camera::ORTHOGRAPHIC: {
951 distance =
952 ((center - pivotPoint()) * viewDirection()) + (radius / orthoCoef_);
953 break;
954 }
955 }
956 Vec newPos(center - distance * viewDirection());
957 frame()->setPositionWithConstraint(newPos);
958 }
959
960 /*! Moves the Camera so that the (world axis aligned) bounding box (\p min, \p
961 max) is entirely visible, using fitSphere(). */
962 CGAL_INLINE_FUNCTION
fitBoundingBox(const Vec & min,const Vec & max)963 void Camera::fitBoundingBox(const Vec &min, const Vec &max) {
964 qreal diameter = qMax(fabs(max[1] - min[1]), fabs(max[0] - min[0]));
965 diameter = qMax(fabs(max[2] - min[2]), diameter);
966 fitSphere(0.5 * (min + max), 0.5 * diameter);
967 }
968
969 /*! Moves the Camera so that the rectangular screen region defined by \p
970 rectangle (pixel units, with origin in the upper left corner) fits the screen.
971
972 The Camera is translated (its orientation() is unchanged) so that \p rectangle
973 is entirely visible. Since the pixel coordinates only define a \e frustum in
974 3D, it's the intersection of this frustum with a plane (orthogonal to the
975 viewDirection() and passing through the sceneCenter()) that is used to define
976 the 3D rectangle that is eventually fitted. */
977 CGAL_INLINE_FUNCTION
fitScreenRegion(const QRect & rectangle)978 void Camera::fitScreenRegion(const QRect &rectangle) {
979 const Vec vd = viewDirection();
980 const qreal distToPlane = distanceToSceneCenter();
981 const QPoint center = rectangle.center();
982
983 Vec orig, dir;
984 convertClickToLine(center, orig, dir);
985 Vec newCenter = orig + distToPlane / (dir * vd) * dir;
986
987 convertClickToLine(QPoint(rectangle.x(), center.y()), orig, dir);
988 const Vec pointX = orig + distToPlane / (dir * vd) * dir;
989
990 convertClickToLine(QPoint(center.x(), rectangle.y()), orig, dir);
991 const Vec pointY = orig + distToPlane / (dir * vd) * dir;
992
993 qreal distance = 0.0;
994 switch (type()) {
995 case Camera::PERSPECTIVE: {
996 const qreal distX =
997 (pointX - newCenter).norm() / sin(horizontalFieldOfView() / 2.0);
998 const qreal distY = (pointY - newCenter).norm() / sin(fieldOfView() / 2.0);
999 distance = qMax(distX, distY);
1000 break;
1001 }
1002 case Camera::ORTHOGRAPHIC: {
1003 const qreal dist = ((newCenter - pivotPoint()) * vd);
1004 //#CONNECTION# getOrthoWidthHeight
1005 const qreal distX = (pointX - newCenter).norm() / orthoCoef_ /
1006 ((aspectRatio() < 1.0) ? 1.0 : aspectRatio());
1007 const qreal distY = (pointY - newCenter).norm() / orthoCoef_ /
1008 ((aspectRatio() < 1.0) ? 1.0 / aspectRatio() : 1.0);
1009 distance = dist + qMax(distX, distY);
1010 break;
1011 }
1012 }
1013
1014 Vec newPos(newCenter - distance * vd);
1015 frame()->setPositionWithConstraint(newPos);
1016 }
1017
1018 /*! Rotates the Camera so that its upVector() becomes \p up (defined in the
1019 world coordinate system).
1020
1021 The Camera is rotated around an axis orthogonal to \p up and to the current
1022 upVector() direction. Use this method in order to define the Camera horizontal
1023 plane.
1024
1025 When \p noMove is set to \c false, the orientation modification is compensated
1026 by a translation, so that the pivotPoint() stays projected at the same position
1027 on screen. This is especially useful when the Camera is used as an observer of
1028 the scene (default mouse binding).
1029
1030 When \p noMove is \c true (default), the Camera position() is left unchanged,
1031 which is an intuitive behavior when the Camera is in a walkthrough fly mode
1032 (see the CGAL::QGLViewer::MOVE_FORWARD and CGAL::QGLViewer::MOVE_BACKWARD
1033 CGAL::QGLViewer::MouseAction).
1034
1035 The frame()'s ManipulatedCameraFrame::sceneUpVector() is set accordingly.
1036
1037 See also setViewDirection(), lookAt() and setOrientation(). */
1038 CGAL_INLINE_FUNCTION
setUpVector(const Vec & up,bool noMove)1039 void Camera::setUpVector(const Vec &up, bool noMove) {
1040 Quaternion q(Vec(0.0, 1.0, 0.0), frame()->transformOf(up));
1041
1042 if (!noMove)
1043 frame()->setPosition(pivotPoint() -
1044 (frame()->orientation() * q)
1045 .rotate(frame()->coordinatesOf(pivotPoint())));
1046
1047 frame()->rotate(q);
1048
1049 // Useful in fly mode to keep the horizontal direction.
1050 frame()->updateSceneUpVector();
1051 }
1052
1053 /*! Sets the orientation() of the Camera using polar coordinates.
1054
1055 \p theta rotates the Camera around its Y axis, and \e then \p phi rotates it
1056 around its X axis. The polar coordinates are defined in the world coordinates
1057 system: \p theta = \p phi = 0 means that the Camera is directed towards the
1058 world Z axis. Both angles are expressed in radians.
1059
1060 See also setUpVector(). The position() of the Camera is unchanged, you may want
1061 to call showEntireScene() after this method to move the Camera.
1062
1063 This method can be useful to create Quicktime VR panoramic sequences, see the
1064 CGAL::QGLViewer::saveSnapshot() documentation for details. */
1065 CGAL_INLINE_FUNCTION
setOrientation(qreal theta,qreal phi)1066 void Camera::setOrientation(qreal theta, qreal phi) {
1067 Vec axis(0.0, 1.0, 0.0);
1068 const Quaternion rot1(axis, theta);
1069 axis = Vec(-cos(theta), 0.0, sin(theta));
1070 const Quaternion rot2(axis, phi);
1071 setOrientation(rot1 * rot2);
1072 }
1073
1074 /*! Sets the Camera orientation(), defined in the world coordinate system. */
1075 CGAL_INLINE_FUNCTION
setOrientation(const Quaternion & q)1076 void Camera::setOrientation(const Quaternion &q) {
1077 frame()->setOrientation(q);
1078 frame()->updateSceneUpVector();
1079 }
1080
1081 /*! Rotates the Camera so that its viewDirection() is \p direction (defined in
1082 the world coordinate system).
1083
1084 The Camera position() is not modified. The Camera is rotated so that the
1085 horizon (defined by its upVector()) is preserved. See also lookAt() and
1086 setUpVector(). */
1087 CGAL_INLINE_FUNCTION
setViewDirection(const Vec & direction)1088 void Camera::setViewDirection(const Vec &direction) {
1089 if (direction.squaredNorm() < 1E-10)
1090 return;
1091
1092 Vec xAxis = direction ^ upVector();
1093 if (xAxis.squaredNorm() < 1E-10) {
1094 // target is aligned with upVector, this means a rotation around X axis
1095 // X axis is then unchanged, let's keep it !
1096 xAxis = frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0));
1097 }
1098
1099 Quaternion q;
1100 q.setFromRotatedBasis(xAxis, xAxis ^ direction, -direction);
1101 frame()->setOrientationWithConstraint(q);
1102 }
1103
1104 // Compute a 3 by 3 determinant.
det(qreal m00,qreal m01,qreal m02,qreal m10,qreal m11,qreal m12,qreal m20,qreal m21,qreal m22)1105 static qreal det(qreal m00, qreal m01, qreal m02, qreal m10, qreal m11,
1106 qreal m12, qreal m20, qreal m21, qreal m22) {
1107 return m00 * m11 * m22 + m01 * m12 * m20 + m02 * m10 * m21 - m20 * m11 * m02 -
1108 m10 * m01 * m22 - m00 * m21 * m12;
1109 }
1110
1111 // Computes the index of element [i][j] in a \c qreal matrix[3][4].
ind(unsigned int i,unsigned int j)1112 static inline unsigned int ind(unsigned int i, unsigned int j) {
1113 return (i * 4 + j);
1114 }
1115
1116 /*! Returns the Camera position (the eye), defined in the world coordinate
1117 system.
1118
1119 Use setPosition() to set the Camera position. Other convenient methods are
1120 showEntireScene() or fitSphere(). Actually returns \c frame()->position().
1121
1122 This position corresponds to the projection center of a Camera::PERSPECTIVE
1123 Camera. It is not located in the image plane, which is at a zNear() distance
1124 ahead. */
1125 CGAL_INLINE_FUNCTION
position()1126 Vec Camera::position() const { return frame()->position(); }
1127
1128 /*! Returns the normalized up vector of the Camera, defined in the world
1129 coordinate system.
1130
1131 Set using setUpVector() or setOrientation(). It is orthogonal to viewDirection()
1132 and to rightVector().
1133
1134 It corresponds to the Y axis of the associated frame() (actually returns
1135 frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0)) ). */
1136 CGAL_INLINE_FUNCTION
upVector()1137 Vec Camera::upVector() const {
1138 return frame()->inverseTransformOf(Vec(0.0, 1.0, 0.0));
1139 }
1140 /*! Returns the normalized view direction of the Camera, defined in the world
1141 coordinate system.
1142
1143 Change this value using setViewDirection(), lookAt() or setOrientation(). It is
1144 orthogonal to upVector() and to rightVector().
1145
1146 This corresponds to the negative Z axis of the frame() (
1147 frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)) ). */
1148 CGAL_INLINE_FUNCTION
viewDirection()1149 Vec Camera::viewDirection() const {
1150 return frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0));
1151 }
1152
1153 /*! Returns the normalized right vector of the Camera, defined in the world
1154 coordinate system.
1155
1156 This vector lies in the Camera horizontal plane, directed along the X axis
1157 (orthogonal to upVector() and to viewDirection()). Set using setUpVector(),
1158 lookAt() or setOrientation().
1159
1160 Simply returns frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0)). */
1161 CGAL_INLINE_FUNCTION
rightVector()1162 Vec Camera::rightVector() const {
1163 return frame()->inverseTransformOf(Vec(1.0, 0.0, 0.0));
1164 }
1165
1166 /*! Returns the Camera orientation, defined in the world coordinate system.
1167
1168 Actually returns \c frame()->orientation(). Use setOrientation(), setUpVector()
1169 or lookAt() to set the Camera orientation. */
1170 CGAL_INLINE_FUNCTION
orientation()1171 Quaternion Camera::orientation() const { return frame()->orientation(); }
1172
1173 /*! Sets the Camera position() (the eye), defined in the world coordinate
1174 * system. */
1175 CGAL_INLINE_FUNCTION
setPosition(const Vec & pos)1176 void Camera::setPosition(const Vec &pos) { frame()->setPosition(pos); }
1177
1178 /*! Returns the Camera frame coordinates of a point \p src defined in world
1179 coordinates.
1180
1181 worldCoordinatesOf() performs the inverse transformation.
1182
1183 Note that the point coordinates are simply converted in a different coordinate
1184 system. They are not projected on screen. Use projectedCoordinatesOf() for that.
1185 */
1186 CGAL_INLINE_FUNCTION
cameraCoordinatesOf(const Vec & src)1187 Vec Camera::cameraCoordinatesOf(const Vec &src) const {
1188 return frame()->coordinatesOf(src);
1189 }
1190
1191 /*! Returns the world coordinates of the point whose position \p src is defined
1192 in the Camera coordinate system.
1193
1194 cameraCoordinatesOf() performs the inverse transformation. */
1195 CGAL_INLINE_FUNCTION
worldCoordinatesOf(const Vec & src)1196 Vec Camera::worldCoordinatesOf(const Vec &src) const {
1197 return frame()->inverseCoordinatesOf(src);
1198 }
1199
1200 /*! Returns the fly speed of the Camera.
1201
1202 Simply returns frame()->flySpeed(). See the ManipulatedCameraFrame::flySpeed()
1203 documentation. This value is only meaningful when the MouseAction bindings is
1204 CGAL::QGLViewer::MOVE_FORWARD or CGAL::QGLViewer::MOVE_BACKWARD.
1205
1206 Set to 1% of the sceneRadius() by setSceneRadius(). See also setFlySpeed(). */
1207 CGAL_INLINE_FUNCTION
flySpeed()1208 qreal Camera::flySpeed() const { return frame()->flySpeed(); }
1209
1210 /*! Sets the Camera flySpeed().
1211
1212 \attention This value is modified by setSceneRadius(). */
1213 CGAL_INLINE_FUNCTION
setFlySpeed(qreal speed)1214 void Camera::setFlySpeed(qreal speed) { frame()->setFlySpeed(speed); }
1215
1216 /*! The point the Camera pivots around with the CGAL::QGLViewer::ROTATE mouse binding.
1217 Defined in world coordinate system.
1218
1219 Default value is the sceneCenter().
1220
1221 \attention setSceneCenter() changes this value. */
1222 CGAL_INLINE_FUNCTION
pivotPoint()1223 Vec Camera::pivotPoint() const { return frame()->pivotPoint(); }
1224
1225 /*! Sets the Camera's position() and orientation() from an OpenGL ModelView
1226 matrix.
1227
1228 This enables a Camera initialisation from an other OpenGL application. \p
1229 modelView is a 16 GLdouble vector representing a valid OpenGL ModelView matrix,
1230 such as one can get using: \code GLdouble mvm[16];
1231 glGetDoublev(GL_MODELVIEW_MATRIX, mvm);
1232 myCamera->setFromModelViewMatrix(mvm);
1233 \endcode
1234
1235 After this method has been called, getModelViewMatrix() returns a matrix
1236 equivalent to \p modelView.
1237
1238 Only the orientation() and position() of the Camera are modified.
1239
1240 \note If you defined your matrix as \c GLdouble \c mvm[4][4], pass \c
1241 &(mvm[0][0]) as a parameter. */
1242 CGAL_INLINE_FUNCTION
setFromModelViewMatrix(const GLdouble * const modelViewMatrix)1243 void Camera::setFromModelViewMatrix(const GLdouble *const modelViewMatrix) {
1244 // Get upper left (rotation) matrix
1245 qreal upperLeft[3][3];
1246 for (int i = 0; i < 3; ++i)
1247 for (int j = 0; j < 3; ++j)
1248 upperLeft[i][j] = modelViewMatrix[i * 4 + j];
1249
1250 // Transform upperLeft into the associated Quaternion
1251 Quaternion q;
1252 q.setFromRotationMatrix(upperLeft);
1253
1254 setOrientation(q);
1255 setPosition(-q.rotate(
1256 Vec(modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14])));
1257 }
1258
1259 /*! Defines the Camera position(), orientation() and fieldOfView() from a
1260 projection matrix.
1261
1262 \p matrix has to be given in the format used by vision algorithm. It has 3
1263 lines and 4 columns. It transforms a point from the world homogeneous
1264 coordinate system (4 coordinates: \c sx, \c sy, \c sz and \c s) into a point in
1265 the screen homogeneous coordinate system (3 coordinates: \c sx, \c sy, and \c
1266 s, where \c x and \c y are the pixel coordinates on the screen).
1267
1268 Its three lines correspond to the homogeneous coordinates of the normals to the
1269 planes x=0, y=0 and z=0, defined in the Camera coordinate system.
1270
1271 The elements of the matrix are ordered in line major order: you can call \c
1272 setFromProjectionMatrix(&(matrix[0][0])) if you defined your matrix as a \c
1273 qreal \c matrix[3][4].
1274
1275 \attention Passing the result of getProjectionMatrix() or getModelViewMatrix()
1276 to this method is not possible (purposefully incompatible matrix dimensions).
1277 \p matrix is more likely to be the product of these two matrices, without the
1278 last line.
1279
1280 Use setFromModelViewMatrix() to set position() and orientation() from a \c
1281 GL_MODELVIEW matrix. fieldOfView() can also be retrieved from a \e perspective
1282 \c GL_PROJECTION matrix using 2.0 * atan(1.0/projectionMatrix[5]).
1283
1284 This code was written by Sylvain Paris. */
1285 CGAL_INLINE_FUNCTION
setFromProjectionMatrix(const qreal matrix[12])1286 void Camera::setFromProjectionMatrix(const qreal matrix[12]) {
1287 // The 3 lines of the matrix are the normals to the planes x=0, y=0, z=0
1288 // in the camera CS. As we normalize them, we do not need the 4th coordinate.
1289 Vec line_0(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)]);
1290 Vec line_1(matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)]);
1291 Vec line_2(matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)]);
1292
1293 line_0.normalize();
1294 line_1.normalize();
1295 line_2.normalize();
1296
1297 // The camera position is at (0,0,0) in the camera CS so it is the
1298 // intersection of the 3 planes. It can be seen as the kernel
1299 // of the 3x4 projection matrix. We calculate it through 4 dimensional
1300 // vectorial product. We go directly into 3D that is to say we directly
1301 // divide the first 3 coordinates by the 4th one.
1302
1303 // We derive the 4 dimensional vectorial product formula from the
1304 // computation of a 4x4 determinant that is developped according to
1305 // its 4th column. This implies some 3x3 determinants.
1306 const Vec cam_pos =
1307 Vec(det(matrix[ind(0, 1)], matrix[ind(0, 2)], matrix[ind(0, 3)],
1308 matrix[ind(1, 1)], matrix[ind(1, 2)], matrix[ind(1, 3)],
1309 matrix[ind(2, 1)], matrix[ind(2, 2)], matrix[ind(2, 3)]),
1310
1311 -det(matrix[ind(0, 0)], matrix[ind(0, 2)], matrix[ind(0, 3)],
1312 matrix[ind(1, 0)], matrix[ind(1, 2)], matrix[ind(1, 3)],
1313 matrix[ind(2, 0)], matrix[ind(2, 2)], matrix[ind(2, 3)]),
1314
1315 det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 3)],
1316 matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 3)],
1317 matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 3)])) /
1318
1319 (-det(matrix[ind(0, 0)], matrix[ind(0, 1)], matrix[ind(0, 2)],
1320 matrix[ind(1, 0)], matrix[ind(1, 1)], matrix[ind(1, 2)],
1321 matrix[ind(2, 0)], matrix[ind(2, 1)], matrix[ind(2, 2)]));
1322
1323 // We compute the rotation matrix column by column.
1324
1325 // GL Z axis is front facing.
1326 Vec column_2 = -line_2;
1327
1328 // X-axis is almost like line_0 but should be orthogonal to the Z axis.
1329 Vec column_0 = ((column_2 ^ line_0) ^ column_2);
1330 column_0.normalize();
1331
1332 // Y-axis is almost like line_1 but should be orthogonal to the Z axis.
1333 // Moreover line_1 is downward oriented as the screen CS.
1334 Vec column_1 = -((column_2 ^ line_1) ^ column_2);
1335 column_1.normalize();
1336
1337 qreal rot[3][3];
1338 rot[0][0] = column_0[0];
1339 rot[1][0] = column_0[1];
1340 rot[2][0] = column_0[2];
1341
1342 rot[0][1] = column_1[0];
1343 rot[1][1] = column_1[1];
1344 rot[2][1] = column_1[2];
1345
1346 rot[0][2] = column_2[0];
1347 rot[1][2] = column_2[1];
1348 rot[2][2] = column_2[2];
1349
1350 // We compute the field of view
1351
1352 // line_1^column_0 -> vector of intersection line between
1353 // y_screen=0 and x_camera=0 plane.
1354 // column_2*(...) -> cos of the angle between Z vector et y_screen=0 plane
1355 // * 2 -> field of view = 2 * half angle
1356
1357 // We need some intermediate values.
1358 Vec dummy = line_1 ^ column_0;
1359 dummy.normalize();
1360 qreal fov = acos(column_2 * dummy) * 2.0;
1361
1362 // We set the camera.
1363 Quaternion q;
1364 q.setFromRotationMatrix(rot);
1365 setOrientation(q);
1366 setPosition(cam_pos);
1367 setFieldOfView(fov);
1368 }
1369
1370 /*
1371 // persp : projectionMatrix_[0] = f/aspectRatio();
1372 CGAL_INLINE_FUNCTION
1373 void Camera::setFromProjectionMatrix(const GLdouble* projectionMatrix)
1374 {
1375 QString message;
1376 if ((fabs(projectionMatrix[1]) > 1E-3) ||
1377 (fabs(projectionMatrix[2]) > 1E-3) ||
1378 (fabs(projectionMatrix[3]) > 1E-3) ||
1379 (fabs(projectionMatrix[4]) > 1E-3) ||
1380 (fabs(projectionMatrix[6]) > 1E-3) ||
1381 (fabs(projectionMatrix[7]) > 1E-3) ||
1382 (fabs(projectionMatrix[8]) > 1E-3) ||
1383 (fabs(projectionMatrix[9]) > 1E-3))
1384 message = "Non null coefficient in projection matrix - Aborting";
1385 else
1386 if ((fabs(projectionMatrix[11]+1.0) < 1E-5) && (fabs(projectionMatrix[15]) <
1387 1E-5))
1388 {
1389 if (projectionMatrix[5] < 1E-4)
1390 message="Negative field of view in Camera::setFromProjectionMatrix";
1391 else
1392 setType(Camera::PERSPECTIVE);
1393 }
1394 else
1395 if ((fabs(projectionMatrix[11]) < 1E-5) && (fabs(projectionMatrix[15]-1.0) <
1396 1E-5)) setType(Camera::ORTHOGRAPHIC); else message = "Unable to determine camera
1397 type in setFromProjectionMatrix - Aborting";
1398
1399 if (!message.isEmpty())
1400 {
1401 qWarning(message);
1402 return;
1403 }
1404
1405 switch (type())
1406 {
1407 case Camera::PERSPECTIVE:
1408 {
1409 setFieldOfView(2.0 * atan(1.0/projectionMatrix[5]));
1410 const qreal far = projectionMatrix[14] / (2.0 * (1.0 + projectionMatrix[10]));
1411 const qreal near = (projectionMatrix[10]+1.0) / (projectionMatrix[10]-1.0) *
1412 far; setSceneRadius((far-near)/2.0); setSceneCenter(position() + (near +
1413 sceneRadius())*viewDirection()); break;
1414 }
1415 case Camera::ORTHOGRAPHIC:
1416 {
1417 GLdouble w, h;
1418 getOrthoWidthHeight(w,h);
1419 projectionMatrix_[0] = 1.0/w;
1420 projectionMatrix_[5] = 1.0/h;
1421 projectionMatrix_[10] = -2.0/(ZFar - ZNear);
1422 projectionMatrix_[11] = 0.0;
1423 projectionMatrix_[14] = -(ZFar + ZNear)/(ZFar - ZNear);
1424 projectionMatrix_[15] = 1.0;
1425 // same as glOrtho( -w, w, -h, h, zNear(), zFar() );
1426 break;
1427 }
1428 }
1429 }
1430 */
1431
1432 ///////////////////////// Camera to world transform ///////////////////////
1433
1434 /*! Same as cameraCoordinatesOf(), but with \c qreal[3] parameters (\p src and
1435 * \p res may be identical pointers). */
1436 CGAL_INLINE_FUNCTION
getCameraCoordinatesOf(const qreal src[3],qreal res[3])1437 void Camera::getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const {
1438 Vec r = cameraCoordinatesOf(Vec(src));
1439 for (int i = 0; i < 3; ++i)
1440 res[i] = r[i];
1441 }
1442
1443 /*! Same as worldCoordinatesOf(), but with \c qreal[3] parameters (\p src and \p
1444 * res may be identical pointers). */
1445 CGAL_INLINE_FUNCTION
getWorldCoordinatesOf(const qreal src[3],qreal res[3])1446 void Camera::getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const {
1447 Vec r = worldCoordinatesOf(Vec(src));
1448 for (int i = 0; i < 3; ++i)
1449 res[i] = r[i];
1450 }
1451
1452 /*! Fills \p viewport with the Camera OpenGL viewport.
1453
1454 This method is mainly used in conjunction with \c gluProject, which requires
1455 such a viewport. Returned values are (0, screenHeight(), screenWidth(), -
1456 screenHeight()), so that the origin is located in the \e upper left corner of
1457 the window (Qt style coordinate system). */
1458 CGAL_INLINE_FUNCTION
getViewport(GLint viewport[4])1459 void Camera::getViewport(GLint viewport[4]) const {
1460 viewport[0] = 0;
1461 viewport[1] = screenHeight();
1462 viewport[2] = screenWidth();
1463 viewport[3] = -screenHeight();
1464 }
1465
1466 //source code of GluProject and GluUnproject, imported here to avoid the dependency to Glu
1467 CGAL_INLINE_FUNCTION
project(qreal objx,qreal objy,qreal objz,GLdouble * modelview,GLdouble * projection,int * viewport,GLdouble * winX,GLdouble * winY,GLdouble * winZ)1468 int project(qreal objx, qreal objy, qreal objz, GLdouble *modelview,
1469 GLdouble *projection, int *viewport, GLdouble*winX, GLdouble *winY,GLdouble *winZ)
1470 {
1471 //Transformation vectors
1472 GLdouble fTempo[8];
1473 //Modelview transform
1474 fTempo[0]=modelview[0]*objx+modelview[4]*objy+modelview[8]*objz+modelview[12]; //w is always 1
1475 fTempo[1]=modelview[1]*objx+modelview[5]*objy+modelview[9]*objz+modelview[13];
1476 fTempo[2]=modelview[2]*objx+modelview[6]*objy+modelview[10]*objz+modelview[14];
1477 fTempo[3]=modelview[3]*objx+modelview[7]*objy+modelview[11]*objz+modelview[15];
1478 fTempo[4]=projection[0]*fTempo[0]+projection[4]*fTempo[1]+projection[8]*fTempo[2]+projection[12]*fTempo[3];
1479 fTempo[5]=projection[1]*fTempo[0]+projection[5]*fTempo[1]+projection[9]*fTempo[2]+projection[13]*fTempo[3];
1480 fTempo[6]=projection[2]*fTempo[0]+projection[6]*fTempo[1]+projection[10]*fTempo[2]+projection[14]*fTempo[3];
1481 fTempo[7]=projection[3]*fTempo[0]+projection[7]*fTempo[1]+projection[11]*fTempo[2]+projection[15]*fTempo[3];
1482 //The result normalizes between -1 and 1
1483 if(fTempo[7]==0.0) //The w value
1484 return 0;
1485 fTempo[7]=1.0/fTempo[7];
1486 //Perspective division
1487 fTempo[4]*=fTempo[7];
1488 fTempo[5]*=fTempo[7];
1489 fTempo[6]*=fTempo[7];
1490 //Window coordinates
1491 //Map x, y to range 0-1
1492 *winX=(fTempo[4]*0.5+0.5)*viewport[2]+viewport[0];
1493 *winY=(fTempo[5]*0.5+0.5)*viewport[3]+viewport[1];
1494 //This is only correct when glDepthRange(0.0, 1.0)
1495 *winZ=(1.0+fTempo[6])*0.5; //Between 0 and 1
1496 return 1;
1497 }
1498
1499 CGAL_INLINE_FUNCTION
MultiplyMatrices4by4OpenGL_GLdouble(GLdouble * result,GLdouble * matrix1,GLdouble * matrix2)1500 void MultiplyMatrices4by4OpenGL_GLdouble(GLdouble *result, GLdouble *matrix1, GLdouble *matrix2)
1501 {
1502 result[0]=matrix1[0]*matrix2[0]+
1503 matrix1[4]*matrix2[1]+
1504 matrix1[8]*matrix2[2]+
1505 matrix1[12]*matrix2[3];
1506 result[4]=matrix1[0]*matrix2[4]+
1507 matrix1[4]*matrix2[5]+
1508 matrix1[8]*matrix2[6]+
1509 matrix1[12]*matrix2[7];
1510 result[8]=matrix1[0]*matrix2[8]+
1511 matrix1[4]*matrix2[9]+
1512 matrix1[8]*matrix2[10]+
1513 matrix1[12]*matrix2[11];
1514 result[12]=matrix1[0]*matrix2[12]+
1515 matrix1[4]*matrix2[13]+
1516 matrix1[8]*matrix2[14]+
1517 matrix1[12]*matrix2[15];
1518 result[1]=matrix1[1]*matrix2[0]+
1519 matrix1[5]*matrix2[1]+
1520 matrix1[9]*matrix2[2]+
1521 matrix1[13]*matrix2[3];
1522 result[5]=matrix1[1]*matrix2[4]+
1523 matrix1[5]*matrix2[5]+
1524 matrix1[9]*matrix2[6]+
1525 matrix1[13]*matrix2[7];
1526 result[9]=matrix1[1]*matrix2[8]+
1527 matrix1[5]*matrix2[9]+
1528 matrix1[9]*matrix2[10]+
1529 matrix1[13]*matrix2[11];
1530 result[13]=matrix1[1]*matrix2[12]+
1531 matrix1[5]*matrix2[13]+
1532 matrix1[9]*matrix2[14]+
1533 matrix1[13]*matrix2[15];
1534 result[2]=matrix1[2]*matrix2[0]+
1535 matrix1[6]*matrix2[1]+
1536 matrix1[10]*matrix2[2]+
1537 matrix1[14]*matrix2[3];
1538 result[6]=matrix1[2]*matrix2[4]+
1539 matrix1[6]*matrix2[5]+
1540 matrix1[10]*matrix2[6]+
1541 matrix1[14]*matrix2[7];
1542 result[10]=matrix1[2]*matrix2[8]+
1543 matrix1[6]*matrix2[9]+
1544 matrix1[10]*matrix2[10]+
1545 matrix1[14]*matrix2[11];
1546 result[14]=matrix1[2]*matrix2[12]+
1547 matrix1[6]*matrix2[13]+
1548 matrix1[10]*matrix2[14]+
1549 matrix1[14]*matrix2[15];
1550 result[3]=matrix1[3]*matrix2[0]+
1551 matrix1[7]*matrix2[1]+
1552 matrix1[11]*matrix2[2]+
1553 matrix1[15]*matrix2[3];
1554 result[7]=matrix1[3]*matrix2[4]+
1555 matrix1[7]*matrix2[5]+
1556 matrix1[11]*matrix2[6]+
1557 matrix1[15]*matrix2[7];
1558 result[11]=matrix1[3]*matrix2[8]+
1559 matrix1[7]*matrix2[9]+
1560 matrix1[11]*matrix2[10]+
1561 matrix1[15]*matrix2[11];
1562 result[15]=matrix1[3]*matrix2[12]+
1563 matrix1[7]*matrix2[13]+
1564 matrix1[11]*matrix2[14]+
1565 matrix1[15]*matrix2[15];
1566 }
1567
1568 CGAL_INLINE_FUNCTION
MultiplyMatrixByVector4by4OpenGL_GLdouble(GLdouble * resultvector,const GLdouble * matrix,const GLdouble * pvector)1569 void MultiplyMatrixByVector4by4OpenGL_GLdouble(GLdouble *resultvector, const GLdouble *matrix, const GLdouble *pvector)
1570 {
1571 resultvector[0]=matrix[0]*pvector[0]+matrix[4]*pvector[1]+matrix[8]*pvector[2]+matrix[12]*pvector[3];
1572 resultvector[1]=matrix[1]*pvector[0]+matrix[5]*pvector[1]+matrix[9]*pvector[2]+matrix[13]*pvector[3];
1573 resultvector[2]=matrix[2]*pvector[0]+matrix[6]*pvector[1]+matrix[10]*pvector[2]+matrix[14]*pvector[3];
1574 resultvector[3]=matrix[3]*pvector[0]+matrix[7]*pvector[1]+matrix[11]*pvector[2]+matrix[15]*pvector[3];
1575 }
1576
1577 #define SWAP_ROWS_DOUBLE(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; }
1578 #define SWAP_ROWS_GLdouble(a, b) { GLdouble *_tmp = a; (a)=(b); (b)=_tmp; }
1579 #define MAT(m,r,c) (m)[(c)*4+(r)]
1580 //This code comes directly from GLU except that it is for GLdouble
1581 CGAL_INLINE_FUNCTION
glhInvertMatrixf2(GLdouble * m,GLdouble * out)1582 int glhInvertMatrixf2(GLdouble *m, GLdouble *out)
1583 {
1584 GLdouble wtmp[4][8];
1585 GLdouble m0, m1, m2, m3, s;
1586 GLdouble *r0, *r1, *r2, *r3;
1587 r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];
1588 r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1),
1589 r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3),
1590 r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,
1591 r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1),
1592 r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3),
1593 r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,
1594 r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1),
1595 r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3),
1596 r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,
1597 r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1),
1598 r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3),
1599 r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;
1600 /* choose pivot - or die */
1601 if (fabs(r3[0]) > fabs(r2[0]))
1602 SWAP_ROWS_GLdouble(r3, r2);
1603 if (fabs(r2[0]) > fabs(r1[0]))
1604 SWAP_ROWS_GLdouble(r2, r1);
1605 if (fabs(r1[0]) > fabs(r0[0]))
1606 SWAP_ROWS_GLdouble(r1, r0);
1607 if (0.0 == r0[0])
1608 return 0;
1609 /* eliminate first variable */
1610 m1 = r1[0] / r0[0];
1611 m2 = r2[0] / r0[0];
1612 m3 = r3[0] / r0[0];
1613 s = r0[1];
1614 r1[1] -= m1 * s;
1615 r2[1] -= m2 * s;
1616 r3[1] -= m3 * s;
1617 s = r0[2];
1618 r1[2] -= m1 * s;
1619 r2[2] -= m2 * s;
1620 r3[2] -= m3 * s;
1621 s = r0[3];
1622 r1[3] -= m1 * s;
1623 r2[3] -= m2 * s;
1624 r3[3] -= m3 * s;
1625 s = r0[4];
1626 if (s != 0.0) {
1627 r1[4] -= m1 * s;
1628 r2[4] -= m2 * s;
1629 r3[4] -= m3 * s;
1630 }
1631 s = r0[5];
1632 if (s != 0.0) {
1633 r1[5] -= m1 * s;
1634 r2[5] -= m2 * s;
1635 r3[5] -= m3 * s;
1636 }
1637 s = r0[6];
1638 if (s != 0.0) {
1639 r1[6] -= m1 * s;
1640 r2[6] -= m2 * s;
1641 r3[6] -= m3 * s;
1642 }
1643 s = r0[7];
1644 if (s != 0.0) {
1645 r1[7] -= m1 * s;
1646 r2[7] -= m2 * s;
1647 r3[7] -= m3 * s;
1648 }
1649 /* choose pivot - or die */
1650 if (fabs(r3[1]) > fabs(r2[1]))
1651 SWAP_ROWS_GLdouble(r3, r2);
1652 if (fabs(r2[1]) > fabs(r1[1]))
1653 SWAP_ROWS_GLdouble(r2, r1);
1654 if (0.0 == r1[1])
1655 return 0;
1656 /* eliminate second variable */
1657 m2 = r2[1] / r1[1];
1658 m3 = r3[1] / r1[1];
1659 r2[2] -= m2 * r1[2];
1660 r3[2] -= m3 * r1[2];
1661 r2[3] -= m2 * r1[3];
1662 r3[3] -= m3 * r1[3];
1663 s = r1[4];
1664 if (0.0 != s) {
1665 r2[4] -= m2 * s;
1666 r3[4] -= m3 * s;
1667 }
1668 s = r1[5];
1669 if (0.0 != s) {
1670 r2[5] -= m2 * s;
1671 r3[5] -= m3 * s;
1672 }
1673 s = r1[6];
1674 if (0.0 != s) {
1675 r2[6] -= m2 * s;
1676 r3[6] -= m3 * s;
1677 }
1678 s = r1[7];
1679 if (0.0 != s) {
1680 r2[7] -= m2 * s;
1681 r3[7] -= m3 * s;
1682 }
1683 /* choose pivot - or die */
1684 if (fabs(r3[2]) > fabs(r2[2]))
1685 SWAP_ROWS_GLdouble(r3, r2);
1686 if (0.0 == r2[2])
1687 return 0;
1688 /* eliminate third variable */
1689 m3 = r3[2] / r2[2];
1690 r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
1691 r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7];
1692 /* last check */
1693 if (0.0 == r3[3])
1694 return 0;
1695 s = 1.0 / r3[3]; /* now back substitute row 3 */
1696 r3[4] *= s;
1697 r3[5] *= s;
1698 r3[6] *= s;
1699 r3[7] *= s;
1700 m2 = r2[3]; /* now back substitute row 2 */
1701 s = 1.0 / r2[2];
1702 r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
1703 r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
1704 m1 = r1[3];
1705 r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
1706 r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
1707 m0 = r0[3];
1708 r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
1709 r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
1710 m1 = r1[2]; /* now back substitute row 1 */
1711 s = 1.0 / r1[1];
1712 r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
1713 r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
1714 m0 = r0[2];
1715 r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
1716 r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
1717 m0 = r0[1]; /* now back substitute row 0 */
1718 s = 1.0 / r0[0];
1719 r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
1720 r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
1721 MAT(out, 0, 0) = r0[4];
1722 MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6];
1723 MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4];
1724 MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6];
1725 MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4];
1726 MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6];
1727 MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4];
1728 MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6];
1729 MAT(out, 3, 3) = r3[7];
1730 return 1;
1731 }
1732
1733 #undef MAT
1734 #undef SWAP_ROWS_GLdouble
1735 #undef SWAP_ROWS_DOUBLE
1736
1737 CGAL_INLINE_FUNCTION
unProject(GLdouble winx,GLdouble winy,GLdouble winz,GLdouble * modelview,GLdouble * projection,int * viewport,GLdouble * objX,GLdouble * objY,GLdouble * objZ)1738 int unProject(GLdouble winx, GLdouble winy, GLdouble winz, GLdouble *modelview, GLdouble *projection, int *viewport,
1739 GLdouble *objX,GLdouble *objY,GLdouble *objZ)
1740 {
1741 //Transformation matrices
1742 GLdouble m[16], A[16];
1743 GLdouble in[4], out[4];
1744 //Calculation for inverting a matrix, compute projection x modelview
1745 //and store in A[16]
1746 MultiplyMatrices4by4OpenGL_GLdouble(A, projection, modelview);
1747 //Now compute the inverse of matrix A
1748 if(glhInvertMatrixf2(A, m)==0)
1749 return 0;
1750 //Transformation of normalized coordinates between -1 and 1
1751 in[0]=(winx-(GLdouble)viewport[0])/(GLdouble)viewport[2]*2.0-1.0;
1752 in[1]=(winy-(GLdouble)viewport[1])/(GLdouble)viewport[3]*2.0-1.0;
1753 in[2]=2.0*winz-1.0;
1754 in[3]=1.0;
1755 //Objects coordinates
1756 MultiplyMatrixByVector4by4OpenGL_GLdouble(out, m, in);
1757 if(out[3]==0.0)
1758 return 0;
1759 out[3]=1.0/out[3];
1760 *objX=out[0]*out[3];
1761 *objY=out[1]*out[3];
1762 *objZ=out[2]*out[3];
1763 return 1;
1764 }
1765
1766 /*! Returns the screen projected coordinates of a point \p src defined in the \p frame coordinate
1767 system.
1768 When \p frame in \c nullptr (default), \p src is expressed in the world coordinate system.
1769 The x and y coordinates of the returned Vec are expressed in pixel, (0,0) being the \e upper left
1770 corner of the window. The z coordinate ranges between 0.0 (near plane) and 1.0 (excluded, far
1771 plane). See the \c gluProject man page for details.
1772 unprojectedCoordinatesOf() performs the inverse transformation.
1773 See the <a href="../examples/screenCoordSystem.html">screenCoordSystem example</a>.
1774 This method only uses the intrinsic Camera parameters (see getModelViewMatrix(),
1775 getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c
1776 GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use
1777 this method to compute projections out of a classical rendering context.
1778 \attention However, if your Camera is not attached to a CGAL::QGLViewer (used for offscreen computations
1779 for instance), make sure the Camera matrices are updated before calling this method. Call
1780 computeModelViewMatrix() and computeProjectionMatrix() to do so.
1781 If you call this method several times with no change in the matrices, consider precomputing the
1782 projection times modelview matrix to save computation time if required (\c P x \c M in the \c
1783 gluProject man page).
1784 Here is the code corresponding to what this method does (kindly submitted by Robert W. Kuhn) :
1785 \code
1786 Vec project(Vec point)
1787 {
1788 GLint Viewport[4];
1789 GLdouble Projection[16], Modelview[16];
1790 GLdouble matrix[16];
1791 // Precomputation begin
1792 glGetIntegerv(GL_VIEWPORT , Viewport);
1793 glGetDoublev (GL_MODELVIEW_MATRIX , Modelview);
1794 glGetDoublev (GL_PROJECTION_MATRIX, Projection);
1795 for (unsigned short m=0; m<4; ++m)
1796 {
1797 for (unsigned short l=0; l<4; ++l)
1798 {
1799 qreal sum = 0.0;
1800 for (unsigned short k=0; k<4; ++k)
1801 sum += Projection[l+4*k]*Modelview[k+4*m];
1802 matrix[l+4*m] = sum;
1803 }
1804 }
1805 // Precomputation end
1806 GLdouble v[4], vs[4];
1807 v[0]=point[0]; v[1]=point[1]; v[2]=point[2]; v[3]=1.0;
1808 vs[0]=matrix[0 ]*v[0] + matrix[4 ]*v[1] + matrix[8 ]*v[2] + matrix[12 ]*v[3];
1809 vs[1]=matrix[1 ]*v[0] + matrix[5 ]*v[1] + matrix[9 ]*v[2] + matrix[13 ]*v[3];
1810 vs[2]=matrix[2 ]*v[0] + matrix[6 ]*v[1] + matrix[10]*v[2] + matrix[14 ]*v[3];
1811 vs[3]=matrix[3 ]*v[0] + matrix[7 ]*v[1] + matrix[11]*v[2] + matrix[15 ]*v[3];
1812 vs[0] /= vs[3];
1813 vs[1] /= vs[3];
1814 vs[2] /= vs[3];
1815 vs[0] = vs[0] * 0.5 + 0.5;
1816 vs[1] = vs[1] * 0.5 + 0.5;
1817 vs[2] = vs[2] * 0.5 + 0.5;
1818 vs[0] = vs[0] * Viewport[2] + Viewport[0];
1819 vs[1] = vs[1] * Viewport[3] + Viewport[1];
1820 return Vec(vs[0], Viewport[3]-vs[1], vs[2]);
1821 }
1822 \endcode
1823 */
1824 CGAL_INLINE_FUNCTION
projectedCoordinatesOf(const Vec & src,const Frame * frame)1825 Vec Camera::projectedCoordinatesOf(const Vec& src, const Frame* frame) const
1826 {
1827 GLdouble x = 0.f, y = 0.f, z = 0.f;
1828 static GLint viewport[4];
1829 getViewport(viewport);
1830
1831 if (frame)
1832 {
1833 const Vec tmp = frame->inverseCoordinatesOf(src);
1834 project(tmp.x,tmp.y,tmp.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1835 }
1836 else
1837 project(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1838
1839 return Vec(x,y,z);
1840 }
1841
1842 /*! Returns the world unprojected coordinates of a point \p src defined in the screen coordinate
1843 system.
1844 The \p src.x and \p src.y input values are expressed in pixels, (0,0) being the \e upper left corner
1845 of the window. \p src.z is a depth value ranging in [0..1[ (respectively corresponding to the near
1846 and far planes). Note that src.z is \e not a linear interpolation between zNear and zFar.
1847 /code
1848 src.z = zFar() / (zFar() - zNear()) * (1.0 - zNear() / z);
1849 /endcode
1850 Where z is the distance from the point you project to the camera, along the viewDirection().
1851 See the \c gluUnProject man page for details.
1852 The result is expressed in the \p frame coordinate system. When \p frame is \c nullptr (default), the
1853 result is expressed in the world coordinates system. The possible \p frame Frame::referenceFrame()
1854 are taken into account.
1855 projectedCoordinatesOf() performs the inverse transformation.
1856 This method only uses the intrinsic Camera parameters (see getModelViewMatrix(),
1857 getProjectionMatrix() and getViewport()) and is completely independent of the OpenGL \c
1858 GL_MODELVIEW, \c GL_PROJECTION and viewport matrices. You can hence define a virtual Camera and use
1859 this method to compute un-projections out of a classical rendering context.
1860 \attention However, if your Camera is not attached to a CGAL::QGLViewer (used for offscreen computations
1861 for instance), make sure the Camera matrices are updated before calling this method (use
1862 computeModelViewMatrix(), computeProjectionMatrix()). See also setScreenWidthAndHeight().
1863 This method is not computationally optimized. If you call it several times with no change in the
1864 matrices, you should buffer the entire inverse projection matrix (modelview, projection and then
1865 viewport) to speed-up the queries. See the \c gluUnProject man page for details. */
1866 CGAL_INLINE_FUNCTION
unprojectedCoordinatesOf(const Vec & src,const Frame * frame)1867 Vec Camera::unprojectedCoordinatesOf(const Vec& src, const Frame* frame) const
1868 {
1869 GLdouble x = 0.f, y = 0.f, z = 0.f;
1870 static GLint viewport[4];
1871 getViewport(viewport);
1872 unProject(src.x,src.y,src.z, modelViewMatrix_, projectionMatrix_, viewport, &x,&y,&z);
1873 if (frame)
1874 return frame->coordinatesOf(Vec(x,y,z));
1875 else
1876 return Vec(x,y,z);
1877 }
1878
1879 /*! Same as projectedCoordinatesOf(), but with \c qreal parameters (\p src and
1880 * \p res can be identical pointers). */
1881 CGAL_INLINE_FUNCTION
getProjectedCoordinatesOf(const qreal src[3],qreal res[3],const Frame * frame)1882 void Camera::getProjectedCoordinatesOf(const qreal src[3], qreal res[3],
1883 const Frame *frame) const {
1884 Vec r = projectedCoordinatesOf(Vec(src), frame);
1885 for (int i = 0; i < 3; ++i)
1886 res[i] = r[i];
1887 }
1888
1889 /*! Same as unprojectedCoordinatesOf(), but with \c qreal parameters (\p src and
1890 * \p res can be identical pointers). */
1891 CGAL_INLINE_FUNCTION
getUnprojectedCoordinatesOf(const qreal src[3],qreal res[3],const Frame * frame)1892 void Camera::getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3],
1893 const Frame *frame) const {
1894 Vec r = unprojectedCoordinatesOf(Vec(src), frame);
1895 for (int i = 0; i < 3; ++i)
1896 res[i] = r[i];
1897 }
1898
1899 ///////////////////////////////////// KFI
1900 ////////////////////////////////////////////
1901
1902 /*! Returns the KeyFrameInterpolator that defines the Camera path number \p i.
1903
1904 If path \p i is not defined for this index, the method returns a \c nullptr
1905 pointer. */
1906 CGAL_INLINE_FUNCTION
keyFrameInterpolator(unsigned int i)1907 KeyFrameInterpolator *Camera::keyFrameInterpolator(unsigned int i) const {
1908 if (kfi_.contains(i))
1909 return kfi_[i];
1910 else
1911 return nullptr;
1912 }
1913
1914 /*! Sets the KeyFrameInterpolator that defines the Camera path of index \p i.
1915
1916 The previous keyFrameInterpolator() is lost and should be deleted by the
1917 calling method if needed.
1918
1919 The KeyFrameInterpolator::interpolated() signal of \p kfi probably needs to be
1920 connected to the Camera's associated CGAL::QGLViewer::update() slot, so that when the
1921 Camera position is interpolated using \p kfi, every interpolation step updates
1922 the display: \code myViewer.camera()->deletePath(3);
1923 myViewer.camera()->setKeyFrameInterpolator(3, myKeyFrameInterpolator);
1924 connect(myKeyFrameInterpolator, SIGNAL(interpolated()), myViewer,
1925 SLOT(update()); \endcode
1926
1927 \note These connections are done automatically when a Camera is attached to a
1928 CGAL::QGLViewer, or when a new KeyFrameInterpolator is defined using the
1929 CGAL::QGLViewer::addKeyFrameKeyboardModifiers() and CGAL::QGLViewer::pathKey() (default is
1930 Alt+F[1-12]). See the <a href="../keyboard.html">keyboard page</a> for details.
1931 */
1932 CGAL_INLINE_FUNCTION
setKeyFrameInterpolator(unsigned int i,KeyFrameInterpolator * const kfi)1933 void Camera::setKeyFrameInterpolator(unsigned int i,
1934 KeyFrameInterpolator *const kfi) {
1935 if (kfi)
1936 kfi_[i] = kfi;
1937 else
1938 kfi_.remove(i);
1939 }
1940
1941 /*! Adds the current Camera position() and orientation() as a keyFrame to the
1942 path number \p i.
1943
1944 This method can also be used if you simply want to save a Camera point of view
1945 (a path made of a single keyFrame). Use playPath() to make the Camera play the
1946 keyFrame path (resp. restore the point of view). Use deletePath() to clear the
1947 path.
1948
1949 The default keyboard shortcut for this method is Alt+F[1-12]. Set
1950 CGAL::QGLViewer::pathKey() and CGAL::QGLViewer::addKeyFrameKeyboardModifiers().
1951
1952 If you use directly this method and the keyFrameInterpolator(i) does not exist,
1953 a new one is created. Its KeyFrameInterpolator::interpolated() signal should
1954 then be connected to the CGAL::QGLViewer::update() slot (see
1955 setKeyFrameInterpolator()). */
1956 CGAL_INLINE_FUNCTION
addKeyFrameToPath(unsigned int i)1957 void Camera::addKeyFrameToPath(unsigned int i) {
1958 if (!kfi_.contains(i))
1959 setKeyFrameInterpolator(i, new KeyFrameInterpolator(frame()));
1960
1961 kfi_[i]->addKeyFrame(*(frame()));
1962 }
1963
1964 /*! Makes the Camera follow the path of keyFrameInterpolator() number \p i.
1965
1966 If the interpolation is started, it stops it instead.
1967
1968 This method silently ignores undefined (empty) paths (see
1969 keyFrameInterpolator()).
1970
1971 The default keyboard shortcut for this method is F[1-12]. Set
1972 CGAL::QGLViewer::pathKey() and CGAL::QGLViewer::playPathKeyboardModifiers(). */
1973 CGAL_INLINE_FUNCTION
playPath(unsigned int i)1974 void Camera::playPath(unsigned int i) {
1975 if (kfi_.contains(i)) {
1976 if (kfi_[i]->interpolationIsStarted())
1977 kfi_[i]->stopInterpolation();
1978 else
1979 kfi_[i]->startInterpolation();
1980 }
1981 }
1982
1983 /*! Resets the path of the keyFrameInterpolator() number \p i.
1984
1985 If this path is \e not being played (see playPath() and
1986 KeyFrameInterpolator::interpolationIsStarted()), resets it to its starting
1987 position (see KeyFrameInterpolator::resetInterpolation()). If the path is
1988 played, simply stops interpolation. */
1989 CGAL_INLINE_FUNCTION
resetPath(unsigned int i)1990 void Camera::resetPath(unsigned int i) {
1991 if (kfi_.contains(i)) {
1992 if ((kfi_[i]->interpolationIsStarted()))
1993 kfi_[i]->stopInterpolation();
1994 else {
1995 kfi_[i]->resetInterpolation();
1996 kfi_[i]->interpolateAtTime(kfi_[i]->interpolationTime());
1997 }
1998 }
1999 }
2000
2001 /*! Deletes the keyFrameInterpolator() of index \p i.
2002
2003 Disconnect the keyFrameInterpolator() KeyFrameInterpolator::interpolated()
2004 signal before deleting the keyFrameInterpolator() if needed: \code
2005 disconnect(camera()->keyFrameInterpolator(i), SIGNAL(interpolated()), this,
2006 SLOT(update())); camera()->deletePath(i); \endcode */
2007 CGAL_INLINE_FUNCTION
deletePath(unsigned int i)2008 void Camera::deletePath(unsigned int i) {
2009 if (kfi_.contains(i)) {
2010 kfi_[i]->stopInterpolation();
2011 delete kfi_[i];
2012 kfi_.remove(i);
2013 }
2014 }
2015
2016
2017 ////////////////////////////////////////////////////////////////////////////////
2018
2019 /*! Gives the coefficients of a 3D half-line passing through the Camera eye and
2020 pixel (x,y).
2021
2022 The origin of the half line (eye position) is stored in \p orig, while \p dir
2023 contains the properly oriented and normalized direction of the half line.
2024
2025 \p x and \p y are expressed in Qt format (origin in the upper left corner). Use
2026 screenHeight() - y to convert to OpenGL units.
2027
2028 This method is useful for analytical intersection in a selection method.
2029
2030 See the <a href="../examples/select.html">select example</a> for an
2031 illustration. */
2032 CGAL_INLINE_FUNCTION
convertClickToLine(const QPoint & pixel,Vec & orig,Vec & dir)2033 void Camera::convertClickToLine(const QPoint &pixel, Vec &orig,
2034 Vec &dir) const {
2035 switch (type()) {
2036 case Camera::PERSPECTIVE:
2037 orig = position();
2038 dir = Vec(((2.0 * pixel.x() / screenWidth()) - 1.0) *
2039 tan(fieldOfView() / 2.0) * aspectRatio(),
2040 ((2.0 * (screenHeight() - pixel.y()) / screenHeight()) - 1.0) *
2041 tan(fieldOfView() / 2.0),
2042 -1.0);
2043 dir = worldCoordinatesOf(dir) - orig;
2044 dir.normalize();
2045 break;
2046
2047 case Camera::ORTHOGRAPHIC: {
2048 GLdouble w, h;
2049 getOrthoWidthHeight(w, h);
2050 orig = Vec((2.0 * pixel.x() / screenWidth() - 1.0) * w,
2051 -(2.0 * pixel.y() / screenHeight() - 1.0) * h, 0.0);
2052 orig = worldCoordinatesOf(orig);
2053 dir = viewDirection();
2054 break;
2055 }
2056 }
2057 }
2058
2059
2060 /*! Returns the 6 plane equations of the Camera frustum.
2061
2062 The six 4-component vectors of \p coef respectively correspond to the left,
2063 right, near, far, top and bottom Camera frustum planes. Each vector holds a
2064 plane equation of the form: \code a*x + b*y + c*z + d = 0 \endcode where \c a,
2065 \c b, \c c and \c d are the 4 components of each vector, in that order.
2066
2067 See the <a href="../examples/frustumCulling.html">frustumCulling example</a> for
2068 an application.
2069
2070 This format is compatible with the \c glClipPlane() function. One camera frustum
2071 plane can hence be applied in an other viewer to visualize the culling results:
2072 \code
2073 // Retrieve plane equations
2074 GLdouble coef[6][4];
2075 mainViewer->camera()->getFrustumPlanesCoefficients(coef);
2076
2077 // These two additional clipping planes (which must have been enabled)
2078 // will reproduce the mainViewer's near and far clipping.
2079 glClipPlane(GL_CLIP_PLANE0, coef[2]);
2080 glClipPlane(GL_CLIP_PLANE1, coef[3]);
2081 \endcode */
2082 CGAL_INLINE_FUNCTION
getFrustumPlanesCoefficients(GLdouble coef[6][4])2083 void Camera::getFrustumPlanesCoefficients(GLdouble coef[6][4]) const {
2084 // Computed once and for all
2085 const Vec pos = position();
2086 const Vec viewDir = viewDirection();
2087 const Vec up = upVector();
2088 const Vec right = rightVector();
2089 const qreal posViewDir = pos * viewDir;
2090
2091 static Vec normal[6];
2092 static GLdouble dist[6];
2093
2094 switch (type()) {
2095 case Camera::PERSPECTIVE: {
2096 const qreal hhfov = horizontalFieldOfView() / 2.0;
2097 const qreal chhfov = cos(hhfov);
2098 const qreal shhfov = sin(hhfov);
2099 normal[0] = -shhfov * viewDir;
2100 normal[1] = normal[0] + chhfov * right;
2101 normal[0] = normal[0] - chhfov * right;
2102
2103 normal[2] = -viewDir;
2104 normal[3] = viewDir;
2105
2106 const qreal hfov = fieldOfView() / 2.0;
2107 const qreal chfov = cos(hfov);
2108 const qreal shfov = sin(hfov);
2109 normal[4] = -shfov * viewDir;
2110 normal[5] = normal[4] - chfov * up;
2111 normal[4] = normal[4] + chfov * up;
2112
2113 for (int i = 0; i < 2; ++i)
2114 dist[i] = pos * normal[i];
2115 for (int j = 4; j < 6; ++j)
2116 dist[j] = pos * normal[j];
2117
2118 // Natural equations are:
2119 // dist[0,1,4,5] = pos * normal[0,1,4,5];
2120 // dist[2] = (pos + zNear() * viewDir) * normal[2];
2121 // dist[3] = (pos + zFar() * viewDir) * normal[3];
2122
2123 // 2 times less computations using expanded/merged equations. Dir vectors
2124 // are normalized.
2125 const qreal posRightCosHH = chhfov * pos * right;
2126 dist[0] = -shhfov * posViewDir;
2127 dist[1] = dist[0] + posRightCosHH;
2128 dist[0] = dist[0] - posRightCosHH;
2129 const qreal posUpCosH = chfov * pos * up;
2130 dist[4] = -shfov * posViewDir;
2131 dist[5] = dist[4] - posUpCosH;
2132 dist[4] = dist[4] + posUpCosH;
2133
2134 break;
2135 }
2136 case Camera::ORTHOGRAPHIC:
2137 normal[0] = -right;
2138 normal[1] = right;
2139 normal[4] = up;
2140 normal[5] = -up;
2141
2142 GLdouble hw, hh;
2143 getOrthoWidthHeight(hw, hh);
2144 dist[0] = (pos - hw * right) * normal[0];
2145 dist[1] = (pos + hw * right) * normal[1];
2146 dist[4] = (pos + hh * up) * normal[4];
2147 dist[5] = (pos - hh * up) * normal[5];
2148 break;
2149 }
2150
2151 // Front and far planes are identical for both camera types.
2152 normal[2] = -viewDir;
2153 normal[3] = viewDir;
2154 dist[2] = -posViewDir - zNear();
2155 dist[3] = posViewDir + zFar();
2156
2157 for (int i = 0; i < 6; ++i) {
2158 coef[i][0] = GLdouble(normal[i].x);
2159 coef[i][1] = GLdouble(normal[i].y);
2160 coef[i][2] = GLdouble(normal[i].z);
2161 coef[i][3] = dist[i];
2162 }
2163 }
2164
2165 CGAL_INLINE_FUNCTION
onFrameModified()2166 void Camera::onFrameModified() {
2167 projectionMatrixIsUpToDate_ = false;
2168 modelViewMatrixIsUpToDate_ = false;
2169 }
2170
2171 CGAL_INLINE_FUNCTION
setHorizontalFieldOfView(qreal hfov)2172 void Camera::setHorizontalFieldOfView(qreal hfov) {
2173 setFieldOfView(2.0 * atan(tan(hfov / 2.0) / aspectRatio()));
2174 }
2175
2176 CGAL_INLINE_FUNCTION
horizontalFieldOfView()2177 qreal Camera::horizontalFieldOfView() const {
2178 return 2.0 * atan(tan(fieldOfView() / 2.0) * aspectRatio());
2179 }
2180
2181
2182
2183 CGAL_INLINE_FUNCTION
setFrustum(double frustum[6])2184 void Camera::setFrustum(double frustum[6])
2185 {
2186 double l(frustum[0]),r(frustum[1]),t(frustum[2]),
2187 b(frustum[3]),n(frustum[4]),f(frustum[5]);
2188 if(type() == PERSPECTIVE)
2189 {
2190 double A = 2*n/(r-l);
2191 double B = (r+l)/(r-l);
2192 double C = 2*n/(t-b);
2193 double D = (t+b)/(t-b);
2194 double E = -(f+n)/(f-n);
2195 double F = -2*(f*n)/(f-n);
2196 projectionMatrix_[0] = A; projectionMatrix_[4] = 0; projectionMatrix_[8] = B ; projectionMatrix_[12] = 0;
2197 projectionMatrix_[1] = 0; projectionMatrix_[5] = C; projectionMatrix_[9] = D ; projectionMatrix_[13] = 0;
2198 projectionMatrix_[2] = 0; projectionMatrix_[6] = 0; projectionMatrix_[10] = E ; projectionMatrix_[14] = F;
2199 projectionMatrix_[3] =0; projectionMatrix_[7] =0; projectionMatrix_[11] =-1; projectionMatrix_[15] =0;
2200 }
2201 else
2202 {
2203 double A = 2/(r-l);
2204 double B = -(r+l)/(r-l);
2205 double C = 2/(t-b);
2206 double D = -(t+b)/(t-b);
2207 double E = -(f+n)/(f-n);
2208 double F = -2/(f-n);
2209 projectionMatrix_[0] = A; projectionMatrix_[1] = 0; projectionMatrix_[2] = 0 ; projectionMatrix_[3] = 0;
2210 projectionMatrix_[4] = 0; projectionMatrix_[5] = C; projectionMatrix_[6] = 0 ; projectionMatrix_[7] = 0;
2211 projectionMatrix_[8] = 0; projectionMatrix_[9] = 0; projectionMatrix_[10] = F ; projectionMatrix_[11] = 0;
2212 projectionMatrix_[12] = B; projectionMatrix_[13] = D; projectionMatrix_[14] = E; projectionMatrix_[15] = 1;
2213 }
2214 projectionMatrixIsUpToDate_ = true;
2215 }
2216
2217 CGAL_INLINE_FUNCTION
getFrustum(double frustum[6])2218 void Camera::getFrustum(double frustum[6])
2219 {
2220 double l,r,t,b,n,f;
2221 if(type() == PERSPECTIVE)
2222 {
2223 n = projectionMatrix_[14]/2*((projectionMatrix_[10]+1)/(projectionMatrix_[10]-1)-1);
2224 f = n*(projectionMatrix_[10]-1)/(projectionMatrix_[10]+1);
2225 l = ((2*n/projectionMatrix_[0])*(projectionMatrix_[8]-1)/(projectionMatrix_[8]+1))/(1-(projectionMatrix_[8]-1)/(projectionMatrix_[8]+1));
2226 r = 2*n/projectionMatrix_[0]+l;
2227 b=(-2*n/projectionMatrix_[5]*(1-projectionMatrix_[9])/(1+projectionMatrix_[9]))/(1+(1-projectionMatrix_[9])/(1+projectionMatrix_[9]));
2228 t = 2*n/projectionMatrix_[5]+b;
2229 }
2230 else
2231 {
2232 double A(projectionMatrix_[0]),B(projectionMatrix_[12]),
2233 C(projectionMatrix_[5]),D(projectionMatrix_[13]),
2234 E(projectionMatrix_[14]),F(projectionMatrix_[10]);
2235 double B1 = (B+1)/(1-B), D1 = (1-D)/(D+1),
2236 E1=(E+1)/(1-E);
2237
2238 l = -2*B1/(1+B1*A);
2239 r = 2+A*l;
2240 t = 2*D1/(C*(1+D1));
2241 b =t -2/C;
2242 n = -2/(F*(1+E1));
2243 f=n-2/F;
2244
2245 }
2246 frustum[0] = l;
2247 frustum[1] = r;
2248 frustum[2] = t;
2249 frustum[3] = b;
2250 frustum[4] = n;
2251 frustum[5] = f;
2252 }
2253 }}//end of namespace
2254