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" ) );
279 elemCamera.setAttribute( QStringLiteral(
"x" ), centerPoint.
x() );
280 elemCamera.setAttribute( QStringLiteral(
"y" ), centerPoint.
z() );
281 elemCamera.setAttribute( QStringLiteral(
"elev" ), centerPoint.
y() );
283 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
284 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
290 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
291 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
292 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
293 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
294 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
295 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
298 centerPoint = centerPoint - mOrigin;
302double QgsCameraController::sampleDepthBuffer(
int px,
int py )
304 if ( !mDepthBufferIsReady )
306 QgsDebugError( QStringLiteral(
"Asked to sample depth buffer, but depth buffer not ready!" ) );
311 if ( QWindow *win = window() )
315 px =
static_cast<int>( px * win->devicePixelRatio() );
316 py =
static_cast<int>( py * win->devicePixelRatio() );
320 for (
int x = px - 3; x <= px + 3; ++x )
322 for (
int y = py - 3; y <= py + 3; ++y )
324 if ( mDepthBufferImage.valid( x, y ) )
333double QgsCameraController::depthBufferNonVoidAverage()
336 if ( mDepthBufferNonVoidAverage != -1 )
337 return mDepthBufferNonVoidAverage;
341 int samplesCount = 0;
343 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
344 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
346 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
347 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
359 if ( samplesCount == 0 )
362 depth /= samplesCount;
364 mDepthBufferNonVoidAverage = depth;
369QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
374 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
375 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
381 QgsDebugError( QStringLiteral(
"moveGeocentricPoint: transform failed!" ) );
389 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
391 updateCameraFromPose();
397 updateCameraFromPose();
403 updateCameraFromPose();
409 updateCameraFromPose();
421 QgsDebugError( QStringLiteral(
"resetGlobe: transform failed!" ) );
431void QgsCameraController::updateCameraFromPose()
442 viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
446 QgsDebugError( QStringLiteral(
"updateCameraFromPose: transform failed!" ) );
456 mCameraChanged =
true;
460void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
463 updateCameraFromPose();
466void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
468 if ( !mInputHandlersEnabled )
471 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral(
"3D" ), QStringLiteral(
"QgsCameraController::onPositionChanged" ) );
473 switch ( mCameraNavigationMode )
476 onPositionChangedTerrainNavigation( mouse );
480 onPositionChangedFlyNavigation( mouse );
484 onPositionChangedGlobeTerrainNavigation( mouse );
489bool QgsCameraController::screenPointToWorldPos( QPoint position,
double &depth, QVector3D &worldPosition )
491 depth = sampleDepthBuffer( position.x(), position.y() );
497 depth = depthBufferNonVoidAverage();
501 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
503 QgsDebugMsgLevel( QStringLiteral(
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
510void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
512 if ( mIgnoreNextMouseMove )
514 mIgnoreNextMouseMove =
false;
515 mMousePos = QPoint( mouse->x(), mouse->y() );
519 const int dx = mouse->x() - mMousePos.x();
520 const int dy = mouse->y() - mMousePos.y();
522 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
523 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
524 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
525 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
526 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
528 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
531 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
533 float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
534 float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
535 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
537 if ( !mDepthBufferIsReady )
540 if ( !mRotationCenterCalculated )
543 QVector3D worldPosition;
544 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
546 mRotationCenter = worldPosition;
547 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
549 mRotationCenterCalculated =
true;
555 else if ( hasLeftButton && hasCtrl && !hasShift )
557 setMouseParameters( MouseOperation::RotationCamera );
559 const float diffPitch = 0.2f * dy;
560 const float diffYaw = -0.2f * dx;
563 else if ( hasLeftButton && !hasShift && !hasCtrl )
566 setMouseParameters( MouseOperation::Translation, mMousePos );
568 if ( !mDepthBufferIsReady )
571 if ( !mDragPointCalculated )
574 QVector3D worldPosition;
575 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
578 mDragPoint = worldPosition;
579 mDragPointCalculated =
true;
583 QVector3D cameraBeforeDragPos = mCameraBefore->position();
586 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
587 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
590 if ( cameraBeforeToMoveToPos.z() == 0 )
592 cameraBeforeToMoveToPos.setZ( 0.01 );
593 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
596 if ( cameraBeforeToDragPointPos.z() == 0 )
598 cameraBeforeToDragPointPos.setZ( 0.01 );
599 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
602 double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
603 double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
605 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
606 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
608 QVector3D shiftVector = to - from;
610 mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
611 updateCameraFromPose();
613 else if ( hasLeftButton && hasShift && hasCtrl )
617 double tElev = mMousePos.y() - mouse->y();
618 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
620 updateCameraFromPose();
622 else if ( hasRightButton && !hasShift && !hasCtrl )
624 setMouseParameters( MouseOperation::Zoom, mMousePos );
625 if ( !mDepthBufferIsReady )
628 if ( !mDragPointCalculated )
631 QVector3D worldPosition;
632 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
634 mDragPoint = worldPosition;
635 mDragPointCalculated =
true;
639 float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
640 float newDist = oldDist;
643 int screenHeight = mScene->
engine()->
size().height();
644 QWindow *win = window();
647 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
648 screenHeight = win->screen()->size().height();
652 if ( mMousePos.y() > mClickPoint.y() )
654 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / (
double ) ( screenHeight - mClickPoint.y() - yOffset );
655 f = std::max( 0.0, std::min( 1.0, f ) );
656 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
657 newDist = newDist * f;
661 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / (
double ) ( mClickPoint.y() + yOffset );
662 f = std::max( 0.0, std::min( 1.0, f ) );
663 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
664 newDist = newDist + 2 * newDist * f;
667 double zoomFactor = newDist / oldDist;
671 mMousePos = QPoint( mouse->x(), mouse->y() );
674void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
676 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
677 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
678 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
679 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
681 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
683 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
685 const float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
686 const float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
687 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
691 updateCameraFromPose();
695 if ( !( mouse->buttons() & Qt::LeftButton ) )
699 setMouseParameters( MouseOperation::Translation, mMousePos );
701 if ( !mDepthBufferIsReady )
704 if ( !mDragPointCalculated )
707 QVector3D worldPosition;
708 if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
712 mDragPoint = worldPosition;
713 mDragPointCalculated =
true;
719 const double sphereRadiusMap = startPosMap.
length();
726 const double quadC =
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
727 const double disc = quadB * quadB - 4 * quadA * quadC;
732 const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
733 if ( rayDistMap < 0 )
735 QgsDebugError( QStringLiteral(
"Sphere intersection result negative, cancelling move" ) );
746 oldLatLon = mGlobeCrsToLatLon.
transform( startPosMap );
747 newLatLon = mGlobeCrsToLatLon.
transform( newPosMap );
751 QgsDebugError( QStringLiteral(
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
755 const double latDiff = oldLatLon.
y() - newLatLon.
y();
756 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
758 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
762 updateCameraFromPose();
770 dist -= dist * factor * 0.01f;
772 updateCameraFromPose();
775void QgsCameraController::handleTerrainNavigationWheelZoom()
777 if ( !mDepthBufferIsReady )
780 if ( !mZoomPointCalculated )
783 QVector3D worldPosition;
784 if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
786 mZoomPoint = worldPosition;
787 mZoomPointCalculated =
true;
791 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
793 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
795 newDist = std::max( newDist, 2.0 );
796 double zoomFactor = newDist / oldDist;
798 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
802 mCumulatedWheelY = 0;
803 setMouseParameters( MouseOperation::None );
806void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
808 if ( !mInputHandlersEnabled )
811 switch ( mCameraNavigationMode )
815 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
816 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
824 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1 : 1.0 );
828 mCumulatedWheelY += scaling * wheel->angleDelta().y();
830 if ( mCurrentOperation != MouseOperation::ZoomWheel )
832 setMouseParameters( MouseOperation::ZoomWheel );
837 handleTerrainNavigationWheelZoom();
844 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
845 float factor = abs( wheelAmount ) / 1000.f;
846 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
848 updateCameraFromPose();
854void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
856 if ( !mInputHandlersEnabled )
859 mKeyboardHandler->setFocus(
true );
861 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 ) )
863 mMousePos = QPoint( mouse->x(), mouse->y() );
865 if ( mCaptureFpsMouseMovements )
866 mIgnoreNextMouseMove =
true;
868 const MouseOperation operation {
869 ( mouse->modifiers() & Qt::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
871 setMouseParameters( operation, mMousePos );
874 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
876 mMousePos = QPoint( mouse->x(), mouse->y() );
878 if ( mCaptureFpsMouseMovements )
879 mIgnoreNextMouseMove =
true;
881 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
882 setMouseParameters( operation, mMousePos );
886void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
889 if ( !mInputHandlersEnabled )
893 setMouseParameters( MouseOperation::None );
896bool QgsCameraController::onKeyPressedTerrainNavigation( QKeyEvent *event )
898 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
899 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
901 int tx = 0, ty = 0, tElev = 0;
902 switch ( event->key() )
918 case Qt::Key_PageDown:
930 if ( !hasShift && !hasCtrl )
934 else if ( hasShift && !hasCtrl )
940 else if ( hasCtrl && !hasShift )
943 const float diffPitch = ty;
944 const float diffYaw = -tx;
953 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
956 updateCameraFromPose();
962bool QgsCameraController::onKeyPressedGlobeTerrainNavigation( QKeyEvent *event )
966 constexpr float MOVE_FACTOR = 0.000001f;
967 constexpr float ZOOM_FACTOR = 0.9f;
969 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
971 switch ( event->key() )
997 case Qt::Key_PageDown:
1000 case Qt::Key_PageUp:
1009static const QSet<int> walkNavigationSavedKeys = {
1024bool QgsCameraController::onKeyPressedFlyNavigation( QKeyEvent *event )
1026 switch ( event->key() )
1028 case Qt::Key_QuoteLeft:
1031 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
1032 mIgnoreNextMouseMove =
true;
1033 if ( mCaptureFpsMouseMovements )
1035 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1039 qApp->restoreOverrideCursor();
1045 case Qt::Key_Escape:
1048 if ( mCaptureFpsMouseMovements )
1050 mCaptureFpsMouseMovements =
false;
1051 mIgnoreNextMouseMove =
true;
1052 qApp->restoreOverrideCursor();
1062 if ( walkNavigationSavedKeys.contains( event->key() ) )
1064 if ( !event->isAutoRepeat() )
1066 mDepressedKeys.insert( event->key() );
1076 const QVector3D cameraUp = mCamera->upVector().normalized();
1078 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1080 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1084 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1088 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1092 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1095 moveCameraPositionBy( cameraPosDiff );
1098void QgsCameraController::applyFlyModeKeyMovements()
1104 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1105 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1107 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1109 bool changed =
false;
1113 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1119 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1125 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1131 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1139 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1140 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1143 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1146 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1149 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1156void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1158 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1159 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1161 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1162 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1163 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1165 if ( mIgnoreNextMouseMove )
1167 mIgnoreNextMouseMove =
false;
1171 if ( hasMiddleButton )
1174 const QVector3D cameraUp = mCamera->upVector().normalized();
1176 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1177 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1178 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
1180 else if ( hasRightButton )
1184 const QVector3D cameraPosDiff = dy * cameraFront;
1185 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
1189 if ( mCaptureFpsMouseMovements )
1191 float diffPitch = -0.2f * dy;
1192 switch ( mVerticalAxisInversion )
1203 const float diffYaw = -0.2f * dx;
1206 else if ( mouse->buttons() & Qt::LeftButton )
1208 float diffPitch = -0.2f * dy;
1209 switch ( mVerticalAxisInversion )
1219 const float diffYaw = -0.2f * dx;
1224 if ( mCaptureFpsMouseMovements )
1226 mIgnoreNextMouseMove =
true;
1237 pitch -= deltaPitch;
1239 updateCameraFromPose();
1248 updateCameraFromPose();
1254 updateCameraFromPose();
1261 const float x = tx * dist * 0.02f;
1262 const float y = -ty * dist * 0.02f;
1265 const float t = sqrt( x * x + y * y );
1266 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1267 const float dx = cos( a ) * t;
1268 const float dy = sin( a ) * t;
1271 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1273 updateCameraFromPose();
1278 if ( !mInputHandlersEnabled )
1281 if ( event->type() == QKeyEvent::Type::KeyRelease )
1283 if ( !event->isAutoRepeat() && mDepressedKeys.contains( event->key() ) )
1285 mDepressedKeys.remove( event->key() );
1289 else if ( event->type() == QEvent::ShortcutOverride )
1291 if ( event->modifiers() & Qt::ControlModifier )
1293 switch ( event->key() )
1295 case Qt::Key_QuoteLeft:
1298 switch ( mCameraNavigationMode )
1344 switch ( mCameraNavigationMode )
1347 return onKeyPressedFlyNavigation( event );
1350 return onKeyPressedTerrainNavigation( event );
1353 return onKeyPressedGlobeTerrainNavigation( event );
1361 mDepthBufferImage = depthImage;
1362 mDepthBufferIsReady =
true;
1363 mDepthBufferNonVoidAverage = -1;
1371 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1373 handleTerrainNavigationWheelZoom();
1377bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1379 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1382void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1384 if ( newOperation == mCurrentOperation )
1389 if ( newOperation == MouseOperation::None )
1391 mClickPoint = QPoint();
1399 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1401 mClickPoint = clickPoint;
1405 mCurrentOperation = newOperation;
1406 mDepthBufferIsReady =
false;
1407 mRotationCenterCalculated =
false;
1408 mDragPointCalculated =
false;
1409 mZoomPointCalculated =
false;
1411 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1413 mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
1426 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1427 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1428 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1429 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1433 updateCameraFromPose();
1436void QgsCameraController::rotateToRespectingTerrain(
float pitch,
float yaw )
1439 double elevation = 0.0;
1443 QVector3D camPos = mCamera->position();
1444 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCamera->farPlane() );
1446 if ( !hits.isEmpty() )
1448 elevation = hits.at( 0 ).pos.z();
1449 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
1456 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.
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 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 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.