17#include "moc_qgscameracontroller.cpp"
26#include <QDomDocument>
27#include <Qt3DRender/QCamera>
29#include <QStringLiteral>
37 , mCamera( scene->engine()->camera() )
39 , mMouseHandler( new
Qt3DInput::QMouseHandler )
40 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
41 , mOrigin( scene->mapSettings()->origin() )
43 mMouseHandler->setSourceDevice(
new Qt3DInput::QMouseDevice() );
44 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
this, &QgsCameraController::onPositionChanged );
45 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
this, &QgsCameraController::onWheel );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
this, &QgsCameraController::onMousePressed );
47 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
this, &QgsCameraController::onMouseReleased );
48 addComponent( mMouseHandler );
50 mKeyboardHandler->setSourceDevice(
new Qt3DInput::QKeyboardDevice() );
51 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
this, &QgsCameraController::onKeyPressed );
52 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
this, &QgsCameraController::onKeyReleased );
53 addComponent( mKeyboardHandler );
56 connect(
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
57 connect(
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
59 mFpsNavTimer =
new QTimer(
this );
60 mFpsNavTimer->setInterval( 10 );
61 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
62 mFpsNavTimer->start();
73QWindow *QgsCameraController::window()
const
76 return windowEngine ? windowEngine->
window() :
nullptr;
81 if ( navigationMode == mCameraNavigationMode )
84 mCameraNavigationMode = navigationMode;
85 mIgnoreNextMouseMove =
true;
91 if ( movementSpeed == mCameraMovementSpeed )
96 mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
102 mVerticalAxisInversion = inversion;
107 const float oldPitch = mCameraPose.
pitchAngle();
109 float newPitch = oldPitch + diffPitch;
110 float newHeading = oldHeading + diffHeading;
112 newPitch = std::clamp( newPitch, 0.f, 180.f );
118 const QQuaternion q = qNew * qOld.conjugated();
121 const QVector3D position = mCamera->position();
122 QVector3D viewCenter = mCamera->viewCenter();
123 const QVector3D viewVector = viewCenter - position;
124 const QVector3D cameraToCenter = q * viewVector;
125 viewCenter = position + cameraToCenter;
130 updateCameraFromPose();
135 const float oldPitch = mCameraPose.
pitchAngle();
138 newPitch = std::clamp( newPitch, 0.f, 180.f );
144 const QQuaternion q = qNew * qOld.conjugated();
146 const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
151 updateCameraFromPose();
157 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
162 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
163 QVector3D newViewCenter = newCamPosition + cameraToCenter;
167 updateCameraFromPose();
174 if ( mCameraChanged )
177 mCameraChanged =
false;
192 QgsDebugError( QStringLiteral(
"setViewFromTop() should not be used with globe!" ) );
198 const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
204 mCamera->setNearPlane(
distance / 2 );
205 mCamera->setFarPlane(
distance * 2 );
237 if ( camPose == mCameraPose && !force )
240 mCameraPose = camPose;
241 updateCameraFromPose();
246 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
259 elemCamera.setAttribute( QStringLiteral(
"x" ), centerPoint.
x() );
260 elemCamera.setAttribute( QStringLiteral(
"y" ), centerPoint.
z() );
261 elemCamera.setAttribute( QStringLiteral(
"elev" ), centerPoint.
y() );
263 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
264 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
270 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
271 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
272 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
273 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
274 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
275 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
278 centerPoint = centerPoint - mOrigin;
282double QgsCameraController::sampleDepthBuffer(
int px,
int py )
284 if ( !mDepthBufferIsReady )
286 QgsDebugError( QStringLiteral(
"Asked to sample depth buffer, but depth buffer not ready!" ) );
291 if ( QWindow *win = window() )
295 px =
static_cast<int>( px * win->devicePixelRatio() );
296 py =
static_cast<int>( py * win->devicePixelRatio() );
300 for (
int x = px - 3; x <= px + 3; ++x )
302 for (
int y = py - 3; y <= py + 3; ++y )
304 if ( mDepthBufferImage.valid( x, y ) )
313double QgsCameraController::depthBufferNonVoidAverage()
316 if ( mDepthBufferNonVoidAverage != -1 )
317 return mDepthBufferNonVoidAverage;
321 int samplesCount = 0;
323 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
324 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
326 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
327 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
339 if ( samplesCount == 0 )
342 depth /= samplesCount;
344 mDepthBufferNonVoidAverage = depth;
349QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
354 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
355 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
361 QgsDebugError( QStringLiteral(
"moveGeocentricPoint: transform failed!" ) );
369 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
371 updateCameraFromPose();
377 updateCameraFromPose();
383 updateCameraFromPose();
389 updateCameraFromPose();
401 QgsDebugError( QStringLiteral(
"resetGlobe: transform failed!" ) );
411void QgsCameraController::updateCameraFromPose()
422 viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
426 QgsDebugError( QStringLiteral(
"updateCameraFromPose: transform failed!" ) );
436 mCameraChanged =
true;
440void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
443 updateCameraFromPose();
446void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
448 if ( !mInputHandlersEnabled )
451 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral(
"3D" ), QStringLiteral(
"QgsCameraController::onPositionChanged" ) );
453 switch ( mCameraNavigationMode )
456 onPositionChangedTerrainNavigation( mouse );
460 onPositionChangedFlyNavigation( mouse );
464 onPositionChangedGlobeTerrainNavigation( mouse );
469bool QgsCameraController::screenPointToWorldPos( QPoint position,
double &depth, QVector3D &worldPosition )
471 depth = sampleDepthBuffer( position.x(), position.y() );
477 depth = depthBufferNonVoidAverage();
481 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
483 QgsDebugMsgLevel( QStringLiteral(
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
490void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
492 if ( mIgnoreNextMouseMove )
494 mIgnoreNextMouseMove =
false;
495 mMousePos = QPoint( mouse->x(), mouse->y() );
499 const int dx = mouse->x() - mMousePos.x();
500 const int dy = mouse->y() - mMousePos.y();
502 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
503 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
504 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
505 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
506 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
508 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
511 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
513 float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
514 float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
515 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
517 if ( !mDepthBufferIsReady )
520 if ( !mRotationCenterCalculated )
523 QVector3D worldPosition;
524 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
526 mRotationCenter = worldPosition;
527 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
529 mRotationCenterCalculated =
true;
535 else if ( hasLeftButton && hasCtrl && !hasShift )
537 setMouseParameters( MouseOperation::RotationCamera );
539 const float diffPitch = 0.2f * dy;
540 const float diffYaw = -0.2f * dx;
543 else if ( hasLeftButton && !hasShift && !hasCtrl )
546 setMouseParameters( MouseOperation::Translation, mMousePos );
548 if ( !mDepthBufferIsReady )
551 if ( !mDragPointCalculated )
554 QVector3D worldPosition;
555 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
558 mDragPoint = worldPosition;
559 mDragPointCalculated =
true;
563 QVector3D cameraBeforeDragPos = mCameraBefore->position();
566 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
567 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
570 if ( cameraBeforeToMoveToPos.z() == 0 )
572 cameraBeforeToMoveToPos.setZ( 0.01 );
573 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
576 if ( cameraBeforeToDragPointPos.z() == 0 )
578 cameraBeforeToDragPointPos.setZ( 0.01 );
579 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
582 double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
583 double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
585 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
586 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
588 QVector3D shiftVector = to - from;
590 mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
591 updateCameraFromPose();
593 else if ( hasLeftButton && hasShift && hasCtrl )
597 double tElev = mMousePos.y() - mouse->y();
598 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
600 updateCameraFromPose();
602 else if ( hasRightButton && !hasShift && !hasCtrl )
604 setMouseParameters( MouseOperation::Zoom, mMousePos );
605 if ( !mDepthBufferIsReady )
608 if ( !mDragPointCalculated )
611 QVector3D worldPosition;
612 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
614 mDragPoint = worldPosition;
615 mDragPointCalculated =
true;
619 float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
620 float newDist = oldDist;
623 int screenHeight = mScene->
engine()->
size().height();
624 QWindow *win = window();
627 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
628 screenHeight = win->screen()->size().height();
632 if ( mMousePos.y() > mClickPoint.y() )
634 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / (
double ) ( screenHeight - mClickPoint.y() - yOffset );
635 f = std::max( 0.0, std::min( 1.0, f ) );
636 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
637 newDist = newDist * f;
641 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / (
double ) ( mClickPoint.y() + yOffset );
642 f = std::max( 0.0, std::min( 1.0, f ) );
643 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
644 newDist = newDist + 2 * newDist * f;
647 double zoomFactor = newDist / oldDist;
651 mMousePos = QPoint( mouse->x(), mouse->y() );
654void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
656 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
657 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
658 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
659 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
661 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
663 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
665 const float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
666 const float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
667 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
671 updateCameraFromPose();
675 if ( !( mouse->buttons() & Qt::LeftButton ) )
679 setMouseParameters( MouseOperation::Translation, mMousePos );
681 if ( !mDepthBufferIsReady )
684 if ( !mDragPointCalculated )
687 QVector3D worldPosition;
688 if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
692 mDragPoint = worldPosition;
693 mDragPointCalculated =
true;
699 const double sphereRadiusMap = startPosMap.
length();
706 const double quadC =
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
707 const double disc = quadB * quadB - 4 * quadA * quadC;
712 const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
713 if ( rayDistMap < 0 )
715 QgsDebugError( QStringLiteral(
"Sphere intersection result negative, cancelling move" ) );
726 oldLatLon = mGlobeCrsToLatLon.
transform( startPosMap );
727 newLatLon = mGlobeCrsToLatLon.
transform( newPosMap );
731 QgsDebugError( QStringLiteral(
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
735 const double latDiff = oldLatLon.
y() - newLatLon.
y();
736 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
738 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
742 updateCameraFromPose();
750 dist -= dist * factor * 0.01f;
752 updateCameraFromPose();
755void QgsCameraController::handleTerrainNavigationWheelZoom()
757 if ( !mDepthBufferIsReady )
760 if ( !mZoomPointCalculated )
763 QVector3D worldPosition;
764 if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
766 mZoomPoint = worldPosition;
767 mZoomPointCalculated =
true;
771 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
773 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
775 newDist = std::max( newDist, 2.0 );
776 double zoomFactor = newDist / oldDist;
778 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
782 mCumulatedWheelY = 0;
783 setMouseParameters( MouseOperation::None );
786void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
788 if ( !mInputHandlersEnabled )
791 switch ( mCameraNavigationMode )
795 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
796 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
804 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1 : 1.0 );
808 mCumulatedWheelY += scaling * wheel->angleDelta().y();
810 if ( mCurrentOperation != MouseOperation::ZoomWheel )
812 setMouseParameters( MouseOperation::ZoomWheel );
817 handleTerrainNavigationWheelZoom();
824 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
825 float factor = abs( wheelAmount ) / 1000.f;
826 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
828 updateCameraFromPose();
834void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
836 if ( !mInputHandlersEnabled )
839 mKeyboardHandler->setFocus(
true );
841 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 ) )
843 mMousePos = QPoint( mouse->x(), mouse->y() );
845 if ( mCaptureFpsMouseMovements )
846 mIgnoreNextMouseMove =
true;
848 const MouseOperation operation {
849 ( mouse->modifiers() & Qt::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
851 setMouseParameters( operation, mMousePos );
854 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
856 mMousePos = QPoint( mouse->x(), mouse->y() );
858 if ( mCaptureFpsMouseMovements )
859 mIgnoreNextMouseMove =
true;
861 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
862 setMouseParameters( operation, mMousePos );
866void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
869 if ( !mInputHandlersEnabled )
873 setMouseParameters( MouseOperation::None );
876void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
878 if ( !mInputHandlersEnabled )
881 if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
884 switch ( mCameraNavigationMode )
897 switch ( mCameraNavigationMode )
901 onKeyPressedFlyNavigation( event );
907 onKeyPressedTerrainNavigation( event );
913 onKeyPressedGlobeTerrainNavigation( event );
919void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
921 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
922 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
924 int tx = 0, ty = 0, tElev = 0;
925 switch ( event->key() )
941 case Qt::Key_PageDown:
951 if ( !hasShift && !hasCtrl )
955 else if ( hasShift && !hasCtrl )
961 else if ( hasCtrl && !hasShift )
964 const float diffPitch = ty;
965 const float diffYaw = -tx;
973 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
975 updateCameraFromPose();
979void QgsCameraController::onKeyPressedGlobeTerrainNavigation( Qt3DInput::QKeyEvent *event )
983 constexpr float MOVE_FACTOR = 0.000001f;
984 constexpr float ZOOM_FACTOR = 0.9f;
986 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
988 switch ( event->key() )
1014 case Qt::Key_PageDown:
1017 case Qt::Key_PageUp:
1025void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
1027 switch ( event->key() )
1029 case Qt::Key_QuoteLeft:
1032 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
1033 mIgnoreNextMouseMove =
true;
1034 if ( mCaptureFpsMouseMovements )
1036 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1040 qApp->restoreOverrideCursor();
1045 case Qt::Key_Escape:
1048 if ( mCaptureFpsMouseMovements )
1050 mCaptureFpsMouseMovements =
false;
1051 mIgnoreNextMouseMove =
true;
1052 qApp->restoreOverrideCursor();
1062 if ( event->isAutoRepeat() )
1065 mDepressedKeys.insert( event->key() );
1070 const QVector3D cameraUp = mCamera->upVector().normalized();
1072 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1074 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1078 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1082 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1086 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1089 moveCameraPositionBy( cameraPosDiff );
1092void QgsCameraController::applyFlyModeKeyMovements()
1095 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1096 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1098 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1100 bool changed =
false;
1104 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1110 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1116 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1122 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1130 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1131 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1134 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1137 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1140 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1147void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1149 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1150 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1152 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1153 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1154 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1156 if ( mIgnoreNextMouseMove )
1158 mIgnoreNextMouseMove =
false;
1162 if ( hasMiddleButton )
1165 const QVector3D cameraUp = mCamera->upVector().normalized();
1167 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1168 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1169 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
1171 else if ( hasRightButton )
1175 const QVector3D cameraPosDiff = dy * cameraFront;
1176 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
1180 if ( mCaptureFpsMouseMovements )
1182 float diffPitch = -0.2f * dy;
1183 switch ( mVerticalAxisInversion )
1194 const float diffYaw = -0.2f * dx;
1197 else if ( mouse->buttons() & Qt::LeftButton )
1199 float diffPitch = -0.2f * dy;
1200 switch ( mVerticalAxisInversion )
1210 const float diffYaw = -0.2f * dx;
1215 if ( mCaptureFpsMouseMovements )
1217 mIgnoreNextMouseMove =
true;
1224void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
1226 if ( !mInputHandlersEnabled )
1229 if ( event->isAutoRepeat() )
1232 mDepressedKeys.remove( event->key() );
1239 pitch -= deltaPitch;
1241 updateCameraFromPose();
1250 updateCameraFromPose();
1256 updateCameraFromPose();
1263 const float x = tx * dist * 0.02f;
1264 const float y = -ty * dist * 0.02f;
1267 const float t = sqrt( x * x + y * y );
1268 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1269 const float dx = cos( a ) * t;
1270 const float dy = sin( a ) * t;
1273 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1275 updateCameraFromPose();
1280 if ( event->key() == Qt::Key_QuoteLeft )
1283 switch ( mCameraNavigationMode )
1287 switch ( event->key() )
1297 case Qt::Key_PageUp:
1299 case Qt::Key_PageDown:
1303 case Qt::Key_Escape:
1304 if ( mCaptureFpsMouseMovements )
1316 switch ( event->key() )
1322 case Qt::Key_PageUp:
1323 case Qt::Key_PageDown:
1334 switch ( event->key() )
1340 case Qt::Key_PageUp:
1341 case Qt::Key_PageDown:
1355 mDepthBufferImage = depthImage;
1356 mDepthBufferIsReady =
true;
1357 mDepthBufferNonVoidAverage = -1;
1365 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1367 handleTerrainNavigationWheelZoom();
1371bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1373 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1376void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1378 if ( newOperation == mCurrentOperation )
1383 if ( newOperation == MouseOperation::None )
1385 mClickPoint = QPoint();
1393 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1395 mClickPoint = clickPoint;
1399 mCurrentOperation = newOperation;
1400 mDepthBufferIsReady =
false;
1401 mRotationCenterCalculated =
false;
1402 mDragPointCalculated =
false;
1403 mZoomPointCalculated =
false;
1405 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1407 mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
1420 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1421 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1422 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1423 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1427 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.
@ Local
Local scene based on a projected 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 std::unique_ptr< Qt3DRender::QCamera > copyCamera(Qt3DRender::QCamera *cam)
Returns new camera object with copied properties.
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.
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.
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 representation of a ray in 3D.
QVector3D origin() const
Returns the origin of the ray.
QVector3D direction() const
Returns the direction of the ray see setDirection()
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.
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
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.
double length() const
Returns the length of the vector.
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)