33#include <QDomDocument>
36#include <QStringLiteral>
38#include <Qt3DRender/QCamera>
40#include "moc_qgscameracontroller.cpp"
42using namespace Qt::StringLiterals;
47 , mCamera( scene->engine()->
camera() )
49 , mMouseHandler( new
Qt3DInput::QMouseHandler )
50 , mKeyboardHandler( new
Qt3DInput::QKeyboardHandler )
51 , mOrigin( scene->mapSettings()->
origin() )
53 mMouseHandler->setSourceDevice(
new Qt3DInput::QMouseDevice() );
54 connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
this, &QgsCameraController::onPositionChanged );
55 connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel,
this, &QgsCameraController::onWheel );
56 connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed,
this, &QgsCameraController::onMousePressed );
57 connect( mMouseHandler, &Qt3DInput::QMouseHandler::released,
this, &QgsCameraController::onMouseReleased );
58 addComponent( mMouseHandler );
61 connect(
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
62 connect(
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
64 mFpsNavTimer =
new QTimer(
this );
65 mFpsNavTimer->setInterval( 10 );
66 connect( mFpsNavTimer, &QTimer::timeout,
this, &QgsCameraController::applyFlyModeKeyMovements );
67 mFpsNavTimer->start();
72 mGlobeCrsToLatLon =
QgsCoordinateTransform( mScene->mapSettings()->crs(), mScene->mapSettings()->crs().toGeographicCrs(), mScene->mapSettings()->transformContext() );
78QWindow *QgsCameraController::window()
const
81 return windowEngine ? windowEngine->
window() :
nullptr;
86 if ( navigationMode == mCameraNavigationMode )
89 mCameraNavigationMode = navigationMode;
90 mIgnoreNextMouseMove =
true;
96 if ( movementSpeed == mCameraMovementSpeed )
101 mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
107 mVerticalAxisInversion = inversion;
112 float newPitch = mCameraPose.pitchAngle() + diffPitch;
113 float newHeading = mCameraPose.headingAngle() + diffHeading;
115 newPitch = std::clamp( newPitch, 0.f, 180.f );
117 switch ( mScene->mapSettings()->sceneMode() )
128 viewCenterLatLon = mGlobeCrsToLatLon.transform( mCameraPose.centerPoint() + mOrigin );
132 QgsDebugError( u
"rotateCamera: ECEF -> lat,lon transform failed!"_s );
135 QQuaternion qLatLon = QQuaternion::fromAxisAndAngle( QVector3D( 0, 0, 1 ),
static_cast<float>( viewCenterLatLon.
x() ) )
136 * QQuaternion::fromAxisAndAngle( QVector3D( 0, -1, 0 ),
static_cast<float>( viewCenterLatLon.
y() ) );
137 QQuaternion qPitchHeading = QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), newHeading ) * QQuaternion::fromAxisAndAngle( QVector3D( 0, 1, 0 ), newPitch );
138 QVector3D newCameraToCenter = ( qLatLon * qPitchHeading * QVector3D( -1, 0, 0 ) ) * mCameraPose.distanceFromCenterPoint();
140 mCameraPose.setCenterPoint( mCamera->position() + newCameraToCenter );
141 mCameraPose.setPitchAngle( newPitch );
142 mCameraPose.setHeadingAngle( newHeading );
143 updateCameraFromPose();
150 QVector3D newCameraToCenter = q * QVector3D( 0, 0, -mCameraPose.distanceFromCenterPoint() );
151 mCameraPose.setCenterPoint( mCamera->position() + newCameraToCenter );
152 mCameraPose.setPitchAngle( newPitch );
153 mCameraPose.setHeadingAngle( newHeading );
154 updateCameraFromPose();
162 const float oldPitch = mCameraPose.pitchAngle();
163 const float oldHeading = mCameraPose.headingAngle();
165 newPitch = std::clamp( newPitch, 0.f, 180.f );
171 const QQuaternion q = qNew * qOld.conjugated();
173 const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
175 mCameraPose.setCenterPoint( newViewCenter );
176 mCameraPose.setPitchAngle( newPitch );
177 mCameraPose.setHeadingAngle( newHeading );
178 updateCameraFromPose();
184 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
185 double newDistance = mCameraPose.distanceFromCenterPoint() * zoomFactor;
189 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
190 QVector3D newViewCenter = newCamPosition + cameraToCenter;
192 mCameraPose.setDistanceFromCenterPoint( newDistance );
193 mCameraPose.setCenterPoint( newViewCenter );
194 updateCameraFromPose();
200 QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
201 const double newDistance = oldDistanceFromCenterPoint * zoomFactor;
205 QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
206 QVector3D newViewCenter = newCamPosition + cameraToCenter;
208 mCameraPose.setDistanceFromCenterPoint( newDistance );
209 mCameraPose.setCenterPoint( newViewCenter );
210 updateCameraFromPose();
218 updateOrthographicProjectionPlane();
220 if ( mCameraChanged )
223 mCameraChanged =
false;
229 QgsPointXY extentCenter = mScene->mapSettings()->extent().center();
238 QgsDebugError( u
"setViewFromTop() should not be used with globe!"_s );
243 QgsTerrainEntity *terrain = mScene->terrainEntity();
244 const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
250 mCamera->setNearPlane(
distance / 2 );
251 mCamera->setFarPlane(
distance * 2 );
258 return mCameraPose.centerPoint();
283 if ( camPose == mCameraPose && !force )
286 mCameraPose = camPose;
287 updateCameraFromPose();
292 QDomElement elemCamera = doc.createElement( u
"camera"_s );
295 QgsVector3D centerPoint = mCameraPose.centerPoint() + mOrigin;
296 elemCamera.setAttribute( u
"xMap"_s, centerPoint.
x() );
297 elemCamera.setAttribute( u
"yMap"_s, centerPoint.
y() );
298 elemCamera.setAttribute( u
"zMap"_s, centerPoint.
z() );
299 elemCamera.setAttribute( u
"dist"_s, mCameraPose.distanceFromCenterPoint() );
300 elemCamera.setAttribute( u
"pitch"_s, mCameraPose.pitchAngle() );
301 elemCamera.setAttribute( u
"yaw"_s, mCameraPose.headingAngle() );
307 const float dist = elem.attribute( u
"dist"_s ).toFloat();
308 const float pitch = elem.attribute( u
"pitch"_s ).toFloat();
309 const float yaw = elem.attribute( u
"yaw"_s ).toFloat();
312 if ( elem.hasAttribute(
"xMap" ) )
315 const double x = elem.attribute( u
"xMap"_s ).toDouble();
316 const double y = elem.attribute( u
"yMap"_s ).toDouble();
317 const double z = elem.attribute( u
"zMap"_s ).toDouble();
323 const double x = elem.attribute( u
"x"_s ).toDouble();
324 const double y = elem.attribute( u
"y"_s ).toDouble();
325 const double elev = elem.attribute( u
"elev"_s ).toDouble();
326 centerPoint =
QgsVector3D( x, elev, y ) - savedOrigin + mOrigin;
331double QgsCameraController::sampleDepthBuffer(
int px,
int py )
333 if ( !mDepthBufferIsReady )
335 QgsDebugError( u
"Asked to sample depth buffer, but depth buffer not ready!"_s );
340 if ( QWindow *win = window() )
344 px =
static_cast<int>( px * win->devicePixelRatio() );
345 py =
static_cast<int>( py * win->devicePixelRatio() );
349 for (
int x = px - 3; x <= px + 3; ++x )
351 for (
int y = py - 3; y <= py + 3; ++y )
353 if ( mDepthBufferImage.valid( x, y ) )
362double QgsCameraController::depthBufferNonVoidAverage()
365 if ( mDepthBufferNonVoidAverage != -1 )
366 return mDepthBufferNonVoidAverage;
370 int samplesCount = 0;
372 Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
373 for (
int y = 0; y < mDepthBufferImage.height(); ++y )
375 const QRgb *line =
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
376 for (
int x = 0; x < mDepthBufferImage.width(); ++x )
388 if ( samplesCount == 0 )
391 depth /= samplesCount;
393 mDepthBufferNonVoidAverage = depth;
398QgsVector3D QgsCameraController::moveGeocentricPoint(
const QgsVector3D &point,
double latDiff,
double lonDiff )
402 QgsVector3D pointLatLon = mGlobeCrsToLatLon.transform( point );
403 pointLatLon.
setX( pointLatLon.
x() + lonDiff );
404 pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
408 catch (
const QgsCsException & )
410 QgsDebugError( u
"moveGeocentricPoint: transform failed!"_s );
417 const QgsVector3D viewCenter = mCameraPose.centerPoint() + mOrigin;
418 const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
419 mCameraPose.setCenterPoint( newViewCenter - mOrigin );
420 updateCameraFromPose();
425 mCameraPose.setDistanceFromCenterPoint( mCameraPose.distanceFromCenterPoint() * factor );
426 updateCameraFromPose();
431 mCameraPose.setPitchAngle( std::clamp( mCameraPose.pitchAngle() + angleDiff, 0.f, 90.f ) );
432 updateCameraFromPose();
437 mCameraPose.setHeadingAngle( mCameraPose.headingAngle() + angleDiff );
438 updateCameraFromPose();
460void QgsCameraController::updateOrthographicProjectionPlane()
466 const QSize viewportRect = mScene->
engine()->
size();
467 const float viewWidthFromCenter =
distance();
468 const float viewHeightFromCenter =
distance() *
static_cast<float>( viewportRect.height() ) /
static_cast<float>( viewportRect.width() );
469 mCamera->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mCamera->nearPlane(), mCamera->farPlane() );
473void QgsCameraController::updateCameraFromPose()
479 const QgsVector3D viewCenter = mCameraPose.centerPoint() + mOrigin;
481 QgsVector3D viewCenterLatLon;
484 viewCenterLatLon = mGlobeCrsToLatLon.transform( viewCenter );
486 catch (
const QgsCsException & )
488 QgsDebugError( u
"updateCameraFromPose: transform failed!"_s );
492 mCameraPose.updateCameraGlobe( mCamera, viewCenterLatLon.
y(), viewCenterLatLon.
x() );
496 mCameraPose.updateCamera( mCamera );
499 updateOrthographicProjectionPlane();
501 mCameraChanged =
true;
507 mCameraPose.setCenterPoint( mCameraPose.centerPoint() + posDiff );
508 updateCameraFromPose();
511void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
513 if ( !mInputHandlersEnabled )
516 QgsScopedEvent traceEvent( u
"3D"_s, u
"QgsCameraController::onPositionChanged"_s );
518 switch ( mCameraNavigationMode )
521 onPositionChangedTerrainNavigation( mouse );
525 onPositionChangedFlyNavigation( mouse );
529 onPositionChangedGlobeTerrainNavigation( mouse );
534bool QgsCameraController::screenPointToWorldPos( QPoint position,
double &depth, QVector3D &worldPosition )
536 depth = sampleDepthBuffer( position.x(), position.y() );
542 depth = depthBufferNonVoidAverage();
546 if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
548 QgsDebugMsgLevel( u
"screenPointToWorldPos: position is NaN or Inf. This should not happen."_s, 2 );
555void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
557 if ( mIgnoreNextMouseMove )
559 mIgnoreNextMouseMove =
false;
560 mMousePos = QPoint( mouse->x(), mouse->y() );
564 const int dx = mouse->x() - mMousePos.x();
565 const int dy = mouse->y() - mMousePos.y();
567 const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
568 const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
569 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
570 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
571 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
573 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
576 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
578 float scale =
static_cast<float>( std::max( mScene->engine()->size().width(), mScene->engine()->size().height() ) );
579 float pitchDiff = -180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
580 float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
585 if ( !mDepthBufferIsReady )
588 if ( !mRotationCenterCalculated )
591 QVector3D worldPosition;
592 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
594 mRotationCenter = worldPosition;
595 mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
597 mRotationCenterCalculated =
true;
603 else if ( hasLeftButton && hasCtrl && !hasShift )
605 setMouseParameters( MouseOperation::RotationCamera );
607 float diffPitch = -0.2f *
static_cast<float>( dy );
608 const float diffYaw = -0.2f * dx;
613 else if ( hasLeftButton && !hasShift && !hasCtrl )
616 setMouseParameters( MouseOperation::Translation, mMousePos );
618 if ( !mDepthBufferIsReady )
621 if ( !mDragPointCalculated )
624 QVector3D worldPosition;
625 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
628 mDragPoint = worldPosition;
629 mDragPointCalculated =
true;
633 QVector3D cameraBeforeDragPos = mCameraBefore->position();
636 QVector3D shiftVector;
639 float angle = std::fabs( std::acos( QVector3D::dotProduct( QVector3D( 0, 0, 1 ), mCameraBefore->viewVector().normalized() ) ) - M_PI / 2 );
640 bool changeAltitude =
false;
642 switch ( mScene->mapSettings()->projectionType() )
645 changeAltitude =
angle < M_PI / 30;
648 changeAltitude =
angle < M_PI / 3;
654 if ( changeAltitude )
655 shiftVector = mDragPoint - moveToPosition;
657 switch ( mScene->mapSettings()->projectionType() )
664 shiftVector = { mDragPoint.x() - moveToPosition.x(), mDragPoint.y() - moveToPosition.y(), 0 };
669 QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
670 QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
673 if ( cameraBeforeToMoveToPos.z() == 0 )
675 cameraBeforeToMoveToPos.setZ( 0.01 );
676 cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
679 if ( cameraBeforeToDragPointPos.z() == 0 )
681 cameraBeforeToDragPointPos.setZ( 0.01 );
682 cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
685 float d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
686 float d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
688 QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
689 QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
691 shiftVector = to - from;
698 mCameraPose.setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
699 updateCameraFromPose();
701 else if ( hasLeftButton && hasShift && hasCtrl )
704 QgsVector3D center = mCameraPose.centerPoint();
705 double tElev = mMousePos.y() - mouse->y();
706 center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
707 mCameraPose.setCenterPoint( center );
708 updateCameraFromPose();
710 else if ( hasRightButton && !hasShift && !hasCtrl )
712 setMouseParameters( MouseOperation::Zoom, mMousePos );
713 if ( !mDepthBufferIsReady )
716 if ( !mDragPointCalculated )
719 QVector3D worldPosition;
720 if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
722 mDragPoint = worldPosition;
723 mDragPointCalculated =
true;
727 const double oldDist = ( QgsVector3D( mCameraBefore->position() ) - QgsVector3D( mDragPoint ) ).length();
728 double newDist = oldDist;
731 int screenHeight = mScene->engine()->size().height();
732 QWindow *win = window();
735 yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
736 screenHeight = win->screen()->virtualSize().height();
740 if ( mMousePos.y() > mClickPoint.y() )
742 double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / ( double ) ( screenHeight - mClickPoint.y() - yOffset );
743 f = std::max( 0.0, std::min( 1.0, f ) );
744 f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
745 newDist = newDist * f;
749 double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / ( double ) ( mClickPoint.y() + yOffset );
750 f = std::max( 0.0, std::min( 1.0, f ) );
751 f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
752 newDist = newDist + 2 * newDist * f;
755 const double zoomFactor = newDist / oldDist;
756 zoomCameraAroundPivot( mCameraBefore->position(), mCameraBefore->viewCenter().distanceToPoint( mCameraBefore->position() ), zoomFactor, mDragPoint );
759 mMousePos = QPoint( mouse->x(), mouse->y() );
762void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
764 const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
765 const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
766 const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
767 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
769 if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
771 setMouseParameters( MouseOperation::RotationCenter, mMousePos );
773 const float scale =
static_cast<float>( std::max( mScene->engine()->size().width(), mScene->engine()->size().height() ) );
774 float pitchDiff = -180.0f *
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
775 const float yawDiff = -180.0f *
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
780 mCameraPose.setPitchAngle( mRotationPitch + pitchDiff );
781 mCameraPose.setHeadingAngle( mRotationYaw + yawDiff );
782 updateCameraFromPose();
786 if ( !( mouse->buttons() & Qt::LeftButton ) )
790 setMouseParameters( MouseOperation::Translation, mMousePos );
792 if ( !mDepthBufferIsReady )
795 if ( !mDragPointCalculated )
798 QVector3D worldPosition;
799 if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
803 mDragPoint = worldPosition;
804 mDragPointCalculated =
true;
809 const QgsVector3D startPosMap = QgsVector3D( mDragPoint ) + mOrigin;
810 const double sphereRadiusMap = startPosMap.
length();
813 const QgsVector3D rayOriginMap = QgsVector3D( ray.
origin() ) + mOrigin;
817 const double quadC =
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
818 const double disc = quadB * quadB - 4 * quadA * quadC;
823 const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
824 if ( rayDistMap < 0 )
826 QgsDebugError( u
"Sphere intersection result negative, canceling move"_s );
829 const QgsVector3D newPosMap = rayOriginMap + QgsVector3D( ray.
direction() ) * rayDistMap;
834 QgsVector3D oldLatLon, newLatLon;
837 oldLatLon = mGlobeCrsToLatLon.transform( startPosMap );
838 newLatLon = mGlobeCrsToLatLon.transform( newPosMap );
840 catch (
const QgsCsException & )
842 QgsDebugError( u
"onPositionChangedGlobeTerrainNavigation: transform failed!"_s );
846 const double latDiff = oldLatLon.
y() - newLatLon.
y();
847 const double lonDiff = oldLatLon.
x() - newLatLon.
x();
849 const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
850 const QgsVector3D newVCWorld = newVC - mOrigin;
852 mCameraPose.setCenterPoint( newVCWorld );
853 updateCameraFromPose();
860 float dist = mCameraPose.distanceFromCenterPoint();
861 dist -= dist * factor * 0.01f;
862 mCameraPose.setDistanceFromCenterPoint( dist );
863 updateCameraFromPose();
866void QgsCameraController::handleTerrainNavigationWheelZoom()
868 if ( !mDepthBufferIsReady )
871 if ( !mZoomPointCalculated )
874 QVector3D worldPosition;
875 if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
877 mZoomPoint = worldPosition;
878 mZoomPointCalculated =
true;
882 double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
884 double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
886 newDist = std::max( newDist, 2.0 );
887 double zoomFactor = newDist / oldDist;
889 zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
891 zoomCameraAroundPivot( mCameraBefore->position(), mCameraBefore->viewCenter().distanceToPoint( mCameraBefore->position() ), zoomFactor, mZoomPoint );
893 mCumulatedWheelY = 0;
894 setMouseParameters( MouseOperation::None );
897void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
899 if ( !mInputHandlersEnabled )
902 switch ( mCameraNavigationMode )
906 const float scaling = ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
907 setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
915 const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1 : 1.0 );
919 mCumulatedWheelY += scaling * wheel->angleDelta().y();
921 if ( mCurrentOperation != MouseOperation::ZoomWheel )
923 setMouseParameters( MouseOperation::ZoomWheel );
928 handleTerrainNavigationWheelZoom();
935 float wheelAmount =
static_cast<float>( wheel->angleDelta().y() );
936 float factor = abs( wheelAmount ) / 1000.f;
937 float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
938 mCameraPose.setDistanceFromCenterPoint( mCameraPose.distanceFromCenterPoint() * mulFactor );
939 updateCameraFromPose();
945void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
947 if ( !mInputHandlersEnabled )
950 mKeyboardHandler->setFocus(
true );
952 if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton
953 || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton )
954 || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
956 mMousePos = QPoint( mouse->x(), mouse->y() );
958 if ( mCaptureFpsMouseMovements )
959 mIgnoreNextMouseMove =
true;
961 const MouseOperation operation {
962 ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera
963 : MouseOperation::RotationCenter
965 setMouseParameters( operation, mMousePos );
968 else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
970 mMousePos = QPoint( mouse->x(), mouse->y() );
972 if ( mCaptureFpsMouseMovements )
973 mIgnoreNextMouseMove =
true;
975 const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
976 setMouseParameters( operation, mMousePos );
980void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
983 if ( !mInputHandlersEnabled )
987 setMouseParameters( MouseOperation::None );
990bool QgsCameraController::onKeyPressedTerrainNavigation( QKeyEvent *event )
992 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
993 const bool hasCtrl = (
event->modifiers() & Qt::ControlModifier );
995 int tx = 0, ty = 0, tElev = 0;
996 switch ( event->key() )
1012 case Qt::Key_PageDown:
1015 case Qt::Key_PageUp:
1024 if ( !hasShift && !hasCtrl )
1028 else if ( hasShift && !hasCtrl )
1034 else if ( hasCtrl && !hasShift )
1037 const float diffPitch = ty;
1038 const float diffYaw = -tx;
1046 QgsVector3D center = mCameraPose.centerPoint();
1047 center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
1048 mCameraPose.setCenterPoint( center );
1055bool QgsCameraController::onKeyPressedGlobeTerrainNavigation( QKeyEvent *event )
1059 constexpr float MOVE_FACTOR = 0.000001f;
1060 constexpr float ZOOM_FACTOR = 0.9f;
1062 const bool hasShift = (
event->modifiers() & Qt::ShiftModifier );
1064 switch ( event->key() )
1090 case Qt::Key_PageDown:
1093 case Qt::Key_PageUp:
1102static const QSet<int> walkNavigationSavedKeys = {
1117bool QgsCameraController::onKeyPressedFlyNavigation( QKeyEvent *event )
1119 switch ( event->key() )
1121 case Qt::Key_QuoteLeft:
1124 mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
1125 mIgnoreNextMouseMove =
true;
1126 if ( mCaptureFpsMouseMovements )
1128 qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
1132 qApp->restoreOverrideCursor();
1138 case Qt::Key_Escape:
1141 if ( mCaptureFpsMouseMovements )
1143 mCaptureFpsMouseMovements =
false;
1144 mIgnoreNextMouseMove =
true;
1145 qApp->restoreOverrideCursor();
1155 if ( walkNavigationSavedKeys.contains( event->key() ) )
1157 if ( !event->isAutoRepeat() )
1159 mDepressedKeys.insert( event->key() );
1169 const QVector3D cameraUp = mCamera->upVector().normalized();
1170 const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
1171 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1173 QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
1177 cameraPosDiff +=
static_cast<float>( tx ) * cameraFront;
1181 cameraPosDiff +=
static_cast<float>( ty ) * cameraLeft;
1185 cameraPosDiff +=
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
1191void QgsCameraController::applyFlyModeKeyMovements()
1197 const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
1198 const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
1200 const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
1202 bool changed =
false;
1206 if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
1212 if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
1218 if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
1224 if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
1232 static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
1233 if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
1236 z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
1239 if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
1242 z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
1249void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
1251 const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
1252 const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
1254 const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
1255 const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
1256 mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
1258 if ( mIgnoreNextMouseMove )
1260 mIgnoreNextMouseMove =
false;
1264 if ( hasMiddleButton )
1267 const QVector3D cameraUp = mCamera->upVector().normalized();
1268 const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
1269 const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
1270 const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
1271 moveCenterPoint(
static_cast<float>( mCameraMovementSpeed ) * cameraPosDiff / 10.0 );
1273 else if ( hasRightButton )
1276 const QVector3D cameraFront = ( QVector3D( mCameraPose.centerPoint().x(), mCameraPose.centerPoint().y(), mCameraPose.centerPoint().z() ) - mCamera->position() ).normalized();
1277 const QVector3D cameraPosDiff = dy * cameraFront;
1278 moveCenterPoint(
static_cast<float>( mCameraMovementSpeed ) * cameraPosDiff / 5.0 );
1282 if ( mCaptureFpsMouseMovements )
1284 float diffPitch = -0.2f * dy;
1288 const float diffYaw = -0.2f * dx;
1291 else if ( mouse->buttons() & Qt::LeftButton )
1293 float diffPitch = -0.2f * dy;
1297 const float diffYaw = -0.2f * dx;
1302 if ( mCaptureFpsMouseMovements )
1304 mIgnoreNextMouseMove =
true;
1307 emit
setCursorPosition( QPoint( mScene->engine()->size().width() / 2, mScene->engine()->size().height() / 2 ) );
1314 float pitch = mCameraPose.pitchAngle();
1315 pitch -= deltaPitch;
1316 mCameraPose.setPitchAngle(
pitch );
1317 updateCameraFromPose();
1323 float yaw = mCameraPose.headingAngle();
1325 mCameraPose.setHeadingAngle(
yaw );
1326 updateCameraFromPose();
1331 mCameraPose.setHeadingAngle( angle );
1332 updateCameraFromPose();
1337 const float yaw = mCameraPose.headingAngle();
1338 const float dist = mCameraPose.distanceFromCenterPoint();
1339 const float x = tx * dist * 0.02f;
1340 const float y = -ty * dist * 0.02f;
1343 const float t = sqrt( x * x + y * y );
1344 const float a = atan2( y, x ) -
yaw * M_PI / 180;
1345 const float dx = cos( a ) * t;
1346 const float dy = sin( a ) * t;
1349 center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
1350 mCameraPose.setCenterPoint( center );
1351 updateCameraFromPose();
1356 if ( !mInputHandlersEnabled )
1359 if ( event->type() == QKeyEvent::Type::KeyRelease )
1361 if ( !event->isAutoRepeat() && mDepressedKeys.contains( event->key() ) )
1363 mDepressedKeys.remove( event->key() );
1367 else if ( event->type() == QEvent::ShortcutOverride )
1369 if ( event->modifiers() & Qt::ControlModifier )
1371 switch ( event->key() )
1373 case Qt::Key_QuoteLeft:
1376 switch ( mCameraNavigationMode )
1424 switch ( mCameraNavigationMode )
1427 return onKeyPressedFlyNavigation( event );
1430 return onKeyPressedTerrainNavigation( event );
1433 return onKeyPressedGlobeTerrainNavigation( event );
1441 mDepthBufferImage = depthImage;
1442 mDepthBufferIsReady =
true;
1443 mDepthBufferNonVoidAverage = -1;
1451 if ( mCurrentOperation == MouseOperation::ZoomWheel )
1453 handleTerrainNavigationWheelZoom();
1457bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
const
1459 return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate )
1460 && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
1463void QgsCameraController::setMouseParameters(
const MouseOperation &newOperation,
const QPoint &clickPoint )
1465 if ( newOperation == mCurrentOperation )
1470 if ( newOperation == MouseOperation::None )
1472 mClickPoint = QPoint();
1480 else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
1482 mClickPoint = clickPoint;
1483 mRotationPitch = mCameraPose.pitchAngle();
1484 mRotationYaw = mCameraPose.headingAngle();
1486 mCurrentOperation = newOperation;
1487 mDepthBufferIsReady =
false;
1488 mRotationCenterCalculated =
false;
1489 mDragPointCalculated =
false;
1490 mZoomPointCalculated =
false;
1492 if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
1494 mMousePressViewCenter = mCameraPose.centerPoint() + mOrigin;
1504 mCameraPose.setCenterPoint( mCameraPose.centerPoint() - diff );
1507 mCameraBefore->setPosition( (
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
1508 mCameraBefore->setViewCenter( (
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
1509 mDragPoint = (
QgsVector3D( mDragPoint ) - diff ).toVector3D();
1510 mRotationCenter = (
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
1514 updateCameraFromPose();
1517void QgsCameraController::rotateToRespectingTerrain(
float pitch,
float yaw )
1520 double elevation = 0.0;
1524 QVector3D camPos = mCamera->position();
1529 const QList<QgsRayCastHit> results = mScene->
terrainEntity()->rayIntersection( ray, context );
1531 if ( !results.isEmpty() )
1533 elevation = results.constFirst().mapCoordinates().z() - mOrigin.z();
1534 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
1541 pos.
set( pos.
x(), pos.
y(), elevation + mScene->terrainEntity()->terrainElevationOffset() );
1548 if ( !crossSection.
isValid() )
1553 const double width = crossSection.
halfWidth();
1555 const QgsVector3D startVec { startPoint.
x(), startPoint.
y(), 0 };
1558 QgsVector linePerpVec( ( endPoint - startPoint ).x(), ( endPoint - startPoint ).y() );
1561 const QgsVector3D linePerpVec3D( linePerpVec.
x(), linePerpVec.
y(), 0 );
1562 const QgsVector3D frontStartPoint( startVec + linePerpVec3D * width );
1563 const QgsVector3D frontEndPoint( endVec + linePerpVec3D * width );
@ WhenPivoting
When pivoting camera around point in terrain.
@ WhenRotatingCaptured
When rotating camera around self with mouse button pressed.
@ WhenRotatingDragging
When rotating camera around self with mouse captured.
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.
QFlags< VerticalAxisInversion > VerticalAxisInversionFlags
@ Orthographic
Orthogonal projection.
@ Perspective
Perspective projection.
@ 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.
QgsTerrainEntity * terrainEntity() SIP_SKIP
Returns terrain entity (may be nullptr if using globe scene, terrain rendering is disabled or when te...
Qgs3DMapSettings * mapSettings() const
Returns the 3D map settings.
QgsAbstract3DEngine * engine() const SIP_SKIP
Returns the abstract 3D engine.
Qgis::Map3DProjectionType projectionType() const
Returns the camera lens' projection type.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
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.
static QgsCameraPose lineSegmentToCameraPose(const QgsVector3D &startPoint, const QgsVector3D &endPoint, const QgsDoubleRange &elevationRange, float fieldOfView, const QgsVector3D &worldOrigin)
Returns the camera pose for a camera looking at mid-point between startPoint and endPoint.
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
void navigationModeChanged(Qgis::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
void rotateCameraToBottom()
Rotate to bottom-up view.
void setLookingAtMapPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets camera configuration like setLookingAtPoint(), but the point is given in map coordinates.
float pitch() const
Returns pitch angle in degrees (0 = looking from the top, 90 = looking from the side).
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
~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.
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.
Q_DECL_DEPRECATED 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 setVerticalAxisInversion(Qgis::VerticalAxisInversionFlags inversion)
Sets the vertical axis inversion behavior.
void cameraChanged()
Emitted when camera has been updated.
void moveCenterPoint(const QVector3D &posDiff)
Moves camera position by the given difference vector in world coordinates.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
void setCrossSectionSideView(const QgsCrossSection &crossSection)
Sets the cross section side view definition for the 3D map canvas.
QgsCameraController(Qgs3DMapScene *scene)
Constructs the camera controller with optional parent node that will take ownership.
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates).
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
QgsVector3D lookingAtMapPoint() const
Returns the point in the map coordinates towards which the camera is looking.
void rotateCameraToSouth()
Rotate to view from the south.
QDomElement writeXml(QDomDocument &doc) const
Writes camera configuration to the given DOM element.
void setViewFromTop(float worldX, float worldY, float distance, float yaw=0)
Sets camera to look down towards given point in world coordinate, in given distance from plane with z...
bool keyboardEventFilter(QKeyEvent *event)
If the event is relevant, handles the event and returns true, otherwise false.
void resetGlobe(float distance, double lat=0, double lon=0)
Resets view of the globe to look at a particular location given as latitude and longitude (in degrees...
void readXml(const QDomElement &elem, QgsVector3D savedOrigin)
Reads camera configuration from the given DOM element.
void zoom(float factor)
Zoom the map by factor.
void globeUpdateHeadingAngle(float angleDiff)
Updates heading angle by the specified amount given as the angular difference in degrees.
void rotateAroundViewCenter(float deltaYaw)
Rotate clockwise the view by deltaYaw around the view center (camera moves).
void walkView(double tx, double ty, double tz)
Walks into the map by tx, ty, and tz.
void setCameraPose(const QgsCameraPose &camPose, bool force=false)
Sets camera pose.
void setOrigin(const QgsVector3D &origin)
Reacts to the shift of origin of the scene, updating camera pose and any other member variables so th...
void setCameraHeadingAngle(float angle)
Set camera heading to angle (used for rotating the view).
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void rotateCameraAroundPivot(float newPitch, float newHeading, const QVector3D &pivotPoint)
Rotates the camera around the pivot point (in world coordinates) to the given new pitch and heading a...
void depthBufferCaptured(const QImage &depthImage)
Sets the depth buffer image used by the camera controller to calculate world position from a pixel's ...
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
void setCursorPosition(QPoint point)
Emitted when the mouse cursor position should be moved to the specified point on the map viewport.
void moveView(float tx, float ty)
Move the map by tx and ty.
Encapsulates camera pose in a 3D scene.
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.
Encapsulates the definition of a cross section in 3D map coordinates.
double halfWidth() const
Returns the half-width of the cross section.
QgsPoint endPoint() const
Returns the end point of the cross section.
bool isValid() const
Returns cross section validity.
QgsPoint startPoint() const
Returns the start point of the cross section.
Custom exception class for Coordinate Reference System related exceptions.
Point geometry type, with support for z-dimension and m-values.
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().
Responsible for defining parameters of the ray casting operations in 3D map canvases.
void setSingleResult(bool enable)
Sets whether to fetch only the closest hit for each layer or entity type.
void setMaximumDistance(float distance)
Sets the maximum distance from ray origin to look for hits when casting a ray.
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.
Represent a 2-dimensional vector.
double y() const
Returns the vector's y-component.
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
QgsVector perpVector() const
Returns the perpendicular vector to this vector (rotated 90 degrees counter-clockwise).
double x() const
Returns the vector's x-component.
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.
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).
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)