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 ¢er, 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 ¢er); 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