17#include "moc_qgscameracontroller.cpp"
27#include <QDomDocument>
28#include <Qt3DRender/QCamera>
30#include <QStringLiteral>
39 , mCamera( scene->engine()->camera() )
41 , mMouseHandler( new
Qt3DInput::QMouseHandler )
42 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
43 , mOrigin( scene->mapSettings()->origin() )
45 mMouseHandler->setSourceDevice(
new Qt3DInput::QMouseDevice() );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
this, &QgsCameraController::onPositionChanged );
47 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
this, &QgsCameraController::onWheel );
48 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
this, &QgsCameraController::onMousePressed );
49 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
this, &QgsCameraController::onMouseReleased );
50 addComponent( mMouseHandler );
53 connect(
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
54 connect(
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
56 mFpsNavTimer =
new QTimer(
this );
57 mFpsNavTimer->setInterval( 10 );
58 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
59 mFpsNavTimer->start();
70QWindow *QgsCameraController::window()
const
73 return windowEngine ? windowEngine->
window() :
nullptr;
78 if ( navigationMode == mCameraNavigationMode )
81 mCameraNavigationMode = navigationMode;
82 mIgnoreNextMouseMove =
true;
88 if ( movementSpeed == mCameraMovementSpeed )
93 mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
99 mVerticalAxisInversion = inversion;
104 float newPitch = mCameraPose.
pitchAngle() + diffPitch;
105 float newHeading = mCameraPose.
headingAngle() + diffHeading;
107 newPitch = std::clamp( newPitch, 0.f, 180.f );
124 QgsDebugError( QStringLiteral(
"rotateCamera: ECEF -> lat,lon transform failed!" ) );
127 QQuaternion qLatLon = QQuaternion::fromAxisAndAngle( QVector3D( 0, 0, 1 ),
static_cast<float>( viewCenterLatLon.
x() ) )
128 * QQuaternion::fromAxisAndAngle( QVector3D( 0, -1, 0 ),
static_cast<float>( viewCenterLatLon.
y() ) );
129 QQuaternion qPitchHeading = QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), newHeading )
130 * QQuaternion::fromAxisAndAngle( QVector3D( 0, 1, 0 ), newPitch );
131 QVector3D newCameraToCenter = ( qLatLon * qPitchHeading * QVector3D( -1, 0, 0 ) ) * mCameraPose.
distanceFromCenterPoint();
133 mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
136 updateCameraFromPose();
144 mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
147 updateCameraFromPose();
155 const float oldPitch = mCameraPose.
pitchAngle();
158 newPitch = std::clamp( newPitch, 0.f, 180.f );
164 const QQuaternion q = qNew * qOld.conjugated();
166 const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
171 updateCameraFromPose();
177 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
182 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
183 QVector3D newViewCenter = newCamPosition + cameraToCenter;
187 updateCameraFromPose();
194 if ( mCameraChanged )
197 mCameraChanged =
false;
212 QgsDebugError( QStringLiteral(
"setViewFromTop() should not be used with globe!" ) );
218 const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
224 mCamera->setNearPlane(
distance / 2 );
225 mCamera->setFarPlane(
distance * 2 );
257 if ( camPose == mCameraPose && !force )
260 mCameraPose = camPose;
261 updateCameraFromPose();
266 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
270 elemCamera.setAttribute( QStringLiteral(
"xMap" ), centerPoint.
x() );
271 elemCamera.setAttribute( QStringLiteral(
"yMap" ), centerPoint.
y() );
272 elemCamera.setAttribute( QStringLiteral(
"zMap" ), centerPoint.
z() );
274 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
275 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
281 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
282 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
283 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
286 if ( elem.hasAttribute(
"xMap" ) )
289 const double x = elem.attribute( QStringLiteral(
"xMap" ) ).toDouble();
290 const double y = elem.attribute( QStringLiteral(
"yMap" ) ).toDouble();
291 const double z = elem.attribute( QStringLiteral(
"zMap" ) ).toDouble();
297 const double x = elem.attribute( QStringLiteral(
"x" ) ).toDouble();
298 const double y = elem.attribute( QStringLiteral(
"y" ) ).toDouble();
299 const double elev = elem.attribute( QStringLiteral(
"elev" ) ).toDouble();
300 centerPoint =
QgsVector3D( x, elev, y ) - savedOrigin + mOrigin;
305double QgsCameraController::sampleDepthBuffer(
int px,
int py )
307 if ( !mDepthBufferIsReady )
309 QgsDebugError( QStringLiteral(
"Asked to sample depth buffer, but depth buffer not ready!" ) );
314 if ( QWindow *win = window() )
318 px =
static_cast<int>( px * win->devicePixelRatio() );
319 py =
static_cast<int>( py * win->devicePixelRatio() );
323 for (
int x = px - 3; x <= px + 3; ++x )
325 for (
int y = py - 3; y <= py + 3; ++y )
327 if ( mDepthBufferImage.valid( x, y ) )
336double QgsCameraController::depthBufferNonVoidAverage()
339 if ( mDepthBufferNonVoidAverage != -1 )
340 return mDepthBufferNonVoidAverage;
344 int samplesCount = 0;
346 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
347 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
349 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
350 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
362 if ( samplesCount == 0 )
365 depth /= samplesCount;
367 mDepthBufferNonVoidAverage = depth;
372QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
377 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
378 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
384 QgsDebugError( QStringLiteral(
"moveGeocentricPoint: transform failed!" ) );
392 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
394 updateCameraFromPose();
400 updateCameraFromPose();
406 updateCameraFromPose();
412 updateCameraFromPose();
424 QgsDebugError( QStringLiteral(
"resetGlobe: transform failed!" ) );
434void QgsCameraController::updateCameraFromPose()
445 viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
449 QgsDebugError( QStringLiteral(
"updateCameraFromPose: transform failed!" ) );
459 mCameraChanged =
true;
463void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
466 updateCameraFromPose();
469void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
471 if ( !mInputHandlersEnabled )
474 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral(
"3D" ), QStringLiteral(
"QgsCameraController::onPositionChanged" ) );
476 switch ( mCameraNavigationMode )
479 onPositionChangedTerrainNavigation( mouse );
483 onPositionChangedFlyNavigation( mouse );
487 onPositionChangedGlobeTerrainNavigation( mouse );
492bool QgsCameraController::screenPointToWorldPos( QPoint position,
double &depth, QVector3D &worldPosition )
494 depth = sampleDepthBuffer( position.x(), position.y() );
500 depth = depthBufferNonVoidAverage();
504 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
506 QgsDebugMsgLevel( QStringLiteral(
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
513void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
515 if ( mIgnoreNextMouseMove )
517 mIgnoreNextMouseMove =
false;
518 mMousePos = QPoint( mouse->x(), mouse->y() );
522 const int dx = mouse->x() - mMousePos.x();
523 const int dy = mouse->y() - mMousePos.y();
525 const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
526 const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
527 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
528 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
529 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
531 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
534 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
536 float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
537 float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
538 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
540 if ( !mDepthBufferIsReady )
543 if ( !mRotationCenterCalculated )
546 QVector3D worldPosition;
547 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
549 mRotationCenter = worldPosition;
550 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
552 mRotationCenterCalculated =
true;
558 else if ( hasLeftButton && hasCtrl && !hasShift )
560 setMouseParameters( MouseOperation::RotationCamera );
562 const float diffPitch = 0.2f * dy;
563 const float diffYaw = -0.2f * dx;
566 else if ( hasLeftButton && !hasShift && !hasCtrl )
569 setMouseParameters( MouseOperation::Translation, mMousePos );
571 if ( !mDepthBufferIsReady )
574 if ( !mDragPointCalculated )
577 QVector3D worldPosition;
578 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
581 mDragPoint = worldPosition;
582 mDragPointCalculated =
true;
586 QVector3D cameraBeforeDragPos = mCameraBefore->position();
589 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
590 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
593 if ( cameraBeforeToMoveToPos.z() == 0 )
595 cameraBeforeToMoveToPos.setZ( 0.01 );
596 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
599 if ( cameraBeforeToDragPointPos.z() == 0 )
601 cameraBeforeToDragPointPos.setZ( 0.01 );
602 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
605 double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
606 double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
608 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
609 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
611 QVector3D shiftVector = to - from;
613 mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
614 updateCameraFromPose();
616 else if ( hasLeftButton && hasShift && hasCtrl )
620 double tElev = mMousePos.y() - mouse->y();
621 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
623 updateCameraFromPose();
625 else if ( hasRightButton && !hasShift && !hasCtrl )
627 setMouseParameters( MouseOperation::Zoom, mMousePos );
628 if ( !mDepthBufferIsReady )
631 if ( !mDragPointCalculated )
634 QVector3D worldPosition;
635 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
637 mDragPoint = worldPosition;
638 mDragPointCalculated =
true;
642 float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
643 float newDist = oldDist;
646 int screenHeight = mScene->
engine()->
size().height();
647 QWindow *win = window();
650 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
651 screenHeight = win->screen()->size().height();
655 if ( mMousePos.y() > mClickPoint.y() )
657 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / (
double ) ( screenHeight - mClickPoint.y() - yOffset );
658 f = std::max( 0.0, std::min( 1.0, f ) );
659 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
660 newDist = newDist * f;
664 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / (
double ) ( mClickPoint.y() + yOffset );
665 f = std::max( 0.0, std::min( 1.0, f ) );
666 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
667 newDist = newDist + 2 * newDist * f;
670 double zoomFactor = newDist / oldDist;
674 mMousePos = QPoint( mouse->x(), mouse->y() );
677void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
679 const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
680 const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
681 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
682 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
684 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
686 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
688 const float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
689 const float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
690 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
694 updateCameraFromPose();
698 if ( !( mouse->buttons() & Qt::LeftButton ) )
702 setMouseParameters( MouseOperation::Translation, mMousePos );
704 if ( !mDepthBufferIsReady )
707 if ( !mDragPointCalculated )
710 QVector3D worldPosition;
711 if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
715 mDragPoint = worldPosition;
716 mDragPointCalculated =
true;
722 const double sphereRadiusMap = startPosMap.
length();
729 const double quadC =
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
730 const double disc = quadB * quadB - 4 * quadA * quadC;
735 const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
736 if ( rayDistMap < 0 )
738 QgsDebugError( QStringLiteral(
"Sphere intersection result negative, cancelling move" ) );
749 oldLatLon = mGlobeCrsToLatLon.
transform( startPosMap );
750 newLatLon = mGlobeCrsToLatLon.
transform( newPosMap );
754 QgsDebugError( QStringLiteral(
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
758 const double latDiff = oldLatLon.
y() - newLatLon.
y();
759 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
761 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
765 updateCameraFromPose();
773 dist -= dist * factor * 0.01f;
775 updateCameraFromPose();
778void QgsCameraController::handleTerrainNavigationWheelZoom()
780 if ( !mDepthBufferIsReady )
783 if ( !mZoomPointCalculated )
786 QVector3D worldPosition;
787 if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
789 mZoomPoint = worldPosition;
790 mZoomPointCalculated =
true;
794 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
796 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
798 newDist = std::max( newDist, 2.0 );
799 double zoomFactor = newDist / oldDist;
801 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
805 mCumulatedWheelY = 0;
806 setMouseParameters( MouseOperation::None );
809void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
811 if ( !mInputHandlersEnabled )
814 switch ( mCameraNavigationMode )
818 const float scaling = ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
819 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
827 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1 : 1.0 );
831 mCumulatedWheelY += scaling * wheel->angleDelta().y();
833 if ( mCurrentOperation != MouseOperation::ZoomWheel )
835 setMouseParameters( MouseOperation::ZoomWheel );
840 handleTerrainNavigationWheelZoom();
847 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
848 float factor = abs( wheelAmount ) / 1000.f;
849 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
851 updateCameraFromPose();
857void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
859 if ( !mInputHandlersEnabled )
862 mKeyboardHandler->setFocus(
true );
864 if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
866 mMousePos = QPoint( mouse->x(), mouse->y() );
868 if ( mCaptureFpsMouseMovements )
869 mIgnoreNextMouseMove =
true;
871 const MouseOperation operation {
872 ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
874 setMouseParameters( operation, mMousePos );
877 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
879 mMousePos = QPoint( mouse->x(), mouse->y() );
881 if ( mCaptureFpsMouseMovements )
882 mIgnoreNextMouseMove =
true;
884 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
885 setMouseParameters( operation, mMousePos );
889void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
892 if ( !mInputHandlersEnabled )
896 setMouseParameters( MouseOperation::None );
899bool QgsCameraController::onKeyPressedTerrainNavigation( QKeyEvent *event )
901 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
902 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
904 int tx = 0, ty = 0, tElev = 0;
905 switch ( event->key() )
921 case Qt::Key_PageDown:
933 if ( !hasShift && !hasCtrl )
937 else if ( hasShift && !hasCtrl )
943 else if ( hasCtrl && !hasShift )
946 const float diffPitch = ty;
947 const float diffYaw = -tx;
956 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
959 updateCameraFromPose();
965bool QgsCameraController::onKeyPressedGlobeTerrainNavigation( QKeyEvent *event )
969 constexpr float MOVE_FACTOR = 0.000001f;
970 constexpr float ZOOM_FACTOR = 0.9f;
972 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
974 switch ( event->key() )
1000 case Qt::Key_PageDown:
1003 case Qt::Key_PageUp:
1012static const QSet<int> walkNavigationSavedKeys = {
1027bool QgsCameraController::onKeyPressedFlyNavigation( QKeyEvent *event )
1029 switch ( event->key() )
1031 case Qt::Key_QuoteLeft:
1034 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
1035 mIgnoreNextMouseMove =
true;
1036 if ( mCaptureFpsMouseMovements )
1038 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1042 qApp->restoreOverrideCursor();
1048 case Qt::Key_Escape:
1051 if ( mCaptureFpsMouseMovements )
1053 mCaptureFpsMouseMovements =
false;
1054 mIgnoreNextMouseMove =
true;
1055 qApp->restoreOverrideCursor();
1065 if ( walkNavigationSavedKeys.contains( event->key() ) )
1067 if ( !event->isAutoRepeat() )
1069 mDepressedKeys.insert( event->key() );
1079 const QVector3D cameraUp = mCamera->upVector().normalized();
1081 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1083 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1087 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1091 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1095 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1098 moveCameraPositionBy( cameraPosDiff );
1101void QgsCameraController::applyFlyModeKeyMovements()
1107 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1108 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1110 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1112 bool changed =
false;
1116 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1122 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1128 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1134 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1142 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1143 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1146 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1149 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1152 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1159void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1161 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1162 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1164 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1165 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1166 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1168 if ( mIgnoreNextMouseMove )
1170 mIgnoreNextMouseMove =
false;
1174 if ( hasMiddleButton )
1177 const QVector3D cameraUp = mCamera->upVector().normalized();
1179 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1180 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1181 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
1183 else if ( hasRightButton )
1187 const QVector3D cameraPosDiff = dy * cameraFront;
1188 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
1192 if ( mCaptureFpsMouseMovements )
1194 float diffPitch = -0.2f * dy;
1195 switch ( mVerticalAxisInversion )
1206 const float diffYaw = -0.2f * dx;
1209 else if ( mouse->buttons() & Qt::LeftButton )
1211 float diffPitch = -0.2f * dy;
1212 switch ( mVerticalAxisInversion )
1222 const float diffYaw = -0.2f * dx;
1227 if ( mCaptureFpsMouseMovements )
1229 mIgnoreNextMouseMove =
true;
1240 pitch -= deltaPitch;
1242 updateCameraFromPose();
1251 updateCameraFromPose();
1257 updateCameraFromPose();
1264 const float x = tx * dist * 0.02f;
1265 const float y = -ty * dist * 0.02f;
1268 const float t = sqrt( x * x + y * y );
1269 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1270 const float dx = cos( a ) * t;
1271 const float dy = sin( a ) * t;
1274 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1276 updateCameraFromPose();
1281 if ( !mInputHandlersEnabled )
1284 if ( event->type() == QKeyEvent::Type::KeyRelease )
1286 if ( !event->isAutoRepeat() && mDepressedKeys.contains( event->key() ) )
1288 mDepressedKeys.remove( event->key() );
1292 else if ( event->type() == QEvent::ShortcutOverride )
1294 if ( event->modifiers() & Qt::ControlModifier )
1296 switch ( event->key() )
1298 case Qt::Key_QuoteLeft:
1301 switch ( mCameraNavigationMode )
1347 switch ( mCameraNavigationMode )
1350 return onKeyPressedFlyNavigation( event );
1353 return onKeyPressedTerrainNavigation( event );
1356 return onKeyPressedGlobeTerrainNavigation( event );
1364 mDepthBufferImage = depthImage;
1365 mDepthBufferIsReady =
true;
1366 mDepthBufferNonVoidAverage = -1;
1374 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1376 handleTerrainNavigationWheelZoom();
1380bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1382 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1385void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1387 if ( newOperation == mCurrentOperation )
1392 if ( newOperation == MouseOperation::None )
1394 mClickPoint = QPoint();
1402 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1404 mClickPoint = clickPoint;
1408 mCurrentOperation = newOperation;
1409 mDepthBufferIsReady =
false;
1410 mRotationCenterCalculated =
false;
1411 mDragPointCalculated =
false;
1412 mZoomPointCalculated =
false;
1414 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1416 mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
1429 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1430 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1431 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1432 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1436 updateCameraFromPose();
1439void QgsCameraController::rotateToRespectingTerrain(
float pitch,
float yaw )
1442 double elevation = 0.0;
1446 QVector3D camPos = mCamera->position();
1447 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCamera->farPlane() );
1449 if ( !hits.isEmpty() )
1451 elevation = hits.at( 0 ).pos.z();
1452 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
1459 pos.
set( pos.
x(), pos.
y(), elevation + mScene->
terrainEntity()->terrainElevationOffset() );
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.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
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 rotateCameraToBottom()
Rotate to bottom-up view.
void setLookingAtMapPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets camera configuration like setLookingAtPoint(), but the point is given in map coordinates.
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 rotateCameraToTop()
Rotate to top-down view.
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 rotateCameraToEast()
Rotate to view from the east.
void setVerticalAxisInversion(Qgis::VerticalAxisInversion inversion)
Sets the vertical axis inversion behavior.
const QgsVector3D origin() const
Returns the origin of the scene in map coordinates.
void rotateCameraToHome()
Rotate to diagonal view.
void rotateCameraToNorth()
Rotate to view from the north.
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 rotateCameraToWest()
Rotate to view from the west.
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.
void rotateCameraToSouth()
Rotate to view from the south.
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...
bool keyboardEventFilter(QKeyEvent *event)
If the event is relevant, handles the event and returns true, otherwise false.
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 readXml(const QDomElement &elem, QgsVector3D savedOrigin)
Reads camera configuration from the given DOM element.
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.
QVector3D toVector3D() const
Converts the current object to QVector3D.
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)
Helper struct to store ray casting parameters.