17#include "moc_qgscameracontroller.cpp"
26#include <QDomDocument>
27#include <Qt3DRender/QCamera>
36 , mCamera( scene->engine()->camera() )
38 , mMouseHandler( new
Qt3DInput::QMouseHandler )
39 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
40 , mOrigin( scene->mapSettings()->origin() )
42 mMouseHandler->setSourceDevice(
new Qt3DInput::QMouseDevice() );
43 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
this, &QgsCameraController::onPositionChanged );
44 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
this, &QgsCameraController::onWheel );
45 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
this, &QgsCameraController::onMousePressed );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
this, &QgsCameraController::onMouseReleased );
47 addComponent( mMouseHandler );
49 mKeyboardHandler->setSourceDevice(
new Qt3DInput::QKeyboardDevice() );
50 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
this, &QgsCameraController::onKeyPressed );
51 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
this, &QgsCameraController::onKeyReleased );
52 addComponent( mKeyboardHandler );
55 connect(
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
56 connect(
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
58 mFpsNavTimer =
new QTimer(
this );
59 mFpsNavTimer->setInterval( 10 );
60 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
61 mFpsNavTimer->start();
72QWindow *QgsCameraController::window()
const
75 return windowEngine ? windowEngine->
window() :
nullptr;
80 if ( navigationMode == mCameraNavigationMode )
83 mCameraNavigationMode = navigationMode;
84 mIgnoreNextMouseMove =
true;
90 if ( movementSpeed == mCameraMovementSpeed )
95 mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
101 mVerticalAxisInversion = inversion;
106 const float oldPitch = mCameraPose.
pitchAngle();
108 float newPitch = oldPitch + diffPitch;
109 float newHeading = oldHeading + diffHeading;
111 newPitch = std::clamp( newPitch, 0.f, 180.f );
117 const QQuaternion q = qNew * qOld.conjugated();
120 const QVector3D position = mCamera->position();
121 QVector3D viewCenter = mCamera->viewCenter();
122 const QVector3D viewVector = viewCenter - position;
123 const QVector3D cameraToCenter = q * viewVector;
124 viewCenter = position + cameraToCenter;
129 updateCameraFromPose();
134 const float oldPitch = mCameraPose.
pitchAngle();
137 newPitch = std::clamp( newPitch, 0.f, 180.f );
143 const QQuaternion q = qNew * qOld.conjugated();
145 const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
150 updateCameraFromPose();
156 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
161 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
162 QVector3D newViewCenter = newCamPosition + cameraToCenter;
166 updateCameraFromPose();
173 if ( mCameraChanged )
176 mCameraChanged =
false;
191 QgsDebugError( QStringLiteral(
"setViewFromTop() should not be used with globe!" ) );
197 const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
203 mCamera->setNearPlane(
distance / 2 );
204 mCamera->setFarPlane(
distance * 2 );
236 if ( camPose == mCameraPose && !force )
239 mCameraPose = camPose;
240 updateCameraFromPose();
245 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
246 elemCamera.setAttribute( QStringLiteral(
"x" ), mCameraPose.
centerPoint().
x() );
247 elemCamera.setAttribute( QStringLiteral(
"y" ), mCameraPose.
centerPoint().
z() );
248 elemCamera.setAttribute( QStringLiteral(
"elev" ), mCameraPose.
centerPoint().
y() );
250 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
251 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
257 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
258 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
259 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
260 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
261 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
262 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
266double QgsCameraController::sampleDepthBuffer(
int px,
int py )
270 if ( QWindow *win = window() )
274 px =
static_cast<int>( px * win->devicePixelRatio() );
275 py =
static_cast<int>( py * win->devicePixelRatio() );
279 for (
int x = px - 3; x <= px + 3; ++x )
281 for (
int y = py - 3; y <= py + 3; ++y )
283 if ( mDepthBufferImage.valid( x, y ) )
292double QgsCameraController::depthBufferNonVoidAverage()
295 if ( mDepthBufferNonVoidAverage != -1 )
296 return mDepthBufferNonVoidAverage;
300 int samplesCount = 0;
302 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
303 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
305 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
306 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
318 if ( samplesCount == 0 )
321 depth /= samplesCount;
323 mDepthBufferNonVoidAverage = depth;
328QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
333 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
334 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
340 QgsDebugError( QStringLiteral(
"moveGeocentricPoint: transform failed!" ) );
348 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
350 updateCameraFromPose();
356 updateCameraFromPose();
362 updateCameraFromPose();
368 updateCameraFromPose();
380 QgsDebugError( QStringLiteral(
"resetGlobe: transform failed!" ) );
390void QgsCameraController::updateCameraFromPose()
401 viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
405 QgsDebugError( QStringLiteral(
"updateCameraFromPose: transform failed!" ) );
415 mCameraChanged =
true;
419void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
422 updateCameraFromPose();
425void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
427 if ( !mInputHandlersEnabled )
430 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral(
"3D" ), QStringLiteral(
"QgsCameraController::onPositionChanged" ) );
432 switch ( mCameraNavigationMode )
435 onPositionChangedTerrainNavigation( mouse );
439 onPositionChangedFlyNavigation( mouse );
443 onPositionChangedGlobeTerrainNavigation( mouse );
448bool QgsCameraController::screenPointToWorldPos( QPoint position, Qt3DRender::QCamera *mCameraBefore,
double &depth, QVector3D &worldPosition )
450 depth = sampleDepthBuffer( position.x(), position.y() );
456 depth = depthBufferNonVoidAverage();
460 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
462 QgsDebugMsgLevel( QStringLiteral(
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
469void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
471 if ( mIgnoreNextMouseMove )
473 mIgnoreNextMouseMove =
false;
474 mMousePos = QPoint( mouse->x(), mouse->y() );
478 const int dx = mouse->x() - mMousePos.x();
479 const int dy = mouse->y() - mMousePos.y();
481 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
482 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
483 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
484 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
485 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
487 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
490 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
492 float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
493 float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
494 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
496 if ( !mDepthBufferIsReady )
499 if ( !mRotationCenterCalculated )
502 QVector3D worldPosition;
503 if ( screenPointToWorldPos( mClickPoint, mCameraBefore.get(), depth, worldPosition ) )
505 mRotationCenter = worldPosition;
506 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
508 mRotationCenterCalculated =
true;
514 else if ( hasLeftButton && hasCtrl && !hasShift )
516 setMouseParameters( MouseOperation::RotationCamera );
518 const float diffPitch = 0.2f * dy;
519 const float diffYaw = -0.2f * dx;
522 else if ( hasLeftButton && !hasShift && !hasCtrl )
525 setMouseParameters( MouseOperation::Translation, mMousePos );
527 if ( !mDepthBufferIsReady )
530 if ( !mDragPointCalculated )
533 QVector3D worldPosition;
534 if ( screenPointToWorldPos( mClickPoint, mCameraBefore.get(), depth, worldPosition ) )
537 mDragPoint = worldPosition;
538 mDragPointCalculated =
true;
542 QVector3D cameraBeforeDragPos = mCameraBefore->position();
545 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
546 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
549 if ( cameraBeforeToMoveToPos.z() == 0 )
551 cameraBeforeToMoveToPos.setZ( 0.01 );
552 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
555 if ( cameraBeforeToDragPointPos.z() == 0 )
557 cameraBeforeToDragPointPos.setZ( 0.01 );
558 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
561 double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
562 double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
564 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
565 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
567 QVector3D shiftVector = to - from;
569 mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
570 updateCameraFromPose();
572 else if ( hasLeftButton && hasShift && hasCtrl )
576 double tElev = mMousePos.y() - mouse->y();
577 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
579 updateCameraFromPose();
581 else if ( hasRightButton && !hasShift && !hasCtrl )
583 setMouseParameters( MouseOperation::Zoom, mMousePos );
584 if ( !mDepthBufferIsReady )
587 if ( !mDragPointCalculated )
590 QVector3D worldPosition;
591 if ( screenPointToWorldPos( mClickPoint, mCameraBefore.get(), depth, worldPosition ) )
593 mDragPoint = worldPosition;
594 mDragPointCalculated =
true;
598 float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
599 float newDist = oldDist;
602 int screenHeight = mScene->
engine()->
size().height();
603 QWindow *win = window();
606 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
607 screenHeight = win->screen()->size().height();
611 if ( mMousePos.y() > mClickPoint.y() )
613 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / (
double ) ( screenHeight - mClickPoint.y() - yOffset );
614 f = std::max( 0.0, std::min( 1.0, f ) );
615 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
616 newDist = newDist * f;
620 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / (
double ) ( mClickPoint.y() + yOffset );
621 f = std::max( 0.0, std::min( 1.0, f ) );
622 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
623 newDist = newDist + 2 * newDist * f;
626 double zoomFactor = newDist / oldDist;
630 mMousePos = QPoint( mouse->x(), mouse->y() );
633void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
635 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
636 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
637 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
638 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
640 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
642 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
644 const float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
645 const float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
646 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
650 updateCameraFromPose();
654 if ( !( mouse->buttons() & Qt::LeftButton ) )
658 setMouseParameters( MouseOperation::Translation, mMousePos );
660 if ( !mDepthBufferIsReady )
663 if ( !mDragPointCalculated )
666 QVector3D worldPosition;
667 if ( !screenPointToWorldPos( mClickPoint, mCameraBefore.get(), depth, worldPosition ) )
671 mDragPoint = worldPosition;
672 mDragPointCalculated =
true;
677 double newDepth = sampleDepthBuffer( mouse->x(), mouse->y() );
682 if ( !std::isfinite( newWorldPosition.x() ) || !std::isfinite( newWorldPosition.y() ) || !std::isfinite( newWorldPosition.z() ) )
693 oldLatLon = mGlobeCrsToLatLon.
transform( mapPressPos );
694 newLatLon = mGlobeCrsToLatLon.
transform( newMapPos );
698 QgsDebugError( QStringLiteral(
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
702 const double latDiff = oldLatLon.
y() - newLatLon.
y();
703 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
705 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
708 updateCameraFromPose();
716 dist -= dist * factor * 0.01f;
718 updateCameraFromPose();
721void QgsCameraController::handleTerrainNavigationWheelZoom()
723 if ( !mDepthBufferIsReady )
726 if ( !mZoomPointCalculated )
729 QVector3D worldPosition;
730 if ( screenPointToWorldPos( mMousePos, mCameraBefore.get(), depth, worldPosition ) )
732 mZoomPoint = worldPosition;
733 mZoomPointCalculated =
true;
737 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
739 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
741 newDist = std::max( newDist, 2.0 );
742 double zoomFactor = newDist / oldDist;
744 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
748 mCumulatedWheelY = 0;
749 setMouseParameters( MouseOperation::None );
752void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
754 if ( !mInputHandlersEnabled )
757 switch ( mCameraNavigationMode )
761 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
762 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
770 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1 : 1.0 );
774 mCumulatedWheelY += scaling * wheel->angleDelta().y();
776 if ( mCurrentOperation != MouseOperation::ZoomWheel )
778 setMouseParameters( MouseOperation::ZoomWheel );
783 handleTerrainNavigationWheelZoom();
790 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
791 float factor = abs( wheelAmount ) / 1000.f;
792 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
794 updateCameraFromPose();
800void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
802 if ( !mInputHandlersEnabled )
805 mKeyboardHandler->setFocus(
true );
807 if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton || ( ( mouse->modifiers() & Qt::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) || ( ( mouse->modifiers() & Qt::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
809 mMousePos = QPoint( mouse->x(), mouse->y() );
811 if ( mCaptureFpsMouseMovements )
812 mIgnoreNextMouseMove =
true;
814 const MouseOperation operation {
815 ( mouse->modifiers() & Qt::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
817 setMouseParameters( operation, mMousePos );
820 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
822 mMousePos = QPoint( mouse->x(), mouse->y() );
824 if ( mCaptureFpsMouseMovements )
825 mIgnoreNextMouseMove =
true;
827 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
828 setMouseParameters( operation, mMousePos );
832void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
835 if ( !mInputHandlersEnabled )
839 setMouseParameters( MouseOperation::None );
842void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
844 if ( !mInputHandlersEnabled )
847 if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
850 switch ( mCameraNavigationMode )
863 switch ( mCameraNavigationMode )
867 onKeyPressedFlyNavigation( event );
873 onKeyPressedTerrainNavigation( event );
879 onKeyPressedGlobeTerrainNavigation( event );
885void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
887 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
888 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
890 int tx = 0, ty = 0, tElev = 0;
891 switch ( event->key() )
907 case Qt::Key_PageDown:
917 if ( !hasShift && !hasCtrl )
921 else if ( hasShift && !hasCtrl )
927 else if ( hasCtrl && !hasShift )
930 const float diffPitch = ty;
931 const float diffYaw = -tx;
939 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
941 updateCameraFromPose();
945void QgsCameraController::onKeyPressedGlobeTerrainNavigation( Qt3DInput::QKeyEvent *event )
949 constexpr float MOVE_FACTOR = 0.000001f;
950 constexpr float ZOOM_FACTOR = 0.9f;
952 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
954 switch ( event->key() )
980 case Qt::Key_PageDown:
991void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
993 switch ( event->key() )
995 case Qt::Key_QuoteLeft:
998 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
999 mIgnoreNextMouseMove =
true;
1000 if ( mCaptureFpsMouseMovements )
1002 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1006 qApp->restoreOverrideCursor();
1011 case Qt::Key_Escape:
1014 if ( mCaptureFpsMouseMovements )
1016 mCaptureFpsMouseMovements =
false;
1017 mIgnoreNextMouseMove =
true;
1018 qApp->restoreOverrideCursor();
1028 if ( event->isAutoRepeat() )
1031 mDepressedKeys.insert( event->key() );
1036 const QVector3D cameraUp = mCamera->upVector().normalized();
1038 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1040 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1044 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1048 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1052 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1055 moveCameraPositionBy( cameraPosDiff );
1058void QgsCameraController::applyFlyModeKeyMovements()
1061 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1062 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1064 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1066 bool changed =
false;
1070 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1076 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1082 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1088 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1096 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1097 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1100 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1103 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1106 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1113void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1115 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1116 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1118 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1119 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1120 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1122 if ( mIgnoreNextMouseMove )
1124 mIgnoreNextMouseMove =
false;
1128 if ( hasMiddleButton )
1131 const QVector3D cameraUp = mCamera->upVector().normalized();
1133 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1134 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1135 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
1137 else if ( hasRightButton )
1141 const QVector3D cameraPosDiff = dy * cameraFront;
1142 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
1146 if ( mCaptureFpsMouseMovements )
1148 float diffPitch = -0.2f * dy;
1149 switch ( mVerticalAxisInversion )
1160 const float diffYaw = -0.2f * dx;
1163 else if ( mouse->buttons() & Qt::LeftButton )
1165 float diffPitch = -0.2f * dy;
1166 switch ( mVerticalAxisInversion )
1176 const float diffYaw = -0.2f * dx;
1181 if ( mCaptureFpsMouseMovements )
1183 mIgnoreNextMouseMove =
true;
1190void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
1192 if ( !mInputHandlersEnabled )
1195 if ( event->isAutoRepeat() )
1198 mDepressedKeys.remove( event->key() );
1205 pitch -= deltaPitch;
1207 updateCameraFromPose();
1216 updateCameraFromPose();
1222 updateCameraFromPose();
1229 const float x = tx * dist * 0.02f;
1230 const float y = -ty * dist * 0.02f;
1233 const float t = sqrt( x * x + y * y );
1234 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1235 const float dx = cos( a ) * t;
1236 const float dy = sin( a ) * t;
1239 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1241 updateCameraFromPose();
1246 if ( event->key() == Qt::Key_QuoteLeft )
1249 switch ( mCameraNavigationMode )
1253 switch ( event->key() )
1263 case Qt::Key_PageUp:
1265 case Qt::Key_PageDown:
1269 case Qt::Key_Escape:
1270 if ( mCaptureFpsMouseMovements )
1282 switch ( event->key() )
1288 case Qt::Key_PageUp:
1289 case Qt::Key_PageDown:
1300 switch ( event->key() )
1306 case Qt::Key_PageUp:
1307 case Qt::Key_PageDown:
1321 mDepthBufferImage = depthImage;
1322 mDepthBufferIsReady =
true;
1323 mDepthBufferNonVoidAverage = -1;
1325 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1327 handleTerrainNavigationWheelZoom();
1331bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1333 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1336void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1338 if ( newOperation == mCurrentOperation )
1343 if ( newOperation == MouseOperation::None )
1345 mClickPoint = QPoint();
1353 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1355 mClickPoint = clickPoint;
1359 mCurrentOperation = newOperation;
1360 mDepthBufferIsReady =
false;
1361 mRotationCenterCalculated =
false;
1362 mDragPointCalculated =
false;
1363 mZoomPointCalculated =
false;
1365 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1367 mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
1371 mCameraBefore->setPosition( mCamera->position() );
1372 mCameraBefore->setViewCenter( mCamera->viewCenter() );
1373 mCameraBefore->setUpVector( mCamera->upVector() );
1374 mCameraBefore->setProjectionMatrix( mCamera->projectionMatrix() );
1375 mCameraBefore->setNearPlane( mCamera->nearPlane() );
1376 mCameraBefore->setFarPlane( mCamera->farPlane() );
1377 mCameraBefore->setAspectRatio( mCamera->aspectRatio() );
1378 mCameraBefore->setFieldOfView( mCamera->fieldOfView() );
1388 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1389 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1390 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1391 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1395 updateCameraFromPose();
Provides global constants and enumerations for use throughout the application.
VerticalAxisInversion
Vertical axis inversion options for 3D views.
@ Always
Always invert vertical axis movements.
@ Never
Never invert vertical axis movements.
@ WhenDragging
Invert vertical axis movements when dragging in first person modes.
NavigationMode
The navigation mode used by 3D cameras.
@ TerrainBased
The default navigation based on the terrain.
@ Walk
Uses WASD keys or arrows to navigate in walking (first person) manner.
@ GlobeTerrainBased
Navigation similar to TerrainBased, but for use with globe.
@ Globe
Scene is represented as a globe using a geocentric CRS.
@ Reverse
Reverse/inverse transform (from destination to source)
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
Qgs3DMapSettings * mapSettings() const
Returns the 3D map settings.
QgsAbstract3DEngine * engine() const
Returns the abstract 3D engine.
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be nullptr if using globe scene, terrain rendering is disabled or when te...
Qgis::NavigationMode cameraNavigationMode() const
Returns the navigation mode used by the camera.
Qgis::SceneMode sceneMode() const
Returns mode of the 3D scene - whether it is represented as a globe (when using Geocentric CRS such a...
QgsRectangle extent() const
Returns the 3D scene's 2D extent in the 3D scene's CRS.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0).
static QQuaternion rotationFromPitchHeadingAngles(float pitchAngle, float headingAngle)
Returns rotation quaternion that performs rotation around X axis by pitchAngle, followed by rotation ...
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...
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.
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
void navigationModeChanged(Qgis::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
void setLookingAtMapPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets camera configuration like setLookingAtPoint(), but the point is given in map 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).
~QgsCameraController() override
float yaw() const
Returns yaw angle in degrees.
void requestDepthBufferCapture()
Emitted to ask for the depth buffer image.
void tiltUpAroundViewCenter(float deltaPitch)
Tilt up the view by deltaPitch around the view center (camera moves)
void globeMoveCenterPoint(double latDiff, double lonDiff)
Orbits camera around the globe by the specified amount given as the difference in latitude/longitude ...
void setVerticalAxisInversion(Qgis::VerticalAxisInversion inversion)
Sets the vertical axis inversion behavior.
bool willHandleKeyEvent(QKeyEvent *event)
Returns true if the camera controller will handle the specified key event, preventing it from being i...
const QgsVector3D origin() const
Returns the origin of the scene in map coordinates.
float distance() const
Returns distance of the camera from the point it is looking at.
void globeUpdatePitchAngle(float angleDiff)
Updates pitch angle by the specified amount given as the angular difference in degrees.
void zoomCameraAroundPivot(const QVector3D &oldCameraPosition, double zoomFactor, const QVector3D &pivotPoint)
Zooms camera by given zoom factor (>1 one means zoom in) while keeping the pivot point (given in worl...
void globeZoom(float factor)
Moves camera closer or further away from the globe.
void rotateCamera(float diffPitch, float diffYaw)
Rotates the camera on itself.
void setCameraNavigationMode(Qgis::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
void cameraChanged()
Emitted when camera has been updated.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
QgsCameraController(Qgs3DMapScene *scene)
Constructs the camera controller with optional parent node that will take ownership.
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.
QgsVector3D lookingAtMapPoint() const
Returns the point in the map 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...
void resetGlobe(float distance, double lat=0, double lon=0)
Resets view of the globe to look at a particular location given as latitude and longitude (in degrees...
void zoom(float factor)
Zoom the map by factor.
void globeUpdateHeadingAngle(float angleDiff)
Updates heading angle by the specified amount given as the angular difference in degrees.
void rotateAroundViewCenter(float deltaYaw)
Rotate clockwise the view by deltaYaw around the view center (camera moves)
void walkView(double tx, double ty, double tz)
Walks into the map by tx, ty, and tz.
void setCameraPose(const QgsCameraPose &camPose, bool force=false)
Sets camera pose.
void setOrigin(const QgsVector3D &origin)
Reacts to the shift of origin of the scene, updating camera pose and any other member variables so th...
void setCameraHeadingAngle(float angle)
Set camera heading to angle (used for rotating the view)
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void rotateCameraAroundPivot(float newPitch, float newHeading, const QVector3D &pivotPoint)
Rotates the camera around the pivot point (in world coordinates) to the given new pitch and heading a...
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 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.
Encapsulates camera pose in a 3D scene.
float headingAngle() const
Returns heading (yaw) angle in degrees.
QgsVector3D centerPoint() const
Returns center point (towards which point the camera is looking)
float pitchAngle() const
Returns pitch angle in degrees.
void updateCameraGlobe(Qt3DRender::QCamera *camera, double lat, double lon)
Updates camera when using a globe scene.
float distanceFromCenterPoint() const
Returns distance of the camera from the center point.
void setPitchAngle(float pitch)
Sets pitch angle in degrees.
void setCenterPoint(const QgsVector3D &point)
Sets center point (towards which point the camera is looking)
void setHeadingAngle(float heading)
Sets heading (yaw) angle in degrees.
void setDistanceFromCenterPoint(float distance)
Sets distance of the camera from the center point.
void updateCamera(Qt3DRender::QCamera *camera)
Update Qt3D camera view matrix based on the pose.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
Custom exception class for Coordinate Reference System related exceptions.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
void setX(double x)
Sets X coordinate.
void set(double x, double y, double z)
Sets vector coordinates.
void setY(double y)
Sets Y coordinate.
On-screen 3D engine: it creates an OpenGL window (QWindow) and displays rendered 3D scenes there.
QWindow * window()
Returns the internal 3D window where all the rendered output is displayed.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)