17#include "moc_qgscameracontroller.cpp"
26#include <QDomDocument>
27#include <Qt3DRender/QCamera>
29#include <QStringLiteral>
38 , mCamera( scene->engine()->camera() )
40 , mMouseHandler( new
Qt3DInput::QMouseHandler )
41 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
42 , mOrigin( scene->mapSettings()->origin() )
44 mMouseHandler->setSourceDevice(
new Qt3DInput::QMouseDevice() );
45 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
this, &QgsCameraController::onPositionChanged );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
this, &QgsCameraController::onWheel );
47 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
this, &QgsCameraController::onMousePressed );
48 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
this, &QgsCameraController::onMouseReleased );
49 addComponent( mMouseHandler );
51 mKeyboardHandler->setSourceDevice(
new Qt3DInput::QKeyboardDevice() );
52 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
this, &QgsCameraController::onKeyPressed );
53 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
this, &QgsCameraController::onKeyReleased );
54 addComponent( mKeyboardHandler );
57 connect(
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
58 connect(
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
60 mFpsNavTimer =
new QTimer(
this );
61 mFpsNavTimer->setInterval( 10 );
62 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
63 mFpsNavTimer->start();
74QWindow *QgsCameraController::window()
const
77 return windowEngine ? windowEngine->
window() :
nullptr;
82 if ( navigationMode == mCameraNavigationMode )
85 mCameraNavigationMode = navigationMode;
86 mIgnoreNextMouseMove =
true;
92 if ( movementSpeed == mCameraMovementSpeed )
97 mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
103 mVerticalAxisInversion = inversion;
108 float newPitch = mCameraPose.
pitchAngle() + diffPitch;
109 float newHeading = mCameraPose.
headingAngle() + diffHeading;
111 newPitch = std::clamp( newPitch, 0.f, 180.f );
128 QgsDebugError( QStringLiteral(
"rotateCamera: ECEF -> lat,lon transform failed!" ) );
131 QQuaternion qLatLon = QQuaternion::fromAxisAndAngle( QVector3D( 0, 0, 1 ),
static_cast<float>( viewCenterLatLon.
x() ) )
132 * QQuaternion::fromAxisAndAngle( QVector3D( 0, -1, 0 ),
static_cast<float>( viewCenterLatLon.
y() ) );
133 QQuaternion qPitchHeading = QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), newHeading )
134 * QQuaternion::fromAxisAndAngle( QVector3D( 0, 1, 0 ), newPitch );
135 QVector3D newCameraToCenter = ( qLatLon * qPitchHeading * QVector3D( -1, 0, 0 ) ) * mCameraPose.
distanceFromCenterPoint();
137 mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
140 updateCameraFromPose();
148 mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
151 updateCameraFromPose();
159 const float oldPitch = mCameraPose.
pitchAngle();
162 newPitch = std::clamp( newPitch, 0.f, 180.f );
168 const QQuaternion q = qNew * qOld.conjugated();
170 const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
175 updateCameraFromPose();
181 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
186 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
187 QVector3D newViewCenter = newCamPosition + cameraToCenter;
191 updateCameraFromPose();
198 if ( mCameraChanged )
201 mCameraChanged =
false;
216 QgsDebugError( QStringLiteral(
"setViewFromTop() should not be used with globe!" ) );
222 const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
228 mCamera->setNearPlane(
distance / 2 );
229 mCamera->setFarPlane(
distance * 2 );
261 if ( camPose == mCameraPose && !force )
264 mCameraPose = camPose;
265 updateCameraFromPose();
270 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
283 elemCamera.setAttribute( QStringLiteral(
"x" ), centerPoint.
x() );
284 elemCamera.setAttribute( QStringLiteral(
"y" ), centerPoint.
z() );
285 elemCamera.setAttribute( QStringLiteral(
"elev" ), centerPoint.
y() );
287 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
288 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
294 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
295 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
296 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
297 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
298 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
299 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
302 centerPoint = centerPoint - mOrigin;
306double QgsCameraController::sampleDepthBuffer(
int px,
int py )
308 if ( !mDepthBufferIsReady )
310 QgsDebugError( QStringLiteral(
"Asked to sample depth buffer, but depth buffer not ready!" ) );
315 if ( QWindow *win = window() )
319 px =
static_cast<int>( px * win->devicePixelRatio() );
320 py =
static_cast<int>( py * win->devicePixelRatio() );
324 for (
int x = px - 3; x <= px + 3; ++x )
326 for (
int y = py - 3; y <= py + 3; ++y )
328 if ( mDepthBufferImage.valid( x, y ) )
337double QgsCameraController::depthBufferNonVoidAverage()
340 if ( mDepthBufferNonVoidAverage != -1 )
341 return mDepthBufferNonVoidAverage;
345 int samplesCount = 0;
347 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
348 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
350 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
351 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
363 if ( samplesCount == 0 )
366 depth /= samplesCount;
368 mDepthBufferNonVoidAverage = depth;
373QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
378 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
379 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
385 QgsDebugError( QStringLiteral(
"moveGeocentricPoint: transform failed!" ) );
393 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
395 updateCameraFromPose();
401 updateCameraFromPose();
407 updateCameraFromPose();
413 updateCameraFromPose();
425 QgsDebugError( QStringLiteral(
"resetGlobe: transform failed!" ) );
435void QgsCameraController::updateCameraFromPose()
446 viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
450 QgsDebugError( QStringLiteral(
"updateCameraFromPose: transform failed!" ) );
460 mCameraChanged =
true;
464void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
467 updateCameraFromPose();
470void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
472 if ( !mInputHandlersEnabled )
475 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral(
"3D" ), QStringLiteral(
"QgsCameraController::onPositionChanged" ) );
477 switch ( mCameraNavigationMode )
480 onPositionChangedTerrainNavigation( mouse );
484 onPositionChangedFlyNavigation( mouse );
488 onPositionChangedGlobeTerrainNavigation( mouse );
493bool QgsCameraController::screenPointToWorldPos( QPoint position,
double &depth, QVector3D &worldPosition )
495 depth = sampleDepthBuffer( position.x(), position.y() );
501 depth = depthBufferNonVoidAverage();
505 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
507 QgsDebugMsgLevel( QStringLiteral(
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
514void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
516 if ( mIgnoreNextMouseMove )
518 mIgnoreNextMouseMove =
false;
519 mMousePos = QPoint( mouse->x(), mouse->y() );
523 const int dx = mouse->x() - mMousePos.x();
524 const int dy = mouse->y() - mMousePos.y();
526 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
527 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
528 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
529 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
530 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
532 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
535 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
537 float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
538 float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
539 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
541 if ( !mDepthBufferIsReady )
544 if ( !mRotationCenterCalculated )
547 QVector3D worldPosition;
548 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
550 mRotationCenter = worldPosition;
551 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
553 mRotationCenterCalculated =
true;
559 else if ( hasLeftButton && hasCtrl && !hasShift )
561 setMouseParameters( MouseOperation::RotationCamera );
563 const float diffPitch = 0.2f * dy;
564 const float diffYaw = -0.2f * dx;
567 else if ( hasLeftButton && !hasShift && !hasCtrl )
570 setMouseParameters( MouseOperation::Translation, mMousePos );
572 if ( !mDepthBufferIsReady )
575 if ( !mDragPointCalculated )
578 QVector3D worldPosition;
579 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
582 mDragPoint = worldPosition;
583 mDragPointCalculated =
true;
587 QVector3D cameraBeforeDragPos = mCameraBefore->position();
590 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
591 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
594 if ( cameraBeforeToMoveToPos.z() == 0 )
596 cameraBeforeToMoveToPos.setZ( 0.01 );
597 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
600 if ( cameraBeforeToDragPointPos.z() == 0 )
602 cameraBeforeToDragPointPos.setZ( 0.01 );
603 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
606 double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
607 double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
609 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
610 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
612 QVector3D shiftVector = to - from;
614 mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
615 updateCameraFromPose();
617 else if ( hasLeftButton && hasShift && hasCtrl )
621 double tElev = mMousePos.y() - mouse->y();
622 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
624 updateCameraFromPose();
626 else if ( hasRightButton && !hasShift && !hasCtrl )
628 setMouseParameters( MouseOperation::Zoom, mMousePos );
629 if ( !mDepthBufferIsReady )
632 if ( !mDragPointCalculated )
635 QVector3D worldPosition;
636 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
638 mDragPoint = worldPosition;
639 mDragPointCalculated =
true;
643 float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
644 float newDist = oldDist;
647 int screenHeight = mScene->
engine()->
size().height();
648 QWindow *win = window();
651 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
652 screenHeight = win->screen()->size().height();
656 if ( mMousePos.y() > mClickPoint.y() )
658 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / (
double ) ( screenHeight - mClickPoint.y() - yOffset );
659 f = std::max( 0.0, std::min( 1.0, f ) );
660 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
661 newDist = newDist * f;
665 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / (
double ) ( mClickPoint.y() + yOffset );
666 f = std::max( 0.0, std::min( 1.0, f ) );
667 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
668 newDist = newDist + 2 * newDist * f;
671 double zoomFactor = newDist / oldDist;
675 mMousePos = QPoint( mouse->x(), mouse->y() );
678void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
680 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
681 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
682 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
683 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
685 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
687 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
689 const float scale =
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
690 const float pitchDiff = 180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
691 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
695 updateCameraFromPose();
699 if ( !( mouse->buttons() & Qt::LeftButton ) )
703 setMouseParameters( MouseOperation::Translation, mMousePos );
705 if ( !mDepthBufferIsReady )
708 if ( !mDragPointCalculated )
711 QVector3D worldPosition;
712 if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
716 mDragPoint = worldPosition;
717 mDragPointCalculated =
true;
723 const double sphereRadiusMap = startPosMap.
length();
730 const double quadC =
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
731 const double disc = quadB * quadB - 4 * quadA * quadC;
736 const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
737 if ( rayDistMap < 0 )
739 QgsDebugError( QStringLiteral(
"Sphere intersection result negative, cancelling move" ) );
750 oldLatLon = mGlobeCrsToLatLon.
transform( startPosMap );
751 newLatLon = mGlobeCrsToLatLon.
transform( newPosMap );
755 QgsDebugError( QStringLiteral(
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
759 const double latDiff = oldLatLon.
y() - newLatLon.
y();
760 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
762 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
766 updateCameraFromPose();
774 dist -= dist * factor * 0.01f;
776 updateCameraFromPose();
779void QgsCameraController::handleTerrainNavigationWheelZoom()
781 if ( !mDepthBufferIsReady )
784 if ( !mZoomPointCalculated )
787 QVector3D worldPosition;
788 if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
790 mZoomPoint = worldPosition;
791 mZoomPointCalculated =
true;
795 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
797 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
799 newDist = std::max( newDist, 2.0 );
800 double zoomFactor = newDist / oldDist;
802 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
806 mCumulatedWheelY = 0;
807 setMouseParameters( MouseOperation::None );
810void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
812 if ( !mInputHandlersEnabled )
815 switch ( mCameraNavigationMode )
819 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
820 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
828 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1 : 1.0 );
832 mCumulatedWheelY += scaling * wheel->angleDelta().y();
834 if ( mCurrentOperation != MouseOperation::ZoomWheel )
836 setMouseParameters( MouseOperation::ZoomWheel );
841 handleTerrainNavigationWheelZoom();
848 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
849 float factor = abs( wheelAmount ) / 1000.f;
850 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
852 updateCameraFromPose();
858void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
860 if ( !mInputHandlersEnabled )
863 mKeyboardHandler->setFocus(
true );
865 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 ) )
867 mMousePos = QPoint( mouse->x(), mouse->y() );
869 if ( mCaptureFpsMouseMovements )
870 mIgnoreNextMouseMove =
true;
872 const MouseOperation operation {
873 ( mouse->modifiers() & Qt::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
875 setMouseParameters( operation, mMousePos );
878 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
880 mMousePos = QPoint( mouse->x(), mouse->y() );
882 if ( mCaptureFpsMouseMovements )
883 mIgnoreNextMouseMove =
true;
885 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
886 setMouseParameters( operation, mMousePos );
890void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
893 if ( !mInputHandlersEnabled )
897 setMouseParameters( MouseOperation::None );
900void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
902 if ( !mInputHandlersEnabled )
905 if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
908 switch ( mCameraNavigationMode )
921 switch ( mCameraNavigationMode )
925 onKeyPressedFlyNavigation( event );
931 onKeyPressedTerrainNavigation( event );
937 onKeyPressedGlobeTerrainNavigation( event );
943void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
945 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
946 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
948 int tx = 0, ty = 0, tElev = 0;
949 switch ( event->key() )
965 case Qt::Key_PageDown:
975 if ( !hasShift && !hasCtrl )
979 else if ( hasShift && !hasCtrl )
985 else if ( hasCtrl && !hasShift )
988 const float diffPitch = ty;
989 const float diffYaw = -tx;
997 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
999 updateCameraFromPose();
1003void QgsCameraController::onKeyPressedGlobeTerrainNavigation( Qt3DInput::QKeyEvent *event )
1007 constexpr float MOVE_FACTOR = 0.000001f;
1008 constexpr float ZOOM_FACTOR = 0.9f;
1010 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
1012 switch ( event->key() )
1038 case Qt::Key_PageDown:
1041 case Qt::Key_PageUp:
1049void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
1051 switch ( event->key() )
1053 case Qt::Key_QuoteLeft:
1056 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
1057 mIgnoreNextMouseMove =
true;
1058 if ( mCaptureFpsMouseMovements )
1060 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1064 qApp->restoreOverrideCursor();
1069 case Qt::Key_Escape:
1072 if ( mCaptureFpsMouseMovements )
1074 mCaptureFpsMouseMovements =
false;
1075 mIgnoreNextMouseMove =
true;
1076 qApp->restoreOverrideCursor();
1086 if ( event->isAutoRepeat() )
1089 mDepressedKeys.insert( event->key() );
1094 const QVector3D cameraUp = mCamera->upVector().normalized();
1096 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1098 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1102 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1106 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1110 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1113 moveCameraPositionBy( cameraPosDiff );
1116void QgsCameraController::applyFlyModeKeyMovements()
1119 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1120 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1122 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1124 bool changed =
false;
1128 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1134 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1140 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1146 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1154 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1155 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1158 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1161 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1164 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1171void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1173 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1174 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1176 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1177 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1178 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1180 if ( mIgnoreNextMouseMove )
1182 mIgnoreNextMouseMove =
false;
1186 if ( hasMiddleButton )
1189 const QVector3D cameraUp = mCamera->upVector().normalized();
1191 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1192 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1193 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
1195 else if ( hasRightButton )
1199 const QVector3D cameraPosDiff = dy * cameraFront;
1200 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
1204 if ( mCaptureFpsMouseMovements )
1206 float diffPitch = -0.2f * dy;
1207 switch ( mVerticalAxisInversion )
1218 const float diffYaw = -0.2f * dx;
1221 else if ( mouse->buttons() & Qt::LeftButton )
1223 float diffPitch = -0.2f * dy;
1224 switch ( mVerticalAxisInversion )
1234 const float diffYaw = -0.2f * dx;
1239 if ( mCaptureFpsMouseMovements )
1241 mIgnoreNextMouseMove =
true;
1248void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
1250 if ( !mInputHandlersEnabled )
1253 if ( event->isAutoRepeat() )
1256 mDepressedKeys.remove( event->key() );
1263 pitch -= deltaPitch;
1265 updateCameraFromPose();
1274 updateCameraFromPose();
1280 updateCameraFromPose();
1287 const float x = tx * dist * 0.02f;
1288 const float y = -ty * dist * 0.02f;
1291 const float t = sqrt( x * x + y * y );
1292 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1293 const float dx = cos( a ) * t;
1294 const float dy = sin( a ) * t;
1297 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1299 updateCameraFromPose();
1304 if ( event->key() == Qt::Key_QuoteLeft )
1307 switch ( mCameraNavigationMode )
1311 switch ( event->key() )
1321 case Qt::Key_PageUp:
1323 case Qt::Key_PageDown:
1327 case Qt::Key_Escape:
1328 if ( mCaptureFpsMouseMovements )
1340 switch ( event->key() )
1346 case Qt::Key_PageUp:
1347 case Qt::Key_PageDown:
1358 switch ( event->key() )
1364 case Qt::Key_PageUp:
1365 case Qt::Key_PageDown:
1379 mDepthBufferImage = depthImage;
1380 mDepthBufferIsReady =
true;
1381 mDepthBufferNonVoidAverage = -1;
1389 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1391 handleTerrainNavigationWheelZoom();
1395bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1397 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1400void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1402 if ( newOperation == mCurrentOperation )
1407 if ( newOperation == MouseOperation::None )
1409 mClickPoint = QPoint();
1417 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1419 mClickPoint = clickPoint;
1423 mCurrentOperation = newOperation;
1424 mDepthBufferIsReady =
false;
1425 mRotationCenterCalculated =
false;
1426 mDragPointCalculated =
false;
1427 mZoomPointCalculated =
false;
1429 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1431 mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
1444 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1445 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1446 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1447 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1451 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)