24 #include <QDomDocument>
25 #include <Qt3DRender/QCamera>
26 #include <Qt3DRender/QObjectPicker>
27 #include <Qt3DRender/QPickEvent>
34 , mMouseDevice( new
Qt3DInput::QMouseDevice() )
35 , mKeyboardDevice( new
Qt3DInput::QKeyboardDevice() )
36 , mMouseHandler( new
Qt3DInput::QMouseHandler )
37 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
39 mMouseHandler->setSourceDevice( mMouseDevice );
40 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
41 this, &QgsCameraController::onPositionChanged );
42 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
43 this, &QgsCameraController::onWheel );
44 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
45 this, &QgsCameraController::onMousePressed );
46 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
47 this, &QgsCameraController::onMouseReleased );
48 addComponent( mMouseHandler );
50 mKeyboardHandler->setSourceDevice( mKeyboardDevice );
51 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::pressed,
52 this, &QgsCameraController::onKeyPressed );
53 connect( mKeyboardHandler, &Qt3DInput::QKeyboardHandler::released,
54 this, &QgsCameraController::onKeyReleased );
55 addComponent( mKeyboardHandler );
58 connect(
this, &Qt3DCore::QEntity::enabledChanged,
59 mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
60 connect(
this, &Qt3DCore::QEntity::enabledChanged,
61 mKeyboardHandler, &Qt3DInput::QMouseHandler::setEnabled );
63 mFpsNavTimer =
new QTimer(
this );
64 mFpsNavTimer->setInterval( 10 );
65 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
66 mFpsNavTimer->start();
71 if ( navigationMode == mCameraNavigationMode )
74 mCameraNavigationMode = navigationMode;
75 mIgnoreNextMouseMove =
true;
80 if ( movementSpeed == mCameraMovementSpeed )
83 mCameraMovementSpeed = movementSpeed;
89 mVerticalAxisInversion = inversion;
97 connect( te->terrainPicker(), &Qt3DRender::QObjectPicker::pressed,
this, &QgsCameraController::onPickerMousePressed );
123 static QVector3D unproject( QVector3D v,
const QMatrix4x4 &modelView,
const QMatrix4x4 &projection, QRect viewport )
130 const QMatrix4x4 inverse = QMatrix4x4( projection * modelView ).inverted();
132 QVector4D tmp( v, 1.0f );
133 tmp.setX( ( tmp.x() -
float( viewport.x() ) ) /
float( viewport.width() ) );
134 tmp.setY( ( tmp.y() -
float( viewport.y() ) ) /
float( viewport.height() ) );
135 tmp = tmp * 2.0f - QVector4D( 1.0f, 1.0f, 1.0f, 1.0f );
137 QVector4D obj = inverse * tmp;
141 return obj.toVector3D();
147 const float d_x = x1 - x0;
148 const float d_y = y1 - y0;
149 const float k = ( y - y0 ) / d_y;
156 const QVector3D l0 = unproject( QVector3D( pt.x(), viewport.height() - pt.y(), 0 ), camera->viewMatrix(), camera->projectionMatrix(), viewport );
157 const QVector3D l1 = unproject( QVector3D( pt.x(), viewport.height() - pt.y(), 1 ), camera->viewMatrix(), camera->projectionMatrix(), viewport );
159 const QVector3D p0( 0, y, 0 );
160 const QVector3D n( 0, 1, 0 );
161 const QVector3D l = l1 - l0;
162 const float d = QVector3D::dotProduct( p0 - l0, n ) / QVector3D::dotProduct( l, n );
163 const QVector3D p = d * l + l0;
165 return QPointF( p.x(), p.z() );
169 void QgsCameraController::rotateCamera(
float diffPitch,
float diffYaw )
174 if (
pitch + diffPitch > 180 )
175 diffPitch = 180 -
pitch;
176 if (
pitch + diffPitch < 0 )
177 diffPitch = 0 -
pitch;
184 const QQuaternion q = QQuaternion::fromEulerAngles(
pitch + diffPitch,
yaw + diffYaw, 0 ) *
185 QQuaternion::fromEulerAngles(
pitch,
yaw, 0 ).conjugated();
188 const QVector3D position = mCamera->position();
189 QVector3D viewCenter = mCamera->viewCenter();
190 const QVector3D viewVector = viewCenter - position;
191 const QVector3D cameraToCenter = q * viewVector;
192 viewCenter = position + cameraToCenter;
213 if ( mTerrainEntity )
221 mCamera->setNearPlane(
distance / 2 );
222 mCamera->setFarPlane(
distance * 2 );
244 if ( camPose == mCameraPose )
247 mCameraPose = camPose;
257 QDomElement elemCamera = doc.createElement( QStringLiteral(
"camera" ) );
258 elemCamera.setAttribute( QStringLiteral(
"x" ), mCameraPose.
centerPoint().
x() );
259 elemCamera.setAttribute( QStringLiteral(
"y" ), mCameraPose.
centerPoint().
z() );
260 elemCamera.setAttribute( QStringLiteral(
"elev" ), mCameraPose.
centerPoint().
y() );
262 elemCamera.setAttribute( QStringLiteral(
"pitch" ), mCameraPose.
pitchAngle() );
263 elemCamera.setAttribute( QStringLiteral(
"yaw" ), mCameraPose.
headingAngle() );
269 const float x = elem.attribute( QStringLiteral(
"x" ) ).toFloat();
270 const float y = elem.attribute( QStringLiteral(
"y" ) ).toFloat();
271 const float elev = elem.attribute( QStringLiteral(
"elev" ) ).toFloat();
272 const float dist = elem.attribute( QStringLiteral(
"dist" ) ).toFloat();
273 const float pitch = elem.attribute( QStringLiteral(
"pitch" ) ).toFloat();
274 const float yaw = elem.attribute( QStringLiteral(
"yaw" ) ).toFloat();
278 void QgsCameraController::updateCameraFromPose(
bool centerPointChanged )
283 qWarning() <<
"camera position got NaN!";
297 if ( mCamera && mTerrainEntity && centerPointChanged )
301 QVector3D intersectionPoint;
302 const QgsRayCastingUtils::Ray3D ray = QgsRayCastingUtils::rayForCameraCenter( mCamera );
303 if ( mTerrainEntity->rayIntersection( ray, intersectionPoint ) )
305 const float dist = ( intersectionPoint - mCamera->position() ).length();
313 centerPoint.
set( centerPoint.
x(), mTerrainEntity->terrainElevationOffset(), centerPoint.
z() );
319 if ( mCamera && !mTerrainEntity && centerPointChanged )
322 centerPoint.
set( centerPoint.
x(), 0, centerPoint.
z() );
330 void QgsCameraController::moveCameraPositionBy(
const QVector3D &posDiff )
348 void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
350 switch ( mCameraNavigationMode )
353 onPositionChangedTerrainNavigation( mouse );
357 onPositionChangedFlyNavigation( mouse );
362 void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
364 if ( mIgnoreNextMouseMove )
366 mIgnoreNextMouseMove =
false;
367 mMousePos = QPoint( mouse->x(), mouse->y() );
371 const int dx = mouse->x() - mMousePos.x();
372 const int dy = mouse->y() - mMousePos.y();
374 const bool hasShift = ( mouse->modifiers() & Qt::ShiftModifier );
375 const bool hasCtrl = ( mouse->modifiers() & Qt::ControlModifier );
376 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
377 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
378 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
380 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
389 updateCameraFromPose();
391 else if ( hasLeftButton && hasCtrl && !hasShift )
394 const float diffPitch = 0.2f * dy;
395 const float diffYaw = - 0.2f * dx;
396 rotateCamera( diffPitch, diffYaw );
397 updateCameraFromPose(
true );
399 else if ( hasLeftButton && !hasShift && !hasCtrl )
405 const float z = mLastPressedHeight;
410 center.
set( center.
x() - ( p2.x() - p1.x() ), center.
y(), center.
z() - ( p2.y() - p1.y() ) );
412 updateCameraFromPose(
true );
414 else if ( hasRightButton && !hasShift && !hasCtrl )
419 mMousePos = QPoint( mouse->x(), mouse->y() );
428 dist -= dist * factor * 0.01f;
430 updateCameraFromPose();
433 void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
435 switch ( mCameraNavigationMode )
439 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) ? 0.1f : 1.0f ) / 1000.f;
440 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
446 const float scaling = ( ( wheel->modifiers() & Qt::ControlModifier ) ? 0.1f : 1.0f ) / 1000.f;
448 dist -= dist * scaling * wheel->angleDelta().y();
450 updateCameraFromPose();
456 void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
459 mKeyboardHandler->setFocus(
true );
460 if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton || mouse->button() == Qt3DInput::QMouseEvent::MiddleButton )
462 mMousePos = QPoint( mouse->x(), mouse->y() );
463 mPressedButton = mouse->button();
464 mMousePressed =
true;
465 if ( mCaptureFpsMouseMovements )
466 mIgnoreNextMouseMove =
true;
470 void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
473 mPressedButton = Qt3DInput::QMouseEvent::NoButton;
474 mMousePressed =
false;
477 void QgsCameraController::onKeyPressed( Qt3DInput::QKeyEvent *event )
479 if ( event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_QuoteLeft )
482 switch ( mCameraNavigationMode )
484 case NavigationMode::WalkNavigation:
487 case NavigationMode::TerrainBasedNavigation:
495 switch ( mCameraNavigationMode )
499 onKeyPressedFlyNavigation( event );
505 onKeyPressedTerrainNavigation( event );
511 void QgsCameraController::onKeyPressedTerrainNavigation( Qt3DInput::QKeyEvent *event )
513 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
514 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
516 int tx = 0, ty = 0, tElev = 0;
517 switch ( event->key() )
533 case Qt::Key_PageDown:
543 if ( !hasShift && !hasCtrl )
547 else if ( hasShift && !hasCtrl )
553 else if ( hasCtrl && !hasShift )
556 const float diffPitch = ty;
557 const float diffYaw = -tx;
558 rotateCamera( diffPitch, diffYaw );
559 updateCameraFromPose(
true );
566 center.
set( center.
x(), center.
y() + tElev * 10, center.
z() );
568 updateCameraFromPose(
true );
572 void QgsCameraController::onKeyPressedFlyNavigation( Qt3DInput::QKeyEvent *event )
574 switch ( event->key() )
576 case Qt::Key_QuoteLeft:
579 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
580 mIgnoreNextMouseMove =
true;
581 if ( mCaptureFpsMouseMovements )
583 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
587 qApp->restoreOverrideCursor();
595 if ( mCaptureFpsMouseMovements )
597 mCaptureFpsMouseMovements =
false;
598 mIgnoreNextMouseMove =
true;
599 qApp->restoreOverrideCursor();
609 if ( event->isAutoRepeat() )
612 mDepressedKeys.insert( event->key() );
615 void QgsCameraController::applyFlyModeKeyMovements()
617 const QVector3D cameraUp = mCamera->upVector().normalized();
619 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
621 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
624 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
625 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
627 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
629 bool changed =
false;
630 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
633 cameraPosDiff += movementSpeed * cameraLeft;
636 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
639 cameraPosDiff += - movementSpeed * cameraLeft;
642 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
645 cameraPosDiff += movementSpeed * cameraFront;
648 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
651 cameraPosDiff += - movementSpeed * cameraFront;
656 static constexpr
double ELEVATION_MOVEMENT_SCALE = 0.5;
657 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
660 cameraPosDiff += ELEVATION_MOVEMENT_SCALE * movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
663 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
666 cameraPosDiff += ELEVATION_MOVEMENT_SCALE * - movementSpeed * QVector3D( 0.0f, 1.0f, 0.0f );
670 moveCameraPositionBy( cameraPosDiff );
673 void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
675 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
676 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
678 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
679 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
680 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
682 if ( mIgnoreNextMouseMove )
684 mIgnoreNextMouseMove =
false;
688 if ( hasMiddleButton )
691 const QVector3D cameraUp = mCamera->upVector().normalized();
693 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
694 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
695 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
697 else if ( hasRightButton )
701 const QVector3D cameraPosDiff = dy * cameraFront;
702 moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
706 if ( mCaptureFpsMouseMovements )
708 float diffPitch = -0.2f * dy;
709 switch ( mVerticalAxisInversion )
720 const float diffYaw = - 0.2f * dx;
721 rotateCamera( diffPitch, diffYaw );
722 updateCameraFromPose(
false );
724 else if ( mouse->buttons() & Qt::LeftButton )
726 float diffPitch = -0.2f * dy;
727 switch ( mVerticalAxisInversion )
737 const float diffYaw = - 0.2f * dx;
738 rotateCamera( diffPitch, diffYaw );
739 updateCameraFromPose(
false );
743 if ( mCaptureFpsMouseMovements )
745 mIgnoreNextMouseMove =
true;
748 emit
setCursorPosition( QPoint( mViewport.width() / 2, mViewport.height() / 2 ) );
752 void QgsCameraController::onKeyReleased( Qt3DInput::QKeyEvent *event )
754 if ( event->isAutoRepeat() )
757 mDepressedKeys.remove( event->key() );
760 void QgsCameraController::onPickerMousePressed( Qt3DRender::QPickEvent *pick )
762 mLastPressedHeight = pick->worldIntersection().y();
772 updateCameraFromPose();
781 updateCameraFromPose();
782 qInfo() <<
"Delta yaw: " << deltaYaw;
783 qInfo() <<
"Yaw: " <<
yaw;
789 updateCameraFromPose();
796 const float x = tx * dist * 0.02f;
797 const float y = -ty * dist * 0.02f;
800 const float t = sqrt( x * x + y * y );
801 const float a = atan2( y, x ) -
yaw * M_PI / 180;
802 const float dx = cos( a ) * t;
803 const float dy = sin( a ) * t;
806 center.
set( center.
x() + dx, center.
y(), center.
z() + dy );
808 updateCameraFromPose(
true );
813 if ( event->key() == Qt::Key_QuoteLeft )
816 switch ( mCameraNavigationMode )
820 switch ( event->key() )
832 case Qt::Key_PageDown:
837 if ( mCaptureFpsMouseMovements )
849 switch ( event->key() )
854 case Qt::Key_PageDown:
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse 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).
float yaw() const
Returns yaw angle in degrees.
void setCameraNavigationMode(QgsCameraController::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
void tiltUpAroundViewCenter(float deltaPitch)
Tilt up the view by deltaPitch around the view center (camera moves)
bool willHandleKeyEvent(QKeyEvent *event)
Returns true if the camera controller will handle the specified key event, preventing it from being i...
void navigationModeHotKeyPressed(QgsCameraController::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
Qt3DRender::QCamera * camera
void setVerticalAxisInversion(QgsCameraController::VerticalAxisInversion inversion)
Sets the vertical axis inversion behavior.
float distance() const
Returns distance of the camera from the point it is looking at.
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene.
VerticalAxisInversion
Vertical axis inversion options.
@ WhenDragging
Invert vertical axis movements when dragging in first person modes.
@ Always
Always invert vertical axis movements.
@ Never
Never invert vertical axis movements.
void cameraChanged()
Emitted when camera has been updated.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
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.
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...
NavigationMode
The navigation mode used by the camera.
@ WalkNavigation
Uses WASD keys or arrows to navigate in walking (first person) manner.
@ TerrainBasedNavigation
The default navigation based on the terrain.
void zoom(float factor)
Zoom the map by factor.
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
void rotateAroundViewCenter(float deltaYaw)
Rotate clockwise the view by deltaYaw around the view center (camera moves)
QgsCameraController(Qt3DCore::QNode *parent=nullptr)
Constructs the camera controller with optional parent node that will take ownership.
void setCameraHeadingAngle(float angle)
Set camera heading to angle (used for rotating the view)
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void setCameraPose(const QgsCameraPose &camPose)
Sets camera pose.
void viewportChanged()
Emitted when viewport rectangle has been updated.
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.
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.
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.
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
void set(double x, double y, double z)
Sets vector coordinates.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QPointF screen_point_to_point_on_plane(QPointF pt, QRect viewport, Qt3DRender::QCamera *camera, float y)
float find_x_on_line(float x0, float y0, float x1, float y1, float y)