QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgscameracontroller.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscameracontroller.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgscameracontroller.h"
17 #include "qgsraycastingutils_p.h"
18 #include "qgsterrainentity_p.h"
19 #include "qgsvector3d.h"
20 #include "qgssettings.h"
21 #include "qgs3dutils.h"
22 
23 #include "qgis.h"
24 
25 #include <QDomDocument>
26 #include <Qt3DRender/QCamera>
27 #include <Qt3DRender/QObjectPicker>
28 #include <Qt3DRender/QPickEvent>
29 #include <Qt3DInput>
30 
31 #include "qgslogger.h"
32 
33 QgsCameraController::QgsCameraController( Qt3DCore::QNode *parent )
34  : Qt3DCore::QEntity( parent )
35  , mCameraBeforeRotation( new Qt3DRender::QCamera )
36  , mCameraBeforeDrag( new Qt3DRender::QCamera )
37  , mCameraBeforeZoom( new Qt3DRender::QCamera )
38  , mMouseDevice( new Qt3DInput::QMouseDevice() )
39  , mKeyboardDevice( new Qt3DInput::QKeyboardDevice() )
40  , mMouseHandler( new Qt3DInput::QMouseHandler )
41  , mKeyboardHandler( new Qt3DInput::QKeyboardHandler )
42 {
43  mMouseHandler->setSourceDevice( mMouseDevice );
44  connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
45  this, &QgsCameraController::onPositionChanged );
46  connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
47  this, &QgsCameraController::onWheel );
48  connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
49  this, &QgsCameraController::onMousePressed );
50  connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
51  this, &QgsCameraController::onMouseReleased );
52  addComponent( mMouseHandler );
53 
54  mKeyboardHandler->setSourceDevice( mKeyboardDevice );
55  connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
56  this, &QgsCameraController::onKeyPressed );
57  connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
58  this, &QgsCameraController::onKeyReleased );
59  addComponent( mKeyboardHandler );
60 
61  // Disable the handlers when the entity is disabled
62  connect( this, &Qt3DCore::QEntity::enabledChanged,
63  mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
64  connect( this, &Qt3DCore::QEntity::enabledChanged,
65  mKeyboardHandler, &Qt3DInput::QMouseHandler::setEnabled );
66 
67  mFpsNavTimer = new QTimer( this );
68  mFpsNavTimer->setInterval( 10 );
69  connect( mFpsNavTimer, &QTimer::timeout, this, &QgsCameraController::applyFlyModeKeyMovements );
70  mFpsNavTimer->start();
71 }
72 
74 {
75  if ( navigationMode == mCameraNavigationMode )
76  return;
77 
78  mCameraNavigationMode = navigationMode;
79  mIgnoreNextMouseMove = true;
80 }
81 
82 void QgsCameraController::setCameraMovementSpeed( double movementSpeed )
83 {
84  if ( movementSpeed == mCameraMovementSpeed )
85  return;
86 
87  mCameraMovementSpeed = movementSpeed;
88  emit cameraMovementSpeedChanged( mCameraMovementSpeed );
89 }
90 
92 {
93  mVerticalAxisInversion = inversion;
94 }
95 
96 void QgsCameraController::setTerrainEntity( QgsTerrainEntity *te )
97 {
98  mTerrainEntity = te;
99  // object picker for terrain for correct map panning
100  if ( mTerrainEntity )
101  connect( te->terrainPicker(), &Qt3DRender::QObjectPicker::pressed, this, &QgsCameraController::onPickerMousePressed );
102 }
103 
104 void QgsCameraController::setCamera( Qt3DRender::QCamera *camera )
105 {
106  if ( mCamera == camera )
107  return;
108  mCamera = camera;
109 
110  mCameraPose.updateCamera( mCamera ); // initial setup
111 
112  // TODO: set camera's parent if not set already?
113  // TODO: registerDestructionHelper (?)
114  emit cameraChanged();
115 }
116 
117 void QgsCameraController::setViewport( QRect viewport )
118 {
119  if ( mViewport == viewport )
120  return;
121 
122  mViewport = viewport;
123  emit viewportChanged();
124 }
125 
126 
127 static QVector3D unproject( QVector3D v, const QMatrix4x4 &modelView, const QMatrix4x4 &projection, QRect viewport )
128 {
129  // Reimplementation of QVector3D::unproject() - see qtbase/src/gui/math3d/qvector3d.cpp
130  // The only difference is that the original implementation uses tolerance 1e-5
131  // (see qFuzzyIsNull()) as a protection against division by zero. For us it is however
132  // common to get lower values (e.g. as low as 1e-8 when zoomed out to the whole Earth with web mercator).
133 
134  const QMatrix4x4 inverse = QMatrix4x4( projection * modelView ).inverted();
135 
136  QVector4D tmp( v, 1.0f );
137  tmp.setX( ( tmp.x() - float( viewport.x() ) ) / float( viewport.width() ) );
138  tmp.setY( ( tmp.y() - float( viewport.y() ) ) / float( viewport.height() ) );
139  tmp = tmp * 2.0f - QVector4D( 1.0f, 1.0f, 1.0f, 1.0f );
140 
141  QVector4D obj = inverse * tmp;
142  if ( qgsDoubleNear( obj.w(), 0, 1e-10 ) )
143  obj.setW( 1.0f );
144  obj /= obj.w();
145  return obj.toVector3D();
146 }
147 
148 
149 float find_x_on_line( float x0, float y0, float x1, float y1, float y )
150 {
151  const float d_x = x1 - x0;
152  const float d_y = y1 - y0;
153  const float k = ( y - y0 ) / d_y; // TODO: can we have d_y == 0 ?
154  return x0 + k * d_x;
155 }
156 
157 QPointF screen_point_to_point_on_plane( QPointF pt, QRect viewport, Qt3DRender::QCamera *camera, float y )
158 {
159  // get two points of the ray
160  const QVector3D l0 = unproject( QVector3D( pt.x(), viewport.height() - pt.y(), 0 ), camera->viewMatrix(), camera->projectionMatrix(), viewport );
161  const QVector3D l1 = unproject( QVector3D( pt.x(), viewport.height() - pt.y(), 1 ), camera->viewMatrix(), camera->projectionMatrix(), viewport );
162 
163  const QVector3D p0( 0, y, 0 ); // a point on the plane
164  const QVector3D n( 0, 1, 0 ); // normal of the plane
165  const QVector3D l = l1 - l0; // vector in the direction of the line
166  const float d = QVector3D::dotProduct( p0 - l0, n ) / QVector3D::dotProduct( l, n );
167  const QVector3D p = d * l + l0;
168 
169  return QPointF( p.x(), p.z() );
170 }
171 
172 
173 void QgsCameraController::rotateCamera( float diffPitch, float diffYaw )
174 {
175  const float pitch = mCameraPose.pitchAngle();
176  const float yaw = mCameraPose.headingAngle();
177 
178  if ( pitch + diffPitch > 180 )
179  diffPitch = 180 - pitch; // prevent going over the head
180  if ( pitch + diffPitch < 0 )
181  diffPitch = 0 - pitch; // prevent going over the head
182 
183  // Is it always going to be love/hate relationship with quaternions???
184  // This quaternion combines two rotations:
185  // - first it undoes the previously applied rotation so we have do not have any rotation compared to world coords
186  // - then it applies new rotation
187  // (We can't just apply our euler angles difference because the camera may be already rotated)
188  const QQuaternion q = QQuaternion::fromEulerAngles( pitch + diffPitch, yaw + diffYaw, 0 ) *
189  QQuaternion::fromEulerAngles( pitch, yaw, 0 ).conjugated();
190 
191  // get camera's view vector, rotate it to get new view center
192  const QVector3D position = mCamera->position();
193  QVector3D viewCenter = mCamera->viewCenter();
194  const QVector3D viewVector = viewCenter - position;
195  const QVector3D cameraToCenter = q * viewVector;
196  viewCenter = position + cameraToCenter;
197 
198  mCameraPose.setCenterPoint( viewCenter );
199  mCameraPose.setPitchAngle( pitch + diffPitch );
200  mCameraPose.setHeadingAngle( yaw + diffYaw );
201 }
202 
203 
205 {
206  Q_UNUSED( dt )
207 }
208 
209 void QgsCameraController::resetView( float distance )
210 {
211  setViewFromTop( 0, 0, distance );
212 }
213 
214 void QgsCameraController::setViewFromTop( float worldX, float worldY, float distance, float yaw )
215 {
216  QgsCameraPose camPose;
217  if ( mTerrainEntity )
218  camPose.setCenterPoint( QgsVector3D( worldX, mTerrainEntity->terrainElevationOffset(), worldY ) );
219  else
220  camPose.setCenterPoint( QgsVector3D( worldX, 0.0f, worldY ) );
222  camPose.setHeadingAngle( yaw );
223 
224  // a basic setup to make frustum depth range long enough that it does not cull everything
225  mCamera->setNearPlane( distance / 2 );
226  mCamera->setFarPlane( distance * 2 );
227 
228  setCameraPose( camPose );
229 }
230 
232 {
233  return mCameraPose.centerPoint();
234 }
235 
236 void QgsCameraController::setLookingAtPoint( const QgsVector3D &point, float distance, float pitch, float yaw )
237 {
238  QgsCameraPose camPose;
239  camPose.setCenterPoint( point );
241  camPose.setPitchAngle( pitch );
242  camPose.setHeadingAngle( yaw );
243  setCameraPose( camPose );
244 }
245 
247 {
248  if ( camPose == mCameraPose )
249  return;
250 
251  mCameraPose = camPose;
252 
253  if ( mCamera )
254  mCameraPose.updateCamera( mCamera );
255 
256  emit cameraChanged();
257 }
258 
259 QDomElement QgsCameraController::writeXml( QDomDocument &doc ) const
260 {
261  QDomElement elemCamera = doc.createElement( QStringLiteral( "camera" ) );
262  elemCamera.setAttribute( QStringLiteral( "x" ), mCameraPose.centerPoint().x() );
263  elemCamera.setAttribute( QStringLiteral( "y" ), mCameraPose.centerPoint().z() );
264  elemCamera.setAttribute( QStringLiteral( "elev" ), mCameraPose.centerPoint().y() );
265  elemCamera.setAttribute( QStringLiteral( "dist" ), mCameraPose.distanceFromCenterPoint() );
266  elemCamera.setAttribute( QStringLiteral( "pitch" ), mCameraPose.pitchAngle() );
267  elemCamera.setAttribute( QStringLiteral( "yaw" ), mCameraPose.headingAngle() );
268  return elemCamera;
269 }
270 
271 void QgsCameraController::readXml( const QDomElement &elem )
272 {
273  const float x = elem.attribute( QStringLiteral( "x" ) ).toFloat();
274  const float y = elem.attribute( QStringLiteral( "y" ) ).toFloat();
275  const float elev = elem.attribute( QStringLiteral( "elev" ) ).toFloat();
276  const float dist = elem.attribute( QStringLiteral( "dist" ) ).toFloat();
277  const float pitch = elem.attribute( QStringLiteral( "pitch" ) ).toFloat();
278  const float yaw = elem.attribute( QStringLiteral( "yaw" ) ).toFloat();
279  setLookingAtPoint( QgsVector3D( x, elev, y ), dist, pitch, yaw );
280 }
281 
282 double QgsCameraController::cameraCenterElevation()
283 {
284  if ( std::isnan( mCameraPose.centerPoint().x() ) || std::isnan( mCameraPose.centerPoint().y() ) || std::isnan( mCameraPose.centerPoint().z() ) )
285  {
286  // something went horribly wrong but we need to at least try to fix it somehow
287  qWarning() << "camera position got NaN!";
288  return 0;
289  }
290 
291  double res = 0.0;
292 
293  if ( mCamera && mTerrainEntity )
294  {
295  // figure out our distance from terrain and update the camera's view center
296  // so that camera tilting and rotation is around a point on terrain, not an point at fixed elevation
297  QVector3D intersectionPoint;
298  QgsRayCastingUtils::Ray3D ray = QgsRayCastingUtils::rayForCameraCenter( mCamera );
299  if ( mTerrainEntity->rayIntersection( ray, intersectionPoint ) )
300  res = intersectionPoint.y();
301  else
302  res = mTerrainEntity->terrainElevationOffset();
303  }
304 
305  if ( mCamera && !mTerrainEntity )
306  res = 0.0;
307 
308  return res;
309 }
310 
311 double QgsCameraController::sampleDepthBuffer( const QImage &buffer, int px, int py )
312 {
313  double depth = 1;
314 
315  // Sample the neighbouring pixels for the closest point to the camera
316  for ( int x = px - 3; x <= px + 3; ++x )
317  {
318  for ( int y = py - 3; y <= py + 3; ++y )
319  {
320  if ( buffer.valid( x, y ) )
321  {
322  depth = std::min( depth, Qgs3DUtils::decodeDepth( buffer.pixel( x, y ) ) );
323  }
324  }
325  }
326 
327  if ( depth < 1 )
328  return depth;
329 
330  // Returns the average of depth values that are not 1 (void area)
331  depth = 0;
332  int samplesCount = 0;
333  for ( int x = 0; x < mDepthBufferImage.width(); ++x )
334  {
335  for ( int y = 0; y < mDepthBufferImage.height(); ++y )
336  {
337  double d = Qgs3DUtils::decodeDepth( buffer.pixel( x, y ) );
338  if ( d < 1 )
339  {
340  depth += d;
341  samplesCount += 1;
342  }
343  }
344  }
345 
346  // if the whole buffer is white, a depth cannot be computed
347  if ( samplesCount == 0 )
348  depth = 1.0;
349  else
350  depth /= samplesCount;
351 
352  return depth;
353 }
354 
355 void QgsCameraController::updateCameraFromPose()
356 {
357  // Some changes to be inserted
358  if ( std::isnan( mCameraPose.centerPoint().x() ) || std::isnan( mCameraPose.centerPoint().y() ) || std::isnan( mCameraPose.centerPoint().z() ) )
359  {
360  // something went horribly wrong but we need to at least try to fix it somehow
361  qWarning() << "camera position got NaN!";
362  mCameraPose.setCenterPoint( QgsVector3D( 0, 0, 0 ) );
363  }
364 
365  if ( mCameraPose.pitchAngle() > 180 )
366  mCameraPose.setPitchAngle( 180 ); // prevent going over the head
367  if ( mCameraPose.pitchAngle() < 0 )
368  mCameraPose.setPitchAngle( 0 ); // prevent going over the head
369  if ( mCameraPose.distanceFromCenterPoint() < 10 )
370  mCameraPose.setDistanceFromCenterPoint( 10 );
371 
372  if ( mCamera )
373  mCameraPose.updateCamera( mCamera );
374  emit cameraChanged();
375 }
376 
377 void QgsCameraController::moveCameraPositionBy( const QVector3D &posDiff )
378 {
379  mCameraPose.setCenterPoint( mCameraPose.centerPoint() + posDiff );
380 
381  if ( mCameraPose.pitchAngle() > 180 )
382  mCameraPose.setPitchAngle( 180 ); // prevent going over the head
383  if ( mCameraPose.pitchAngle() < 0 )
384  mCameraPose.setPitchAngle( 0 ); // prevent going over the head
385  if ( mCameraPose.distanceFromCenterPoint() < 10 )
386  mCameraPose.setDistanceFromCenterPoint( 10 );
387 
388  if ( mCamera )
389  mCameraPose.updateCamera( mCamera );
390 
391  emit cameraChanged();
392 
393 }
394 
395 void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
396 {
397  mIsInZoomInState = false;
398  mCumulatedWheelY = 0;
399  switch ( mCameraNavigationMode )
400  {
402  onPositionChangedTerrainNavigation( mouse );
403  break;
404 
405  case WalkNavigation:
406  onPositionChangedFlyNavigation( mouse );
407  break;
408  }
409 }
410 
411 void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
412 {
413  if ( mIgnoreNextMouseMove )
414  {
415  mIgnoreNextMouseMove = false;
416  mMousePos = QPoint( mouse->x(), mouse->y() );
417  return;
418  }
419 
420  const int dx = mouse->x() - mMousePos.x();
421  const int dy = mouse->y() - mMousePos.y();
422 
423  const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
424  const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
425  const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
426  const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
427  const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
428 
429  if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
430  {
431  // rotate/tilt using mouse (camera moves as it rotates around the clicked point)
432 
433  double scale = std::max( mViewport.width(), mViewport.height() );
434  float pitchDiff = 180 * ( mouse->y() - mMiddleButtonClickPos.y() ) / scale;
435  float yawDiff = -180 * ( mouse->x() - mMiddleButtonClickPos.x() ) / scale;
436 
437  if ( !mDepthBufferIsReady )
438  return;
439 
440  if ( !mRotationCenterCalculated )
441  {
442  double depth = sampleDepthBuffer( mDepthBufferImage, mMiddleButtonClickPos.x(), mMiddleButtonClickPos.y() );
443 
444  mRotationCenter = Qgs3DUtils::screenPointToWorldPos( mMiddleButtonClickPos, depth, mViewport.size(), mCameraBeforeRotation.get() );
445 
446  mRotationDistanceFromCenter = ( mRotationCenter - mCameraBeforeRotation->position() ).length();
447 
448  emit cameraRotationCenterChanged( mRotationCenter );
449  mRotationCenterCalculated = true;
450  }
451 
452  // First transformation : Shift camera position and view center and rotate the camera
453  {
454  QVector3D shiftVector = mRotationCenter - mCamera->viewCenter();
455 
456  QVector3D newViewCenterWorld = camera()->viewCenter() + shiftVector;
457  QVector3D newCameraPosition = camera()->position() + shiftVector;
458 
459  mCameraPose.setDistanceFromCenterPoint( ( newViewCenterWorld - newCameraPosition ).length() );
460  mCameraPose.setCenterPoint( newViewCenterWorld );
461  mCameraPose.setPitchAngle( mRotationPitch + pitchDiff );
462  mCameraPose.setHeadingAngle( mRotationYaw + yawDiff );
463  updateCameraFromPose();
464  }
465 
466 
467  // Second transformation : Shift camera position back
468  {
469  QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( QPoint( mMiddleButtonClickPos.x(), mMiddleButtonClickPos.y() ), mViewport.size(), mCamera );
470 
471  QVector3D clickedPositionWorld = ray.origin() + mRotationDistanceFromCenter * ray.direction();
472 
473  QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
474 
475  QVector3D newViewCenterWorld = camera()->viewCenter() - shiftVector;
476  QVector3D newCameraPosition = camera()->position() - shiftVector;
477 
478  mCameraPose.setDistanceFromCenterPoint( ( newViewCenterWorld - newCameraPosition ).length() );
479  mCameraPose.setCenterPoint( newViewCenterWorld );
480  updateCameraFromPose();
481  }
482  }
483  else if ( hasLeftButton && hasCtrl && !hasShift )
484  {
485  // rotate/tilt using mouse (camera stays at one position as it rotates)
486  const float diffPitch = 0.2f * dy;
487  const float diffYaw = - 0.2f * dx;
488  rotateCamera( diffPitch, diffYaw );
489  }
490  else if ( hasLeftButton && !hasShift && !hasCtrl )
491  {
492  // translation works as if one grabbed a point on the 3D viewer and dragged it
493 
494  if ( !mDepthBufferIsReady )
495  return;
496 
497  if ( !mDragPointCalculated )
498  {
499  double depth = sampleDepthBuffer( mDepthBufferImage, mDragButtonClickPos.x(), mDragButtonClickPos.y() );
500 
501  mDragDepth = depth;
502 
503  mDragPoint = Qgs3DUtils::screenPointToWorldPos( mDragButtonClickPos, mDragDepth, mViewport.size(), mCameraBeforeDrag.get() );
504  mDragPointCalculated = true;
505  }
506 
507  QVector3D cameraBeforeDragPos = mCameraBeforeDrag->position();
508 
509  QVector3D moveToPosition = Qgs3DUtils::screenPointToWorldPos( mMousePos, mDragDepth, mViewport.size(), mCameraBeforeDrag.get() );
510  QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBeforeDrag->position() ).normalized();
511  QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBeforeDrag->position() ).normalized();
512 
513  // Make sure the rays are not horizontal (add small y shift if it is)
514  if ( cameraBeforeToMoveToPos.y() == 0 )
515  {
516  cameraBeforeToMoveToPos.setY( 0.01 );
517  cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
518  }
519 
520  if ( cameraBeforeToDragPointPos.y() == 0 )
521  {
522  cameraBeforeToDragPointPos.setY( 0.01 );
523  cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
524  }
525 
526  double d1 = ( mDragPoint.y() - cameraBeforeDragPos.y() ) / cameraBeforeToMoveToPos.y();
527  double d2 = ( mDragPoint.y() - cameraBeforeDragPos.y() ) / cameraBeforeToDragPointPos.y();
528 
529  QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
530  QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
531 
532  QVector3D shiftVector = to - from;
533 
534  mCameraPose.setCenterPoint( mCameraBeforeDrag->viewCenter() + shiftVector );
535  updateCameraFromPose();
536  }
537  else if ( hasRightButton && !hasShift && !hasCtrl )
538  {
539  if ( !mDepthBufferIsReady )
540  return;
541 
542  if ( !mDragPointCalculated )
543  {
544  double depth = sampleDepthBuffer( mDepthBufferImage, mDragButtonClickPos.x(), mDragButtonClickPos.y() );
545 
546  mDragPoint = Qgs3DUtils::screenPointToWorldPos( mDragButtonClickPos, depth, mViewport.size(), mCameraBeforeDrag.get() );
547  mDragPointCalculated = true;
548  }
549 
550  float dist = ( mCameraBeforeDrag->position() - mDragPoint ).length();
551 
552  // Applies smoothing
553  if ( mMousePos.y() > mDragButtonClickPos.y() ) // zoom in
554  {
555  double f = ( double )( mMousePos.y() - mDragButtonClickPos.y() ) / ( double )( mViewport.height() - mDragButtonClickPos.y() );
556  f = std::max( 0.0, std::min( 1.0, f ) );
557  f = 1 - ( std::exp( -2 * f ) - 1 ) / ( std::exp( -2 ) - 1 );
558  dist = dist * f;
559  }
560  else // zoom out
561  {
562  double f = 1 - ( double )( mMousePos.y() ) / ( double )( mDragButtonClickPos.y() );
563  f = std::max( 0.0, std::min( 1.0, f ) );
564  f = ( std::exp( 2 * f ) - 1 ) / ( std::exp( 2 ) - 1 );
565  dist = dist + 2 * dist * f;
566  }
567 
568  // First transformation : Shift camera position and view center and rotate the camera
569  {
570  QVector3D shiftVector = mDragPoint - mCamera->viewCenter();
571 
572  QVector3D newViewCenterWorld = camera()->viewCenter() + shiftVector;
573 
574  mCameraPose.setDistanceFromCenterPoint( dist );
575  mCameraPose.setCenterPoint( newViewCenterWorld );
576  updateCameraFromPose();
577  }
578 
579  // Second transformation : Shift camera position back
580  {
581  QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( QPoint( mDragButtonClickPos.x(), mDragButtonClickPos.y() ), mViewport.size(), mCamera );
582  QVector3D clickedPositionWorld = ray.origin() + dist * ray.direction();
583 
584  QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
585 
586  QVector3D newViewCenterWorld = camera()->viewCenter() - shiftVector;
587  QVector3D newCameraPosition = camera()->position() - shiftVector;
588 
589  mCameraPose.setDistanceFromCenterPoint( ( newViewCenterWorld - newCameraPosition ).length() );
590  mCameraPose.setCenterPoint( newViewCenterWorld );
591  updateCameraFromPose();
592  }
593  }
594 
595  mMousePos = QPoint( mouse->x(), mouse->y() );
596  updateCameraFromPose();
597 }
598 
599 void QgsCameraController::zoom( float factor )
600 {
601  // zoom in/out
602  float dist = mCameraPose.distanceFromCenterPoint();
603  dist -= dist * factor * 0.01f;
604  mCameraPose.setDistanceFromCenterPoint( dist );
605  updateCameraFromPose();
606 }
607 
608 void QgsCameraController::handleTerrainNavigationWheelZoom()
609 {
610  if ( !mDepthBufferIsReady )
611  return;
612 
613  if ( !mZoomPointCalculated )
614  {
615  double depth = sampleDepthBuffer( mDepthBufferImage, mMousePos.x(), mMousePos.y() );
616 
617  mZoomPoint = Qgs3DUtils::screenPointToWorldPos( mMousePos, depth, mViewport.size(), mCameraBeforeZoom.get() );
618  mZoomPointCalculated = true;
619  }
620 
621  float f = mCumulatedWheelY / ( 120.0 * 24.0 );
622 
623  double dist = ( mZoomPoint - mCameraBeforeZoom->position() ).length();
624  dist -= dist * f;
625 
626  // First transformation : Shift camera position and view center and rotate the camera
627  {
628  QVector3D shiftVector = mZoomPoint - mCamera->viewCenter();
629 
630  QVector3D newViewCenterWorld = camera()->viewCenter() + shiftVector;
631 
632  mCameraPose.setDistanceFromCenterPoint( dist );
633  mCameraPose.setCenterPoint( newViewCenterWorld );
634  updateCameraFromPose();
635  }
636 
637  // Second transformation : Shift camera position back
638  {
639  QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( QPoint( mMousePos.x(), mMousePos.y() ), mViewport.size(), mCamera );
640  QVector3D clickedPositionWorld = ray.origin() + dist * ray.direction();
641 
642  QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
643 
644  QVector3D newViewCenterWorld = camera()->viewCenter() - shiftVector;
645  QVector3D newCameraPosition = camera()->position() - shiftVector;
646 
647  mCameraPose.setDistanceFromCenterPoint( ( newViewCenterWorld - newCameraPosition ).length() );
648  mCameraPose.setCenterPoint( newViewCenterWorld );
649  updateCameraFromPose();
650  }
651 }
652 
653 void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
654 {
655  switch ( mCameraNavigationMode )
656  {
658  {
659  const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
660  setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
661  break;
662  }
663 
665  {
666 
667  const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.5f : 5.f );
668 
669  // Apparently angleDelta needs to be accumulated
670  // see: https://doc.qt.io/qt-5/qwheelevent.html#angleDelta
671  mCumulatedWheelY += scaling * wheel->angleDelta().y();
672 
673  if ( !mIsInZoomInState )
674  {
675  mCameraPose.updateCamera( mCameraBeforeZoom.get() );
676 
677  mCameraBeforeZoom->setProjectionMatrix( mCamera->projectionMatrix() );
678  mCameraBeforeZoom->setNearPlane( mCamera->nearPlane() );
679  mCameraBeforeZoom->setFarPlane( mCamera->farPlane() );
680  mCameraBeforeZoom->setAspectRatio( mCamera->aspectRatio() );
681  mCameraBeforeZoom->setFieldOfView( mCamera->fieldOfView() );
682 
683  mZoomPointCalculated = false;
684  mIsInZoomInState = true;
685  mDepthBufferIsReady = false;
687  }
688  else
689  handleTerrainNavigationWheelZoom();
690 
691  break;
692  }
693  }
694 }
695 
696 void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
697 {
698  mKeyboardHandler->setFocus( true );
699  if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
700  {
701  mMousePos = QPoint( mouse->x(), mouse->y() );
702  mDragButtonClickPos = QPoint( mouse->x(), mouse->y() );
703  mPressedButton = mouse->button();
704  mMousePressed = true;
705 
706  if ( mCaptureFpsMouseMovements )
707  mIgnoreNextMouseMove = true;
708 
709  mCameraPose.updateCamera( mCameraBeforeDrag.get() );
710 
711  mCameraBeforeDrag->setProjectionMatrix( mCamera->projectionMatrix() );
712  mCameraBeforeDrag->setNearPlane( mCamera->nearPlane() );
713  mCameraBeforeDrag->setFarPlane( mCamera->farPlane() );
714  mCameraBeforeDrag->setAspectRatio( mCamera->aspectRatio() );
715  mCameraBeforeDrag->setFieldOfView( mCamera->fieldOfView() );
716 
717  mDepthBufferIsReady = false;
718  mDragPointCalculated = false;
719 
721  }
722 
723  if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton || ( ( mouse->modifiers() & Qt::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
724  {
725  mMousePos = QPoint( mouse->x(), mouse->y() );
726  mMiddleButtonClickPos = QPoint( mouse->x(), mouse->y() );
727  mPressedButton = mouse->button();
728  mMousePressed = true;
729  if ( mCaptureFpsMouseMovements )
730  mIgnoreNextMouseMove = true;
731  mDepthBufferIsReady = false;
732  mRotationCenterCalculated = false;
733 
734  mRotationPitch = mCameraPose.pitchAngle();
735  mRotationYaw = mCameraPose.headingAngle();
736 
737  mCameraPose.updateCamera( mCameraBeforeRotation.get() );
738 
739  mCameraBeforeRotation->setProjectionMatrix( mCamera->projectionMatrix() );
740  mCameraBeforeRotation->setNearPlane( mCamera->nearPlane() );
741  mCameraBeforeRotation->setFarPlane( mCamera->farPlane() );
742  mCameraBeforeRotation->setAspectRatio( mCamera->aspectRatio() );
743  mCameraBeforeRotation->setFieldOfView( mCamera->fieldOfView() );
744 
746  }
747 }
748 
749 void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
750 {
751  Q_UNUSED( mouse )
752  mPressedButton = Qt3DInput::QMouseEvent::NoButton;
753  mMousePressed = false;
754 
755  mDragPointCalculated = false;
756  mRotationCenterCalculated = false;
757 }
758 
759 void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
760 {
761  if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
762  {
763  // switch navigation mode
764  switch ( mCameraNavigationMode )
765  {
766  case NavigationMode::WalkNavigation:
767  setCameraNavigationMode( NavigationMode::TerrainBasedNavigation );
768  break;
769  case NavigationMode::TerrainBasedNavigation:
770  setCameraNavigationMode( NavigationMode::WalkNavigation );
771  break;
772  }
773  emit navigationModeHotKeyPressed( mCameraNavigationMode );
774  return;
775  }
776 
777  switch ( mCameraNavigationMode )
778  {
779  case WalkNavigation:
780  {
781  onKeyPressedFlyNavigation( event );
782  break;
783  }
784 
786  {
787  onKeyPressedTerrainNavigation( event );
788  break;
789  }
790  }
791 }
792 
793 void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
794 {
795  const bool hasShift = ( event->modifiers() & Qt::ShiftModifier );
796  const bool hasCtrl = ( event->modifiers() & Qt::ControlModifier );
797 
798  int tx = 0, ty = 0, tElev = 0;
799  switch ( event->key() )
800  {
801  case Qt::Key_Left:
802  tx -= 1;
803  break;
804  case Qt::Key_Right:
805  tx += 1;
806  break;
807 
808  case Qt::Key_Up:
809  ty += 1;
810  break;
811  case Qt::Key_Down:
812  ty -= 1;
813  break;
814 
815  case Qt::Key_PageDown:
816  tElev -= 1;
817  break;
818  case Qt::Key_PageUp:
819  tElev += 1;
820  break;
821  }
822 
823  if ( tx || ty )
824  {
825  if ( !hasShift && !hasCtrl )
826  {
827  moveView( tx, ty );
828  }
829  else if ( hasShift && !hasCtrl )
830  {
831  // rotate/tilt using keyboard (camera moves as it rotates around its view center)
834  }
835  else if ( hasCtrl && !hasShift )
836  {
837  // rotate/tilt using keyboard (camera stays at one position as it rotates)
838  const float diffPitch = ty; // down key = rotating camera down
839  const float diffYaw = -tx; // right key = rotating camera to the right
840  rotateCamera( diffPitch, diffYaw );
841  updateCameraFromPose();
842  }
843  }
844 
845  if ( tElev )
846  {
847  QgsVector3D center = mCameraPose.centerPoint();
848  center.set( center.x(), center.y() + tElev * 10, center.z() );
849  mCameraPose.setCenterPoint( center );
850  updateCameraFromPose();
851  }
852 }
853 
854 void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
855 {
856  switch ( event->key() )
857  {
858  case Qt::Key_QuoteLeft:
859  {
860  // toggle mouse lock mode
861  mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
862  mIgnoreNextMouseMove = true;
863  if ( mCaptureFpsMouseMovements )
864  {
865  qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
866  }
867  else
868  {
869  qApp->restoreOverrideCursor();
870  }
871  return;
872  }
873 
874  case Qt::Key_Escape:
875  {
876  // always exit mouse lock mode
877  if ( mCaptureFpsMouseMovements )
878  {
879  mCaptureFpsMouseMovements = false;
880  mIgnoreNextMouseMove = true;
881  qApp->restoreOverrideCursor();
882  return;
883  }
884  break;
885  }
886 
887  default:
888  break;
889  }
890 
891  if ( event->isAutoRepeat() )
892  return;
893 
894  mDepressedKeys.insert( event->key() );
895 }
896 
897 void QgsCameraController::applyFlyModeKeyMovements()
898 {
899  const QVector3D cameraUp = mCamera->upVector().normalized();
900  const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
901  const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
902 
903  QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
904 
905  // shift = "run", ctrl = "slow walk"
906  const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
907  const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
908 
909  const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
910 
911  bool changed = false;
912  if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
913  {
914  changed = true;
915  cameraPosDiff += movementSpeed * cameraLeft;
916  }
917 
918  if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
919  {
920  changed = true;
921  cameraPosDiff += - movementSpeed * cameraLeft;
922  }
923 
924  if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
925  {
926  changed = true;
927  cameraPosDiff += movementSpeed * cameraFront;
928  }
929 
930  if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
931  {
932  changed = true;
933  cameraPosDiff += - movementSpeed * cameraFront;
934  }
935 
936  // note -- vertical axis movements are slower by default then horizontal ones, as GIS projects
937  // tend to have much more limited elevation range vs ground range
938  static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
939  if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
940  {
941  changed = true;
942  cameraPosDiff += ELEVATION_MOVEMENT_SCALE * movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
943  }
944 
945  if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
946  {
947  changed = true;
948  cameraPosDiff += ELEVATION_MOVEMENT_SCALE * - movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
949  }
950 
951  if ( changed )
952  moveCameraPositionBy( cameraPosDiff );
953 }
954 
955 void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
956 {
957  const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
958  const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
959 
960  const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
961  const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
962  mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
963 
964  if ( mIgnoreNextMouseMove )
965  {
966  mIgnoreNextMouseMove = false;
967  return;
968  }
969 
970  if ( hasMiddleButton )
971  {
972  // middle button drag = pan camera in place (strafe)
973  const QVector3D cameraUp = mCamera->upVector().normalized();
974  const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
975  const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
976  const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
977  moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
978  }
979  else if ( hasRightButton )
980  {
981  // right button drag = camera dolly
982  const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
983  const QVector3D cameraPosDiff = dy * cameraFront;
984  moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
985  }
986  else
987  {
988  if ( mCaptureFpsMouseMovements )
989  {
990  float diffPitch = -0.2f * dy;
991  switch ( mVerticalAxisInversion )
992  {
993  case Always:
994  diffPitch *= -1;
995  break;
996 
997  case WhenDragging:
998  case Never:
999  break;
1000  }
1001 
1002  const float diffYaw = - 0.2f * dx;
1003  rotateCamera( diffPitch, diffYaw );
1004  updateCameraFromPose();
1005  }
1006  else if ( mouse->buttons() & Qt::LeftButton )
1007  {
1008  float diffPitch = -0.2f * dy;
1009  switch ( mVerticalAxisInversion )
1010  {
1011  case Always:
1012  case WhenDragging:
1013  diffPitch *= -1;
1014  break;
1015 
1016  case Never:
1017  break;
1018  }
1019  const float diffYaw = - 0.2f * dx;
1020  rotateCamera( diffPitch, diffYaw );
1021  updateCameraFromPose();
1022  }
1023  }
1024 
1025  if ( mCaptureFpsMouseMovements )
1026  {
1027  mIgnoreNextMouseMove = true;
1028 
1029  // reset cursor back to center of map widget
1030  emit setCursorPosition( QPoint( mViewport.width() / 2, mViewport.height() / 2 ) );
1031  }
1032 }
1033 
1034 void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
1035 {
1036  if ( event->isAutoRepeat() )
1037  return;
1038 
1039  mDepressedKeys.remove( event->key() );
1040 }
1041 
1042 void QgsCameraController::onPickerMousePressed( Qt3DRender::QPickEvent *pick )
1043 {
1044  mLastPressedHeight = pick->worldIntersection().y();
1045 }
1046 
1047 
1049 {
1050  // Tilt up the view by deltaPitch around the view center (camera moves)
1051  float pitch = mCameraPose.pitchAngle();
1052  pitch -= deltaPitch; // down key = moving camera toward terrain
1053  mCameraPose.setPitchAngle( pitch );
1054  updateCameraFromPose();
1055 }
1056 
1058 {
1059  // Rotate clockwise the view by deltaYaw around the view center (camera moves)
1060  float yaw = mCameraPose.headingAngle();
1061  yaw -= deltaYaw; // right key = moving camera clockwise
1062  mCameraPose.setHeadingAngle( yaw );
1063  updateCameraFromPose();
1064 }
1065 
1067 {
1068  mCameraPose.setHeadingAngle( angle );
1069  updateCameraFromPose();
1070 }
1071 
1072 void QgsCameraController::moveView( float tx, float ty )
1073 {
1074  const float yaw = mCameraPose.headingAngle();
1075  const float dist = mCameraPose.distanceFromCenterPoint();
1076  const float x = tx * dist * 0.02f;
1077  const float y = -ty * dist * 0.02f;
1078 
1079  // moving with keyboard - take into account yaw of camera
1080  const float t = sqrt( x * x + y * y );
1081  const float a = atan2( y, x ) - yaw * M_PI / 180;
1082  const float dx = cos( a ) * t;
1083  const float dy = sin( a ) * t;
1084 
1085  QgsVector3D center = mCameraPose.centerPoint();
1086  center.set( center.x() + dx, center.y(), center.z() + dy );
1087  mCameraPose.setCenterPoint( center );
1088  updateCameraFromPose();
1089 }
1090 
1092 {
1093  if ( event->key() == Qt::Key_QuoteLeft )
1094  return true;
1095 
1096  switch ( mCameraNavigationMode )
1097  {
1098  case WalkNavigation:
1099  {
1100  switch ( event->key() )
1101  {
1102  case Qt::Key_Left:
1103  case Qt::Key_A:
1104  case Qt::Key_Right:
1105  case Qt::Key_D:
1106  case Qt::Key_Up:
1107  case Qt::Key_W:
1108  case Qt::Key_Down:
1109  case Qt::Key_S:
1110  case Qt::Key_PageUp:
1111  case Qt::Key_E:
1112  case Qt::Key_PageDown:
1113  case Qt::Key_Q:
1114  return true;
1115 
1116  case Qt::Key_Escape:
1117  if ( mCaptureFpsMouseMovements )
1118  return true;
1119  break;
1120 
1121  default:
1122  break;
1123  }
1124  break;
1125  }
1126 
1128  {
1129  switch ( event->key() )
1130  {
1131  case Qt::Key_Left:
1132  case Qt::Key_Right:
1133  case Qt::Key_PageUp:
1134  case Qt::Key_PageDown:
1135  return true;
1136 
1137  default:
1138  break;
1139  }
1140  break;
1141  }
1142  }
1143  return false;
1144 }
1145 
1146 void QgsCameraController::depthBufferCaptured( const QImage &depthImage )
1147 {
1148  mDepthBufferImage = depthImage;
1149  mDepthBufferIsReady = true;
1150 
1151  if ( mIsInZoomInState )
1152  handleTerrainNavigationWheelZoom();
1153 }
static double decodeDepth(const QRgb &pixel)
Decodes the depth value from the pixel's color value The depth value is encoded from OpenGL side (the...
Definition: qgs3dutils.h:228
static QVector3D screenPointToWorldPos(const QPoint &screenPoint, double depth, const QSize &screenSize, Qt3DRender::QCamera *camera)
Converts the clicked mouse position to the corresponding 3D world coordinates.
Definition: qgs3dutils.cpp:647
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
Definition: qgs3dutils.cpp:621
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates.
void readXml(const QDomElement &elem)
Reads camera configuration from the given DOM element.
float pitch() const
Returns pitch angle in degrees (0 = looking from the top, 90 = looking from the side).
float yaw() const
Returns yaw angle in degrees.
void requestDepthBufferCapture()
Emitted to ask for the depth buffer image.
void setCameraNavigationMode(QgsCameraController::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
void tiltUpAroundViewCenter(float deltaPitch)
Tilt up the view by deltaPitch around the view center (camera moves)
bool willHandleKeyEvent(QKeyEvent *event)
Returns true if the camera controller will handle the specified key event, preventing it from being i...
void navigationModeHotKeyPressed(QgsCameraController::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
Qt3DRender::QCamera * camera
void setVerticalAxisInversion(QgsCameraController::VerticalAxisInversion inversion)
Sets the vertical axis inversion behavior.
float distance() const
Returns distance of the camera from the point it is looking at.
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene.
VerticalAxisInversion
Vertical axis inversion options.
@ WhenDragging
Invert vertical axis movements when dragging in first person modes.
@ Always
Always invert vertical axis movements.
@ Never
Never invert vertical axis movements.
void cameraChanged()
Emitted when camera has been updated.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates)
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
QDomElement writeXml(QDomDocument &doc) const
Writes camera configuration to the given DOM element.
void setViewFromTop(float worldX, float worldY, float distance, float yaw=0)
Sets camera to look down towards given point in world coordinate, in given distance from plane with z...
NavigationMode
The navigation mode used by the camera.
@ WalkNavigation
Uses WASD keys or arrows to navigate in walking (first person) manner.
@ TerrainBasedNavigation
The default navigation based on the terrain.
void zoom(float factor)
Zoom the map by factor.
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
void rotateAroundViewCenter(float deltaYaw)
Rotate clockwise the view by deltaYaw around the view center (camera moves)
QgsCameraController(Qt3DCore::QNode *parent=nullptr)
Constructs the camera controller with optional parent node that will take ownership.
void setCameraHeadingAngle(float angle)
Set camera heading to angle (used for rotating the view)
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
void depthBufferCaptured(const QImage &depthImage)
Sets the depth buffer image used by the camera controller to calculate world position from a pixel's ...
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
void viewportChanged()
Emitted when viewport rectangle has been updated.
void setCursorPosition(QPoint point)
Emitted when the mouse cursor position should be moved to the specified point on the map viewport.
void moveView(float tx, float ty)
Move the map by tx and ty.
float headingAngle() const
Returns heading (yaw) angle in degrees.
Definition: qgscamerapose.h:66
QgsVector3D centerPoint() const
Returns center point (towards which point the camera is looking)
Definition: qgscamerapose.h:51
float pitchAngle() const
Returns pitch angle in degrees.
Definition: qgscamerapose.h:61
float distanceFromCenterPoint() const
Returns distance of the camera from the center point.
Definition: qgscamerapose.h:56
void setPitchAngle(float pitch)
Sets pitch angle in degrees.
Definition: qgscamerapose.h:63
void setCenterPoint(const QgsVector3D &point)
Sets center point (towards which point the camera is looking)
Definition: qgscamerapose.h:53
void setHeadingAngle(float heading)
Sets heading (yaw) angle in degrees.
Definition: qgscamerapose.h:68
void setDistanceFromCenterPoint(float distance)
Sets distance of the camera from the center point.
Definition: qgscamerapose.h:58
void updateCamera(Qt3DRender::QCamera *camera)
Update Qt3D camera view matrix based on the pose.
A representation of a ray in 3D.
Definition: qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition: qgsray3d.h:44
QVector3D direction() const
Returns the direction of the ray see setDirection()
Definition: qgsray3d.h:50
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:56
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
QPointF screen_point_to_point_on_plane(QPointF pt, QRect viewport, Qt3DRender::QCamera *camera, float y)
float find_x_on_line(float x0, float y0, float x1, float y1, float y)