25 #include <QDomDocument>
26 #include <Qt3DRender/QCamera>
27 #include <Qt3DRender/QObjectPicker>
28 #include <Qt3DRender/QPickEvent>
35 , mCameraBeforeRotation( new
Qt3DRender::QCamera )
38 , mMouseDevice( new
Qt3DInput::QMouseDevice() )
39 , mKeyboardDevice( new
Qt3DInput::QKeyboardDevice() )
40 , mMouseHandler( new
Qt3DInput::QMouseHandler )
41 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
43 mMouseHandler->setSourceDevice( mMouseDevice );
44 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
45 this, &QgsCameraController::onPositionChanged );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
47 this, &QgsCameraController::onWheel );
48 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
49 this, &QgsCameraController::onMousePressed );
50 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
51 this, &QgsCameraController::onMouseReleased );
52 addComponent( mMouseHandler );
54 mKeyboardHandler->setSourceDevice( mKeyboardDevice );
55 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
56 this, &QgsCameraController::onKeyPressed );
57 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
58 this, &QgsCameraController::onKeyReleased );
59 addComponent( mKeyboardHandler );
62 connect(
this, &Qt3DCore::QEntity::enabledChanged,
63 mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
64 connect(
this, &Qt3DCore::QEntity::enabledChanged,
65 mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
67 mFpsNavTimer =
new QTimer(
this );
68 mFpsNavTimer->setInterval( 10 );
69 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
70 mFpsNavTimer->start();
75 if ( navigationMode == mCameraNavigationMode )
78 mCameraNavigationMode = navigationMode;
79 mIgnoreNextMouseMove =
true;
85 if ( movementSpeed == mCameraMovementSpeed )
88 mCameraMovementSpeed = movementSpeed;
94 mVerticalAxisInversion = inversion;
101 if ( mTerrainEntity )
102 connect( te->terrainPicker(), &Qt3DRender::QObjectPicker::pressed,
this, &QgsCameraController::onPickerMousePressed );
127 void QgsCameraController::rotateCamera(
float diffPitch,
float diffYaw )
132 if (
pitch + diffPitch > 180 )
133 diffPitch = 180 -
pitch;
134 if (
pitch + diffPitch < 0 )
135 diffPitch = 0 -
pitch;
142 const QQuaternion q = QQuaternion::fromEulerAngles(
pitch + diffPitch,
yaw + diffYaw, 0 ) *
143 QQuaternion::fromEulerAngles(
pitch,
yaw, 0 ).conjugated();
146 const QVector3D position = mCamera->position();
147 QVector3D viewCenter = mCamera->viewCenter();
148 const QVector3D viewVector = viewCenter - position;
149 const QVector3D cameraToCenter = q * viewVector;
150 viewCenter = position + cameraToCenter;
155 updateCameraFromPose();
172 if ( mTerrainEntity )
180 mCamera->setNearPlane(
distance / 2 );
181 mCamera->setFarPlane(
distance * 2 );
203 if ( camPose == mCameraPose )
206 mCameraPose = camPose;
216 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
217 elemCamera.setAttribute( QStringLiteral(
"x" ), mCameraPose.
centerPoint().
x() );
218 elemCamera.setAttribute( QStringLiteral(
"y" ), mCameraPose.
centerPoint().
z() );
219 elemCamera.setAttribute( QStringLiteral(
"elev" ), mCameraPose.
centerPoint().
y() );
221 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
222 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
228 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
229 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
230 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
231 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
232 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
233 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
237 double QgsCameraController::sampleDepthBuffer(
const QImage &buffer,
int px,
int py )
242 for (
int x = px - 3; x <= px + 3; ++x )
244 for (
int y = py - 3; y <= py + 3; ++y )
246 if ( buffer.valid( x, y ) )
258 int samplesCount = 0;
259 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
261 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
273 if ( samplesCount == 0 )
276 depth /= samplesCount;
281 void QgsCameraController::updateCameraFromPose()
288 void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
291 updateCameraFromPose();
294 void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
296 mIsInZoomInState =
false;
297 mCumulatedWheelY = 0;
298 switch ( mCameraNavigationMode )
301 onPositionChangedTerrainNavigation( mouse );
305 onPositionChangedFlyNavigation( mouse );
310 void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
312 if ( mIgnoreNextMouseMove )
314 mIgnoreNextMouseMove =
false;
315 mMousePos = QPoint( mouse->x(), mouse->y() );
319 const int dx = mouse->x() - mMousePos.x();
320 const int dy = mouse->y() - mMousePos.y();
322 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
323 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
324 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
325 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
326 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
328 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
332 double scale = std::max( mViewport.width(), mViewport.height() );
333 float pitchDiff = 180 * ( mouse->y() - mMiddleButtonClickPos.y() ) / scale;
334 float yawDiff = -180 * ( mouse->x() - mMiddleButtonClickPos.x() ) / scale;
336 if ( !mDepthBufferIsReady )
339 if ( !mRotationCenterCalculated )
341 double depth = sampleDepthBuffer( mDepthBufferImage, mMiddleButtonClickPos.x(), mMiddleButtonClickPos.y() );
345 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBeforeRotation->position() ).length();
348 mRotationCenterCalculated =
true;
353 QVector3D shiftVector = mRotationCenter - mCamera->viewCenter();
355 QVector3D newViewCenterWorld =
camera()->viewCenter() + shiftVector;
356 QVector3D newCameraPosition =
camera()->position() + shiftVector;
362 updateCameraFromPose();
370 QVector3D clickedPositionWorld = ray.
origin() + mRotationDistanceFromCenter * ray.
direction();
372 QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
374 QVector3D newViewCenterWorld =
camera()->viewCenter() - shiftVector;
375 QVector3D newCameraPosition =
camera()->position() - shiftVector;
379 updateCameraFromPose();
382 else if ( hasLeftButton && hasCtrl && !hasShift )
385 const float diffPitch = 0.2f * dy;
386 const float diffYaw = - 0.2f * dx;
387 rotateCamera( diffPitch, diffYaw );
389 else if ( hasLeftButton && !hasShift && !hasCtrl )
393 if ( !mDepthBufferIsReady )
396 if ( !mDragPointCalculated )
398 double depth = sampleDepthBuffer( mDepthBufferImage, mDragButtonClickPos.x(), mDragButtonClickPos.y() );
403 mDragPointCalculated =
true;
406 QVector3D cameraBeforeDragPos = mCameraBeforeDrag->position();
409 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBeforeDrag->position() ).normalized();
410 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBeforeDrag->position() ).normalized();
413 if ( cameraBeforeToMoveToPos.y() == 0 )
415 cameraBeforeToMoveToPos.setY( 0.01 );
416 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
419 if ( cameraBeforeToDragPointPos.y() == 0 )
421 cameraBeforeToDragPointPos.setY( 0.01 );
422 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
425 double d1 = ( mDragPoint.y() - cameraBeforeDragPos.y() ) / cameraBeforeToMoveToPos.y();
426 double d2 = ( mDragPoint.y() - cameraBeforeDragPos.y() ) / cameraBeforeToDragPointPos.y();
428 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
429 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
431 QVector3D shiftVector = to - from;
433 mCameraPose.
setCenterPoint( mCameraBeforeDrag->viewCenter() + shiftVector );
434 updateCameraFromPose();
436 else if ( hasRightButton && !hasShift && !hasCtrl )
438 if ( !mDepthBufferIsReady )
441 if ( !mDragPointCalculated )
443 double depth = sampleDepthBuffer( mDepthBufferImage, mDragButtonClickPos.x(), mDragButtonClickPos.y() );
446 mDragPointCalculated =
true;
449 float dist = ( mCameraBeforeDrag->position() - mDragPoint ).length();
452 if ( mMousePos.y() > mDragButtonClickPos.y() )
454 double f = ( double )( mMousePos.y() - mDragButtonClickPos.y() ) / (
double )( mViewport.height() - mDragButtonClickPos.y() );
455 f = std::max( 0.0, std::min( 1.0, f ) );
456 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
461 double f = 1 - ( double )( mMousePos.y() ) / (
double )( mDragButtonClickPos.y() );
462 f = std::max( 0.0, std::min( 1.0, f ) );
463 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
464 dist = dist + 2 * dist * f;
469 QVector3D shiftVector = mDragPoint - mCamera->viewCenter();
471 QVector3D newViewCenterWorld =
camera()->viewCenter() + shiftVector;
475 updateCameraFromPose();
481 QVector3D clickedPositionWorld = ray.
origin() + dist * ray.
direction();
483 QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
485 QVector3D newViewCenterWorld =
camera()->viewCenter() - shiftVector;
486 QVector3D newCameraPosition =
camera()->position() - shiftVector;
490 updateCameraFromPose();
494 mMousePos = QPoint( mouse->x(), mouse->y() );
501 dist -= dist * factor * 0.01f;
503 updateCameraFromPose();
506 void QgsCameraController::handleTerrainNavigationWheelZoom()
508 if ( !mDepthBufferIsReady )
511 if ( !mZoomPointCalculated )
513 double depth = sampleDepthBuffer( mDepthBufferImage, mMousePos.x(), mMousePos.y() );
516 mZoomPointCalculated =
true;
519 float f = mCumulatedWheelY / ( 120.0 * 24.0 );
521 double dist = ( mZoomPoint - mCameraBeforeZoom->position() ).length();
526 QVector3D shiftVector = mZoomPoint - mCamera->viewCenter();
528 QVector3D newViewCenterWorld =
camera()->viewCenter() + shiftVector;
532 updateCameraFromPose();
538 QVector3D clickedPositionWorld = ray.
origin() + dist * ray.
direction();
540 QVector3D shiftVector = clickedPositionWorld - mCamera->viewCenter();
542 QVector3D newViewCenterWorld =
camera()->viewCenter() - shiftVector;
543 QVector3D newCameraPosition =
camera()->position() - shiftVector;
547 updateCameraFromPose();
549 mIsInZoomInState =
false;
550 mCumulatedWheelY = 0;
553 void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
555 switch ( mCameraNavigationMode )
559 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
560 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
567 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) != 0 ? 0.5f : 5.f );
571 mCumulatedWheelY += scaling * wheel->angleDelta().y();
573 if ( !mIsInZoomInState )
577 mCameraBeforeZoom->setProjectionMatrix( mCamera->projectionMatrix() );
578 mCameraBeforeZoom->setNearPlane( mCamera->nearPlane() );
579 mCameraBeforeZoom->setFarPlane( mCamera->farPlane() );
580 mCameraBeforeZoom->setAspectRatio( mCamera->aspectRatio() );
581 mCameraBeforeZoom->setFieldOfView( mCamera->fieldOfView() );
583 mZoomPointCalculated =
false;
584 mIsInZoomInState =
true;
585 mDepthBufferIsReady =
false;
589 handleTerrainNavigationWheelZoom();
596 void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
598 mKeyboardHandler->setFocus(
true );
599 if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
601 mMousePos = QPoint( mouse->x(), mouse->y() );
602 mDragButtonClickPos = QPoint( mouse->x(), mouse->y() );
603 mPressedButton = mouse->button();
604 mMousePressed =
true;
606 if ( mCaptureFpsMouseMovements )
607 mIgnoreNextMouseMove =
true;
611 mCameraBeforeDrag->setProjectionMatrix( mCamera->projectionMatrix() );
612 mCameraBeforeDrag->setNearPlane( mCamera->nearPlane() );
613 mCameraBeforeDrag->setFarPlane( mCamera->farPlane() );
614 mCameraBeforeDrag->setAspectRatio( mCamera->aspectRatio() );
615 mCameraBeforeDrag->setFieldOfView( mCamera->fieldOfView() );
617 mDepthBufferIsReady =
false;
618 mDragPointCalculated =
false;
623 if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton || ( ( mouse->modifiers() & Qt::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
625 mMousePos = QPoint( mouse->x(), mouse->y() );
626 mMiddleButtonClickPos = QPoint( mouse->x(), mouse->y() );
627 mPressedButton = mouse->button();
628 mMousePressed =
true;
629 if ( mCaptureFpsMouseMovements )
630 mIgnoreNextMouseMove =
true;
631 mDepthBufferIsReady =
false;
632 mRotationCenterCalculated =
false;
637 mCameraPose.
updateCamera( mCameraBeforeRotation.get() );
639 mCameraBeforeRotation->setProjectionMatrix( mCamera->projectionMatrix() );
640 mCameraBeforeRotation->setNearPlane( mCamera->nearPlane() );
641 mCameraBeforeRotation->setFarPlane( mCamera->farPlane() );
642 mCameraBeforeRotation->setAspectRatio( mCamera->aspectRatio() );
643 mCameraBeforeRotation->setFieldOfView( mCamera->fieldOfView() );
649 void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
652 mPressedButton = Qt3DInput::QMouseEvent::NoButton;
653 mMousePressed =
false;
655 mDragPointCalculated =
false;
656 mRotationCenterCalculated =
false;
659 void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
661 if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
664 switch ( mCameraNavigationMode )
666 case NavigationMode::WalkNavigation:
669 case NavigationMode::TerrainBasedNavigation:
676 switch ( mCameraNavigationMode )
680 onKeyPressedFlyNavigation( event );
686 onKeyPressedTerrainNavigation( event );
692 void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
694 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
695 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
697 int tx = 0, ty = 0, tElev = 0;
698 switch ( event->key() )
714 case Qt::Key_PageDown:
724 if ( !hasShift && !hasCtrl )
728 else if ( hasShift && !hasCtrl )
734 else if ( hasCtrl && !hasShift )
737 const float diffPitch = ty;
738 const float diffYaw = -tx;
739 rotateCamera( diffPitch, diffYaw );
746 center.
set( center.
x(), center.
y() + tElev * 10, center.
z() );
748 updateCameraFromPose();
752 void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
754 switch ( event->key() )
756 case Qt::Key_QuoteLeft:
759 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
760 mIgnoreNextMouseMove =
true;
761 if ( mCaptureFpsMouseMovements )
763 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
767 qApp->restoreOverrideCursor();
775 if ( mCaptureFpsMouseMovements )
777 mCaptureFpsMouseMovements =
false;
778 mIgnoreNextMouseMove =
true;
779 qApp->restoreOverrideCursor();
789 if ( event->isAutoRepeat() )
792 mDepressedKeys.insert( event->key() );
795 void QgsCameraController::applyFlyModeKeyMovements()
797 const QVector3D cameraUp = mCamera->upVector().normalized();
799 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
801 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
804 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
805 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
807 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
809 bool changed =
false;
810 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
813 cameraPosDiff += movementSpeed * cameraLeft;
816 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
819 cameraPosDiff += - movementSpeed * cameraLeft;
822 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
825 cameraPosDiff += movementSpeed * cameraFront;
828 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
831 cameraPosDiff += - movementSpeed * cameraFront;
836 static constexpr
double ELEVATION_MOVEMENT_SCALE = 0.5;
837 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
840 cameraPosDiff += ELEVATION_MOVEMENT_SCALE * movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
843 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
846 cameraPosDiff += ELEVATION_MOVEMENT_SCALE * - movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
850 moveCameraPositionBy( cameraPosDiff );
853 void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
855 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
856 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
858 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
859 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
860 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
862 if ( mIgnoreNextMouseMove )
864 mIgnoreNextMouseMove =
false;
868 if ( hasMiddleButton )
871 const QVector3D cameraUp = mCamera->upVector().normalized();
873 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
874 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
875 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
877 else if ( hasRightButton )
881 const QVector3D cameraPosDiff = dy * cameraFront;
882 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
886 if ( mCaptureFpsMouseMovements )
888 float diffPitch = -0.2f * dy;
889 switch ( mVerticalAxisInversion )
900 const float diffYaw = - 0.2f * dx;
901 rotateCamera( diffPitch, diffYaw );
903 else if ( mouse->buttons() & Qt::LeftButton )
905 float diffPitch = -0.2f * dy;
906 switch ( mVerticalAxisInversion )
916 const float diffYaw = - 0.2f * dx;
917 rotateCamera( diffPitch, diffYaw );
921 if ( mCaptureFpsMouseMovements )
923 mIgnoreNextMouseMove =
true;
926 emit
setCursorPosition( QPoint( mViewport.width() / 2, mViewport.height() / 2 ) );
930 void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
932 if ( event->isAutoRepeat() )
935 mDepressedKeys.remove( event->key() );
938 void QgsCameraController::onPickerMousePressed( Qt3DRender::QPickEvent *pick )
940 mLastPressedHeight = pick->worldIntersection().y();
950 updateCameraFromPose();
959 updateCameraFromPose();
965 updateCameraFromPose();
972 const float x = tx * dist * 0.02f;
973 const float y = -ty * dist * 0.02f;
976 const float t = sqrt( x * x + y * y );
977 const float a = atan2( y, x ) -
yaw * M_PI / 180;
978 const float dx = cos( a ) * t;
979 const float dy = sin( a ) * t;
982 center.
set( center.
x() + dx, center.
y(), center.
z() + dy );
984 updateCameraFromPose();
989 if ( event->key() == Qt::Key_QuoteLeft )
992 switch ( mCameraNavigationMode )
996 switch ( event->key() )
1006 case Qt::Key_PageUp:
1008 case Qt::Key_PageDown:
1012 case Qt::Key_Escape:
1013 if ( mCaptureFpsMouseMovements )
1025 switch ( event->key() )
1029 case Qt::Key_PageUp:
1030 case Qt::Key_PageDown:
1044 mDepthBufferImage = depthImage;
1045 mDepthBufferIsReady =
true;
1047 if ( mIsInZoomInState )
1048 handleTerrainNavigationWheelZoom();