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.h $
10 // $Id: camera.h 9cd0d45 2021-02-09T11:31:34+01:00 Maxime Gimeno
11 // SPDX-License-Identifier: GPL-3.0-only
12 
13 
14 #ifndef QGLVIEWER_CAMERA_H
15 #define QGLVIEWER_CAMERA_H
16 #include <QMap>
17 #include <CGAL/Qt/vec.h>
18 #include <CGAL/Qt/quaternion.h>
19 #include <CGAL/export/Qt.h>
20 #include <QOpenGLFunctions_2_1>
21 
22 namespace CGAL{
23 class QGLViewer;
24 namespace qglviewer {
25 
26 class KeyFrameInterpolator;
27 class Frame;
28 class ManipulatedCameraFrame;
29 
30 
31 /*! \brief A perspective or orthographic camera.
32   \class Camera camera.h CGAL::QGLViewer/camera.h
33 
34   A Camera defines some intrinsic parameters (fieldOfView(), position(),
35   viewDirection(), upVector()...) and useful positioning tools that ease its
36   placement (showEntireScene(), fitSphere(), lookAt()...). It exports its
37   associated OpenGL projection and modelview matrices and can interactively be
38   modified using the mouse.
39 
40   <h3>Mouse manipulation</h3>
41 
42   The position() and orientation() of the Camera are defined by a
43   ManipulatedCameraFrame (retrieved using frame()). These methods are just
44   convenient wrappers to the equivalent Frame methods. This also means that the
45   Camera frame() can be attached to a Frame::referenceFrame() which enables
46   complex Camera setups.
47 
48   Different displacements can be performed using the mouse. The list of possible
49   actions is defined by the CGAL::QGLViewer::MouseAction enum. Use
50   CGAL::QGLViewer::setMouseBinding() to attach a specific action to an arbitrary mouse
51   button-state key binding. These actions are detailed in the <a
52   href="../mouse.html">mouse page</a>.
53 
54   The default button binding are: CGAL::QGLViewer::ROTATE (left), CGAL::QGLViewer::ZOOM
55   (middle) and CGAL::QGLViewer::TRANSLATE (right). With this configuration, the Camera
56   \e observes a scene and rotates around its pivotPoint(). You can switch
57   between this mode and a fly mode using the CGAL::QGLViewer::CAMERA_MODE (see
58   CGAL::QGLViewer::toggleCameraMode()) keyboard shortcut (default is 'Space').
59 
60   <h3>Other functionalities</h3>
61 
62   The type() of the Camera can be Camera::ORTHOGRAPHIC or Camera::PERSPECTIVE
63   (see Type()). fieldOfView() is meaningless with Camera::ORTHOGRAPHIC.
64 
65   The near and far planes of the Camera are fitted to the scene and determined
66   from CGAL::QGLViewer::sceneRadius(), CGAL::QGLViewer::sceneCenter() and
67   zClippingCoefficient() by the zNear() and zFar() methods. Reasonable values on
68   the scene extends hence have to be provided to the CGAL::QGLViewer in order for the
69   Camera to correctly display the scene. High level positioning methods also use
70   this information (showEntireScene(), centerScene()...).
71 
72   A Camera holds KeyFrameInterpolator that can be used to save Camera positions
73   and paths. You can interactively addKeyFrameToPath() to a given path using the
74   default \c Alt+F[1-12] shortcuts. Use playPath() to make the Camera follow the
75   path (default shortcut is F[1-12]). See the <a
76   href="../keyboard.html">keyboard page</a> for details on key customization.
77 
78   Use cameraCoordinatesOf() and worldCoordinatesOf() to convert to and from the
79   Camera frame() coordinate system. projectedCoordinatesOf() and
80   unprojectedCoordinatesOf() will convert from screen to 3D coordinates.
81   convertClickToLine() is very useful for analytical object selection.
82 
83 
84 
85   A Camera can also be used outside of a CGAL::QGLViewer or even without OpenGL for
86   its coordinate system conversion capabilities. Note however that some of them
87   explicitly rely on the presence of a Z-buffer. \nosubgrouping */
88 class CGAL_QT_EXPORT Camera : public QObject {
89 #ifndef DOXYGEN
90   friend class ::CGAL::QGLViewer;
91 #endif
92 
93   Q_OBJECT
94 
95 public:
96   Camera(QObject *parent);
97   virtual ~Camera();
98 
99   Camera(const Camera &camera);
100   Camera &operator=(const Camera &camera);
101 
102   /*! Enumerates the two possible types of Camera.
103 
104   See type() and setType(). This type mainly defines different Camera projection
105   matrix (see loadProjectionMatrix()). Many other methods (pointUnderPixel(),
106   convertClickToLine(), projectedCoordinatesOf(), pixelGLRatio()...) are
107   affected by this Type. */
108   enum Type { PERSPECTIVE, ORTHOGRAPHIC };
109 
110   /*! @name Position and orientation */
111   //@{
112 public:
113   Vec position() const;
114   Vec upVector() const;
115   Vec viewDirection() const;
116   Vec rightVector() const;
117   Quaternion orientation() const;
118 
119   void setFromModelViewMatrix(const GLdouble *const modelViewMatrix);
120   void setFromProjectionMatrix(const qreal matrix[12]);
121 
122 public Q_SLOTS:
123   void setPosition(const Vec &pos);
124   void setOrientation(const Quaternion &q);
125   void setOrientation(qreal theta, qreal phi);
126   void setUpVector(const Vec &up, bool noMove = true);
127   void setViewDirection(const Vec &direction);
128   //@}
129 
130   /*! @name Positioning tools */
131   //@{
132 public Q_SLOTS:
133   void lookAt(const Vec &target);
134   void showEntireScene();
135   void fitSphere(const Vec &center, qreal radius);
136   void fitBoundingBox(const Vec &min, const Vec &max);
137   void fitScreenRegion(const QRect &rectangle);
138   void centerScene();
139   void interpolateToZoomOnPixel(const QPoint &pixel);
140   void interpolateToFitScene();
141   void interpolateTo(const Frame &fr, qreal duration);
142   //@}
143 
144   /*! @name Frustum */
145   //@{
146 public:
147   /*! Returns the Camera::Type of the Camera.
148 
149   Set by setType(). Mainly used by loadProjectionMatrix().
150 
151   A Camera::PERSPECTIVE Camera uses a classical projection mainly defined by its
152   fieldOfView().
153 
154   With a Camera::ORTHOGRAPHIC type(), the fieldOfView() is meaningless and the
155   width and height of the Camera frustum are inferred from the distance to the
156   pivotPoint() using getOrthoWidthHeight().
157 
158   Both types use zNear() and zFar() (to define their clipping planes) and
159   aspectRatio() (for frustum shape). */
type()160   Type type() const { return type_; }
161 
162   /*! Returns the vertical field of view of the Camera (in radians).
163 
164   Value is set using setFieldOfView(). Default value is pi/4 radians. This value
165   is meaningless if the Camera type() is Camera::ORTHOGRAPHIC.
166 
167   The field of view corresponds the one used in \c gluPerspective (see manual).
168   It sets the Y (vertical) aperture of the Camera. The X (horizontal) angle is
169   inferred from the window aspect ratio (see aspectRatio() and
170   horizontalFieldOfView()).
171 
172   Use setFOVToFitScene() to adapt the fieldOfView() to a given scene. */
fieldOfView()173   qreal fieldOfView() const { return fieldOfView_; }
174 
175   /*! Returns the horizontal field of view of the Camera (in radians).
176 
177   Value is set using setHorizontalFieldOfView() or setFieldOfView(). These
178   values are always linked by: \code horizontalFieldOfView() = 2.0 * atan (
179   tan(fieldOfView()/2.0) * aspectRatio() ). \endcode */
180   qreal horizontalFieldOfView() const;
181 
182   /*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight().
183 
184   When the Camera is attached to a CGAL::QGLViewer, these values and hence the
185   aspectRatio() are automatically fitted to the viewer's window aspect ratio
186   using setScreenWidthAndHeight(). */
aspectRatio()187   qreal aspectRatio() const {
188     return screenWidth_ / static_cast<qreal>(screenHeight_);
189   }
190   /*! Returns the width (in pixels) of the Camera screen.
191 
192   Set using setScreenWidthAndHeight(). This value is automatically fitted to the
193   CGAL::QGLViewer's window dimensions when the Camera is attached to a CGAL::QGLViewer. See
194   also QOpenGLWidget::width() */
screenWidth()195   int screenWidth() const { return screenWidth_; }
196   /*! Returns the height (in pixels) of the Camera screen.
197 
198   Set using setScreenWidthAndHeight(). This value is automatically fitted to the
199   CGAL::QGLViewer's window dimensions when the Camera is attached to a CGAL::QGLViewer. See
200   also QOpenGLWidget::height() */
screenHeight()201   int screenHeight() const { return screenHeight_; }
202   void getViewport(GLint viewport[4]) const;
203   qreal pixelGLRatio(const Vec &position) const;
204 
205   /*! Returns the coefficient which is used to set zNear() when the Camera is
206   inside the sphere defined by sceneCenter() and zClippingCoefficient() *
207   sceneRadius().
208 
209   In that case, the zNear() value is set to zNearCoefficient() *
210   zClippingCoefficient() * sceneRadius(). See the zNear() documentation for
211   details.
212 
213   Default value is 0.005, which is appropriate for most applications. In case
214   you need a high dynamic ZBuffer precision, you can increase this value (~0.1).
215   A lower value will prevent clipping of very close objects at the expense of a
216   worst Z precision.
217 
218   Only meaningful when Camera type is Camera::PERSPECTIVE. */
zNearCoefficient()219   qreal zNearCoefficient() const { return zNearCoef_; }
220   /*! Returns the coefficient used to position the near and far clipping planes.
221 
222   The near (resp. far) clipping plane is positioned at a distance equal to
223   zClippingCoefficient() * sceneRadius() in front of (resp. behind) the
224   sceneCenter(). This guarantees an optimal use of the z-buffer range and
225   minimizes aliasing. See the zNear() and zFar() documentations.
226 
227   Default value is square root of 3.0 (so that a cube of size sceneRadius() is
228   not clipped).
229 
230   However, since the sceneRadius() is used for other purposes (see
231   showEntireScene(), flySpeed(),
232   ...) and you may want to change this value to define more precisely the
233   location of the clipping planes. See also zNearCoefficient().
234 
235   For a total control on clipping planes' positions, an other option is to
236   overload the zNear() and zFar() methods. See the <a
237   href="../examples/standardCamera.html">standardCamera example</a>.
238 
239   \attention When CGAL::QGLViewer::cameraPathAreEdited(), this value is set to 5.0 so
240   that the Camera paths are not clipped. The previous zClippingCoefficient()
241   value is restored back when you leave this mode. */
zClippingCoefficient()242   qreal zClippingCoefficient() const { return zClippingCoef_; }
243 
244   virtual qreal zNear() const;
245   virtual qreal zFar() const;
246   virtual void getOrthoWidthHeight(GLdouble &halfWidth,
247                                    GLdouble &halfHeight) const;
248   void getFrustumPlanesCoefficients(GLdouble coef[6][4]) const;
249 
250 public Q_SLOTS:
251   void setType(Type type);
252 
253   void setFieldOfView(qreal fov);
254 
255   /*! Sets the horizontalFieldOfView() of the Camera (in radians).
256 
257   horizontalFieldOfView() and fieldOfView() are linked by the aspectRatio().
258   This method actually calls setFieldOfView(( 2.0 * atan (tan(hfov / 2.0) /
259   aspectRatio()) )) so that a call to horizontalFieldOfView() returns the
260   expected value. */
261   void setHorizontalFieldOfView(qreal hfov);
262 
263   void setFOVToFitScene();
264 
265   /*! Defines the Camera aspectRatio().
266 
267   This value is actually inferred from the screenWidth() / screenHeight() ratio.
268   You should use setScreenWidthAndHeight() instead.
269 
270   This method might however be convenient when the Camera is not associated with
271   a CGAL::QGLViewer. It actually sets the screenHeight() to 100 and the screenWidth()
272   accordingly. See also setFOVToFitScene().
273 
274   \note If you absolutely need an aspectRatio() that does not correspond to your
275   viewer's window dimensions, overload loadProjectionMatrix() or multiply the
276   created GL_PROJECTION matrix by a scaled diagonal matrix in your
277   CGAL::QGLViewer::draw() method. */
setAspectRatio(qreal aspect)278   void setAspectRatio(qreal aspect) {
279     setScreenWidthAndHeight(int(100.0 * aspect), 100);
280   }
281 
282   void setScreenWidthAndHeight(int width, int height);
283   /*! Sets the zNearCoefficient() value. */
setZNearCoefficient(qreal coef)284   void setZNearCoefficient(qreal coef) {
285     zNearCoef_ = coef;
286     projectionMatrixIsUpToDate_ = false;
287   }
288   /*! Sets the zClippingCoefficient() value. */
setZClippingCoefficient(qreal coef)289   void setZClippingCoefficient(qreal coef) {
290     zClippingCoef_ = coef;
291     projectionMatrixIsUpToDate_ = false;
292   }
293   /*! Sets the zNear value in orthographic mode. */
setOrthoZNear(qreal z)294   void setOrthoZNear(qreal z)
295   {
296     m_zMin = z;
297   }
298   /*! Returns the zNear value in orthographic mode*/
orthoZNear()299   qreal orthoZNear()
300   {
301     return m_zMin;
302   }
303   //@}
304 
305   /*! @name Scene radius and center */
306   //@{
307 public:
308   /*! Returns the radius of the scene observed by the Camera.
309 
310   You need to provide such an approximation of the scene dimensions so that the
311   Camera can adapt its zNear() and zFar() values. See the sceneCenter()
312   documentation.
313 
314   See also setSceneBoundingBox().
315 
316   Note that CGAL::QGLViewer::sceneRadius() (resp. CGAL::QGLViewer::setSceneRadius()) simply
317   call this method (resp. setSceneRadius()) on its associated
318   CGAL::QGLViewer::camera(). */
sceneRadius()319   qreal sceneRadius() const { return sceneRadius_; }
320 
321   /*! Returns the position of the scene center, defined in the world coordinate
322   system.
323 
324   The scene observed by the Camera should be roughly centered on this position,
325   and included in a sceneRadius() sphere. This approximate description of the
326   scene permits a zNear() and zFar() clipping planes definition, and allows
327   convenient positioning methods such as showEntireScene().
328 
329   Default value is (0,0,0) (world origin). Use setSceneCenter() to change it.
330   See also setSceneBoundingBox().
331 
332   Note that CGAL::QGLViewer::sceneCenter() (resp. CGAL::QGLViewer::setSceneCenter()) simply
333   calls this method (resp. setSceneCenter()) on its associated
334   CGAL::QGLViewer::camera(). */
sceneCenter()335   Vec sceneCenter() const { return sceneCenter_; }
336   qreal distanceToSceneCenter() const;
337 
338 public Q_SLOTS:
339   void setSceneRadius(qreal radius);
340   void setSceneCenter(const Vec &center);
341   bool setSceneCenterFromPixel(const QPoint &pixel);
342   void setSceneBoundingBox(const Vec &min, const Vec &max);
343   //@}
344 
345   /*! @name Pivot Point */
346   //@{
347 public Q_SLOTS:
348   void setPivotPoint(const Vec &point);
349   bool setPivotPointFromPixel(const QPoint &pixel);
350 
351 public:
352   Vec pivotPoint() const;
353 
354   //@}
355 
356   /*! @name Associated frame */
357   //@{
358 public:
359   /*! Returns the ManipulatedCameraFrame attached to the Camera.
360 
361   This ManipulatedCameraFrame defines its position() and orientation() and can
362   translate mouse events into Camera displacement. Set using setFrame(). */
frame()363   ManipulatedCameraFrame *frame() const { return frame_; }
364 public Q_SLOTS:
365   void setFrame(ManipulatedCameraFrame *const mcf);
366   //@}
367 
368   /*! @name KeyFramed paths */
369   //@{
370 public:
371   KeyFrameInterpolator *keyFrameInterpolator(unsigned int i) const;
372 
373 public Q_SLOTS:
374   void setKeyFrameInterpolator(unsigned int i, KeyFrameInterpolator *const kfi);
375 
376   virtual void addKeyFrameToPath(unsigned int i);
377   virtual void playPath(unsigned int i);
378   virtual void deletePath(unsigned int i);
379   virtual void resetPath(unsigned int i);
380   //@}
381 
382   /*! @name OpenGL matrices */
383   //@{
384 public:
385   virtual void loadProjectionMatrix(bool reset = true) const;
386   virtual void loadModelViewMatrix(bool reset = true) const;
387   void computeProjectionMatrix() const;
388   void computeModelViewMatrix() const;
389   //!Sets the frustum according to the current type of the camera
390   //! (PERSPECTIVE or ORTHOGRAPHIC) in this order :
391   //! left, right, top, bottom, near, far
392   void setFrustum(double frustum[6]);
393   //!Fills `frustum` from the current frustum of the camera according
394   //! to the current type (PERSPECTIVE or ORTHOGRAPHIC) in this order :
395   //! left, right, top, bottom, near, far
396   void getFrustum(double frustum[6]);
397   void getProjectionMatrix(GLfloat m[16]) const;
398   void getProjectionMatrix(GLdouble m[16]) const;
399 
400   void getModelViewMatrix(GLfloat m[16]) const;
401   void getModelViewMatrix(GLdouble m[16]) const;
402 
403   void getModelViewProjectionMatrix(GLfloat m[16]) const;
404   void getModelViewProjectionMatrix(GLdouble m[16]) const;
405 //@}
406 
407   /*! @name World to Camera coordinate systems conversions */
408   //@{
409 public:
410   Vec cameraCoordinatesOf(const Vec &src) const;
411   Vec worldCoordinatesOf(const Vec &src) const;
412   void getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const;
413   void getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const;
414   //@}
415 
416   /*! @name 2D screen to 3D world coordinate systems conversions */
417   //@{
418 public:
419   Vec projectedCoordinatesOf(const Vec &src, const Frame *frame = nullptr) const;
420   Vec unprojectedCoordinatesOf(const Vec &src, const Frame *frame = nullptr) const;
421   void getProjectedCoordinatesOf(const qreal src[3], qreal res[3],
422                                  const Frame *frame = nullptr) const;
423   void getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3],
424                                    const Frame *frame = nullptr) const;
425   void convertClickToLine(const QPoint &pixel, Vec &orig, Vec &dir) const;
426   Vec pointUnderPixel(const QPoint &pixel, bool &found) const;
427   //@}
428 
429   /*! @name Fly speed */
430   //@{
431 public:
432   qreal flySpeed() const;
433 public Q_SLOTS:
434   void setFlySpeed(qreal speed);
435   //@}
436 
437 private Q_SLOTS:
438   void onFrameModified();
439 
440 private:
gl()441   QOpenGLFunctions_2_1* gl() const{ return dynamic_cast<QOpenGLFunctions_2_1*>(parent()); }
442   // F r a m e
443   ManipulatedCameraFrame *frame_;
444 
445   // C a m e r a   p a r a m e t e r s
446   int screenWidth_, screenHeight_; // size of the window, in pixels
447   qreal fieldOfView_;              // in radians
448   Vec sceneCenter_;
449   qreal sceneRadius_; // OpenGL units
450   qreal zNearCoef_;
451   qreal zClippingCoef_;
452   qreal orthoCoef_;
453   Type type_;                            // PERSPECTIVE or ORTHOGRAPHIC
454   mutable GLdouble modelViewMatrix_[16]; // Buffered model view matrix.
455   mutable bool modelViewMatrixIsUpToDate_;
456   mutable GLdouble projectionMatrix_[16]; // Buffered projection matrix.
457   mutable bool projectionMatrixIsUpToDate_;
458   qreal m_zMin; //USed for near plane in orthographic projection.
459 
460   // S t e r e o   p a r a m e t e r s
461   qreal IODistance_;          // inter-ocular distance, in meters
462   qreal focusDistance_;       // in scene units
463   qreal physicalScreenWidth_; // in meters
464 
465   // P o i n t s   o f   V i e w s   a n d   K e y F r a m e s
466   QMap<unsigned int, KeyFrameInterpolator *> kfi_;
467   KeyFrameInterpolator *interpolationKfi_;
468 };
469 
470 } // namespace qglviewer
471 } //CGAL
472 #endif // QGLVIEWER_CAMERA_H
473