17#include "moc_qgscameracontroller.cpp" 
   27#include <QDomDocument> 
   28#include <Qt3DRender/QCamera> 
   30#include <QStringLiteral> 
   39  , mCamera( scene->engine()->camera() )
 
   41  , mMouseHandler( new 
Qt3DInput::QMouseHandler )
 
   42  , mKeyboardHandler( new 
Qt3DInput::QKeyboardHandler )
 
   43  , mOrigin( scene->mapSettings()->origin() )
 
   45  mMouseHandler->setSourceDevice( 
new Qt3DInput::QMouseDevice() );
 
   46  connect( mMouseHandler, &Qt3DInput::QMouseHandler::positionChanged, 
this, &QgsCameraController::onPositionChanged );
 
   47  connect( mMouseHandler, &Qt3DInput::QMouseHandler::wheel, 
this, &QgsCameraController::onWheel );
 
   48  connect( mMouseHandler, &Qt3DInput::QMouseHandler::pressed, 
this, &QgsCameraController::onMousePressed );
 
   49  connect( mMouseHandler, &Qt3DInput::QMouseHandler::released, 
this, &QgsCameraController::onMouseReleased );
 
   50  addComponent( mMouseHandler );
 
   53  connect( 
this, &Qt3DCore::QEntity::enabledChanged, mMouseHandler, &Qt3DInput::QMouseHandler::setEnabled );
 
   54  connect( 
this, &Qt3DCore::QEntity::enabledChanged, mKeyboardHandler, &Qt3DInput::QKeyboardHandler::setEnabled );
 
   56  mFpsNavTimer = 
new QTimer( 
this );
 
   57  mFpsNavTimer->setInterval( 10 );
 
   58  connect( mFpsNavTimer, &QTimer::timeout, 
this, &QgsCameraController::applyFlyModeKeyMovements );
 
   59  mFpsNavTimer->start();
 
 
   70QWindow *QgsCameraController::window()
 const 
   73  return windowEngine ? windowEngine->
window() : 
nullptr;
 
   78  if ( navigationMode == mCameraNavigationMode )
 
   81  mCameraNavigationMode = navigationMode;
 
   82  mIgnoreNextMouseMove = 
true;
 
 
   88  if ( movementSpeed == mCameraMovementSpeed )
 
   93  mCameraMovementSpeed = std::clamp( movementSpeed, 0.05, 150.0 );
 
 
   99  mVerticalAxisInversion = inversion;
 
 
  104  float newPitch = mCameraPose.
pitchAngle() + diffPitch;
 
  105  float newHeading = mCameraPose.
headingAngle() + diffHeading;
 
  107  newPitch = std::clamp( newPitch, 0.f, 180.f ); 
 
  124        QgsDebugError( QStringLiteral( 
"rotateCamera: ECEF -> lat,lon transform failed!" ) );
 
  127      QQuaternion qLatLon = QQuaternion::fromAxisAndAngle( QVector3D( 0, 0, 1 ), 
static_cast<float>( viewCenterLatLon.
x() ) )
 
  128                            * QQuaternion::fromAxisAndAngle( QVector3D( 0, -1, 0 ), 
static_cast<float>( viewCenterLatLon.
y() ) );
 
  129      QQuaternion qPitchHeading = QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), newHeading )
 
  130                                  * QQuaternion::fromAxisAndAngle( QVector3D( 0, 1, 0 ), newPitch );
 
  131      QVector3D newCameraToCenter = ( qLatLon * qPitchHeading * QVector3D( -1, 0, 0 ) ) * mCameraPose.
distanceFromCenterPoint();
 
  133      mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
 
  136      updateCameraFromPose();
 
  144      mCameraPose.
setCenterPoint( mCamera->position() + newCameraToCenter );
 
  147      updateCameraFromPose();
 
 
  155  const float oldPitch = mCameraPose.
pitchAngle();
 
  158  newPitch = std::clamp( newPitch, 0.f, 180.f ); 
 
  164  const QQuaternion q = qNew * qOld.conjugated();
 
  166  const QVector3D newViewCenter = q * ( mCamera->viewCenter() - pivotPoint ) + pivotPoint;
 
  171  updateCameraFromPose();
 
 
  177  QVector3D newCamPosition = pivotPoint + ( oldCameraPosition - pivotPoint ) * zoomFactor;
 
  182  QVector3D cameraToCenter = q * QVector3D( 0, 0, -newDistance );
 
  183  QVector3D newViewCenter = newCamPosition + cameraToCenter;
 
  187  updateCameraFromPose();
 
 
  194  if ( mCameraChanged )
 
  197    mCameraChanged = 
false;
 
 
  212    QgsDebugError( QStringLiteral( 
"setViewFromTop() should not be used with globe!" ) );
 
  218  const float terrainElevationOffset = terrain ? terrain->terrainElevationOffset() : 0.0f;
 
  224  mCamera->setNearPlane( 
distance / 2 );
 
  225  mCamera->setFarPlane( 
distance * 2 );
 
 
  257  if ( camPose == mCameraPose && !force )
 
  260  mCameraPose = camPose;
 
  261  updateCameraFromPose();
 
 
  266  QDomElement elemCamera = doc.createElement( QStringLiteral( 
"camera" ) );
 
  270  elemCamera.setAttribute( QStringLiteral( 
"xMap" ), centerPoint.
x() );
 
  271  elemCamera.setAttribute( QStringLiteral( 
"yMap" ), centerPoint.
y() );
 
  272  elemCamera.setAttribute( QStringLiteral( 
"zMap" ), centerPoint.
z() );
 
  274  elemCamera.setAttribute( QStringLiteral( 
"pitch" ), mCameraPose.
pitchAngle() );
 
  275  elemCamera.setAttribute( QStringLiteral( 
"yaw" ), mCameraPose.
headingAngle() );
 
 
  281  const float dist = elem.attribute( QStringLiteral( 
"dist" ) ).toFloat();
 
  282  const float pitch = elem.attribute( QStringLiteral( 
"pitch" ) ).toFloat();
 
  283  const float yaw = elem.attribute( QStringLiteral( 
"yaw" ) ).toFloat();
 
  286  if ( elem.hasAttribute( 
"xMap" ) )
 
  289    const double x = elem.attribute( QStringLiteral( 
"xMap" ) ).toDouble();
 
  290    const double y = elem.attribute( QStringLiteral( 
"yMap" ) ).toDouble();
 
  291    const double z = elem.attribute( QStringLiteral( 
"zMap" ) ).toDouble();
 
  297    const double x = elem.attribute( QStringLiteral( 
"x" ) ).toDouble();
 
  298    const double y = elem.attribute( QStringLiteral( 
"y" ) ).toDouble();
 
  299    const double elev = elem.attribute( QStringLiteral( 
"elev" ) ).toDouble();
 
  300    centerPoint = 
QgsVector3D( x, elev, y ) - savedOrigin + mOrigin;
 
 
  305double QgsCameraController::sampleDepthBuffer( 
int px, 
int py )
 
  307  if ( !mDepthBufferIsReady )
 
  309    QgsDebugError( QStringLiteral( 
"Asked to sample depth buffer, but depth buffer not ready!" ) );
 
  314  if ( QWindow *win = window() )
 
  318    px = 
static_cast<int>( px * win->devicePixelRatio() );
 
  319    py = 
static_cast<int>( py * win->devicePixelRatio() );
 
  323  for ( 
int x = px - 3; x <= px + 3; ++x )
 
  325    for ( 
int y = py - 3; y <= py + 3; ++y )
 
  327      if ( mDepthBufferImage.valid( x, y ) )
 
  336double QgsCameraController::depthBufferNonVoidAverage()
 
  339  if ( mDepthBufferNonVoidAverage != -1 )
 
  340    return mDepthBufferNonVoidAverage;
 
  344  int samplesCount = 0;
 
  346  Q_ASSERT( mDepthBufferImage.format() == QImage::Format_RGB32 );
 
  347  for ( 
int y = 0; y < mDepthBufferImage.height(); ++y )
 
  349    const QRgb *line = 
reinterpret_cast<const QRgb *
>( mDepthBufferImage.constScanLine( y ) );
 
  350    for ( 
int x = 0; x < mDepthBufferImage.width(); ++x )
 
  362  if ( samplesCount == 0 )
 
  365    depth /= samplesCount;
 
  367  mDepthBufferNonVoidAverage = depth;
 
  372QgsVector3D QgsCameraController::moveGeocentricPoint( 
const QgsVector3D &point, 
double latDiff, 
double lonDiff )
 
  377    pointLatLon.
setX( pointLatLon.
x() + lonDiff );
 
  378    pointLatLon.
setY( std::clamp( pointLatLon.
y() + latDiff, -90., 90. ) );
 
  384    QgsDebugError( QStringLiteral( 
"moveGeocentricPoint: transform failed!" ) );
 
  392  const QgsVector3D newViewCenter = moveGeocentricPoint( viewCenter, latDiff, lonDiff );
 
  394  updateCameraFromPose();
 
 
  400  updateCameraFromPose();
 
 
  406  updateCameraFromPose();
 
 
  412  updateCameraFromPose();
 
 
  424    QgsDebugError( QStringLiteral( 
"resetGlobe: transform failed!" ) );
 
 
  434void QgsCameraController::updateCameraFromPose()
 
  445        viewCenterLatLon = mGlobeCrsToLatLon.
transform( viewCenter );
 
  449        QgsDebugError( QStringLiteral( 
"updateCameraFromPose: transform failed!" ) );
 
  459    mCameraChanged = 
true;
 
  463void QgsCameraController::moveCameraPositionBy( 
const QVector3D &posDiff )
 
  466  updateCameraFromPose();
 
  469void QgsCameraController::onPositionChanged( Qt3DInput::QMouseEvent *mouse )
 
  471  if ( !mInputHandlersEnabled )
 
  474  QgsEventTracing::ScopedEvent traceEvent( QStringLiteral( 
"3D" ), QStringLiteral( 
"QgsCameraController::onPositionChanged" ) );
 
  476  switch ( mCameraNavigationMode )
 
  479      onPositionChangedTerrainNavigation( mouse );
 
  483      onPositionChangedFlyNavigation( mouse );
 
  487      onPositionChangedGlobeTerrainNavigation( mouse );
 
  492bool QgsCameraController::screenPointToWorldPos( QPoint position, 
double &depth, QVector3D &worldPosition )
 
  494  depth = sampleDepthBuffer( position.x(), position.y() );
 
  500    depth = depthBufferNonVoidAverage();
 
  504  if ( !std::isfinite( worldPosition.x() ) || !std::isfinite( worldPosition.y() ) || !std::isfinite( worldPosition.z() ) )
 
  506    QgsDebugMsgLevel( QStringLiteral( 
"screenPointToWorldPos: position is NaN or Inf. This should not happen." ), 2 );
 
  513void QgsCameraController::onPositionChangedTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
 
  515  if ( mIgnoreNextMouseMove )
 
  517    mIgnoreNextMouseMove = 
false;
 
  518    mMousePos = QPoint( mouse->x(), mouse->y() );
 
  522  const int dx = mouse->x() - mMousePos.x();
 
  523  const int dy = mouse->y() - mMousePos.y();
 
  525  const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
 
  526  const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
 
  527  const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
 
  528  const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
 
  529  const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
 
  531  if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
 
  534    setMouseParameters( MouseOperation::RotationCenter, mMousePos );
 
  536    float scale = 
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
 
  537    float pitchDiff = 180.0f * 
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
 
  538    float yawDiff = -180.0f * 
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
 
  540    if ( !mDepthBufferIsReady )
 
  543    if ( !mRotationCenterCalculated )
 
  546      QVector3D worldPosition;
 
  547      if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
 
  549        mRotationCenter = worldPosition;
 
  550        mRotationDistanceFromCenter = ( mRotationCenter - mCameraBefore->position() ).length();
 
  552        mRotationCenterCalculated = 
true;
 
  558  else if ( hasLeftButton && hasCtrl && !hasShift )
 
  560    setMouseParameters( MouseOperation::RotationCamera );
 
  562    const float diffPitch = 0.2f * dy;
 
  563    const float diffYaw = -0.2f * dx;
 
  566  else if ( hasLeftButton && !hasShift && !hasCtrl )
 
  569    setMouseParameters( MouseOperation::Translation, mMousePos );
 
  571    if ( !mDepthBufferIsReady )
 
  574    if ( !mDragPointCalculated )
 
  577      QVector3D worldPosition;
 
  578      if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
 
  581        mDragPoint = worldPosition;
 
  582        mDragPointCalculated = 
true;
 
  586    QVector3D cameraBeforeDragPos = mCameraBefore->position();
 
  589    QVector3D cameraBeforeToMoveToPos = ( moveToPosition - mCameraBefore->position() ).normalized();
 
  590    QVector3D cameraBeforeToDragPointPos = ( mDragPoint - mCameraBefore->position() ).normalized();
 
  593    if ( cameraBeforeToMoveToPos.z() == 0 )
 
  595      cameraBeforeToMoveToPos.setZ( 0.01 );
 
  596      cameraBeforeToMoveToPos = cameraBeforeToMoveToPos.normalized();
 
  599    if ( cameraBeforeToDragPointPos.z() == 0 )
 
  601      cameraBeforeToDragPointPos.setZ( 0.01 );
 
  602      cameraBeforeToDragPointPos = cameraBeforeToDragPointPos.normalized();
 
  605    double d1 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToMoveToPos.z();
 
  606    double d2 = ( mDragPoint.z() - cameraBeforeDragPos.z() ) / cameraBeforeToDragPointPos.z();
 
  608    QVector3D from = cameraBeforeDragPos + d1 * cameraBeforeToMoveToPos;
 
  609    QVector3D to = cameraBeforeDragPos + d2 * cameraBeforeToDragPointPos;
 
  611    QVector3D shiftVector = to - from;
 
  613    mCameraPose.
setCenterPoint( mCameraBefore->viewCenter() + shiftVector );
 
  614    updateCameraFromPose();
 
  616  else if ( hasLeftButton && hasShift && hasCtrl )
 
  620    double tElev = mMousePos.y() - mouse->y();
 
  621    center.
set( center.
x(), center.
y(), center.
z() + tElev * 0.5 );
 
  623    updateCameraFromPose();
 
  625  else if ( hasRightButton && !hasShift && !hasCtrl )
 
  627    setMouseParameters( MouseOperation::Zoom, mMousePos );
 
  628    if ( !mDepthBufferIsReady )
 
  631    if ( !mDragPointCalculated )
 
  634      QVector3D worldPosition;
 
  635      if ( screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
 
  637        mDragPoint = worldPosition;
 
  638        mDragPointCalculated = 
true;
 
  642    float oldDist = ( mCameraBefore->position() - mDragPoint ).length();
 
  643    float newDist = oldDist;
 
  646    int screenHeight = mScene->
engine()->
size().height();
 
  647    QWindow *win = window();
 
  650      yOffset = win->mapToGlobal( QPoint( 0, 0 ) ).y();
 
  651      screenHeight = win->screen()->size().height();
 
  655    if ( mMousePos.y() > mClickPoint.y() ) 
 
  657      double f = ( double ) ( mMousePos.y() - mClickPoint.y() ) / ( 
double ) ( screenHeight - mClickPoint.y() - yOffset );
 
  658      f = std::max( 0.0, std::min( 1.0, f ) );
 
  659      f = 1 - ( std::expm1( -2 * f ) ) / ( std::expm1( -2 ) );
 
  660      newDist = newDist * f;
 
  664      double f = 1 - ( double ) ( mMousePos.y() + yOffset ) / ( 
double ) ( mClickPoint.y() + yOffset );
 
  665      f = std::max( 0.0, std::min( 1.0, f ) );
 
  666      f = ( std::expm1( 2 * f ) ) / ( std::expm1( 2 ) );
 
  667      newDist = newDist + 2 * newDist * f;
 
  670    double zoomFactor = newDist / oldDist;
 
  674  mMousePos = QPoint( mouse->x(), mouse->y() );
 
  677void QgsCameraController::onPositionChangedGlobeTerrainNavigation( Qt3DInput::QMouseEvent *mouse )
 
  679  const bool hasShift = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier );
 
  680  const bool hasCtrl = ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier );
 
  681  const bool hasLeftButton = ( mouse->buttons() & Qt::LeftButton );
 
  682  const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
 
  684  if ( ( hasLeftButton && hasShift && !hasCtrl ) || ( hasMiddleButton && !hasShift && !hasCtrl ) )
 
  686    setMouseParameters( MouseOperation::RotationCenter, mMousePos );
 
  688    const float scale = 
static_cast<float>( std::max( mScene->
engine()->
size().width(), mScene->
engine()->
size().height() ) );
 
  689    const float pitchDiff = 180.0f * 
static_cast<float>( mouse->y() - mClickPoint.y() ) / scale;
 
  690    const float yawDiff = -180.0f * 
static_cast<float>( mouse->x() - mClickPoint.x() ) / scale;
 
  694    updateCameraFromPose();
 
  698  if ( !( mouse->buttons() & Qt::LeftButton ) )
 
  702  setMouseParameters( MouseOperation::Translation, mMousePos );
 
  704  if ( !mDepthBufferIsReady )
 
  707  if ( !mDragPointCalculated )
 
  710    QVector3D worldPosition;
 
  711    if ( !screenPointToWorldPos( mClickPoint, depth, worldPosition ) )
 
  715    mDragPoint = worldPosition;
 
  716    mDragPointCalculated = 
true;
 
  722  const double sphereRadiusMap = startPosMap.
length();
 
  729  const double quadC = 
QgsVector3D::dotProduct( rayOriginMap, rayOriginMap ) - sphereRadiusMap * sphereRadiusMap;
 
  730  const double disc = quadB * quadB - 4 * quadA * quadC;
 
  735  const double rayDistMap = ( -quadB - sqrt( disc ) ) / ( 2 * quadA );
 
  736  if ( rayDistMap < 0 )
 
  738    QgsDebugError( QStringLiteral( 
"Sphere intersection result negative, cancelling move" ) );
 
  749    oldLatLon = mGlobeCrsToLatLon.
transform( startPosMap );
 
  750    newLatLon = mGlobeCrsToLatLon.
transform( newPosMap );
 
  754    QgsDebugError( QStringLiteral( 
"onPositionChangedGlobeTerrainNavigation: transform failed!" ) );
 
  758  const double latDiff = oldLatLon.
y() - newLatLon.
y();
 
  759  const double lonDiff = oldLatLon.
x() - newLatLon.
x();
 
  761  const QgsVector3D newVC = moveGeocentricPoint( mMousePressViewCenter, latDiff, lonDiff );
 
  765  updateCameraFromPose();
 
  773  dist -= dist * factor * 0.01f;
 
  775  updateCameraFromPose();
 
 
  778void QgsCameraController::handleTerrainNavigationWheelZoom()
 
  780  if ( !mDepthBufferIsReady )
 
  783  if ( !mZoomPointCalculated )
 
  786    QVector3D worldPosition;
 
  787    if ( screenPointToWorldPos( mMousePos, depth, worldPosition ) )
 
  789      mZoomPoint = worldPosition;
 
  790      mZoomPointCalculated = 
true;
 
  794  double oldDist = ( mZoomPoint - mCameraBefore->position() ).length();
 
  796  double newDist = std::pow( 0.8, mCumulatedWheelY ) * oldDist;
 
  798  newDist = std::max( newDist, 2.0 );
 
  799  double zoomFactor = newDist / oldDist;
 
  801  zoomFactor = std::clamp( zoomFactor, 0.01, 100.0 );
 
  805  mCumulatedWheelY = 0;
 
  806  setMouseParameters( MouseOperation::None );
 
  809void QgsCameraController::onWheel( Qt3DInput::QWheelEvent *wheel )
 
  811  if ( !mInputHandlersEnabled )
 
  814  switch ( mCameraNavigationMode )
 
  818      const float scaling = ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1f : 1.0f ) / 1000.f;
 
  819      setCameraMovementSpeed( mCameraMovementSpeed + mCameraMovementSpeed * scaling * wheel->angleDelta().y() );
 
  827      const double scaling = ( 1.0 / 120.0 ) * ( ( wheel->modifiers() & Qt3DInput::QWheelEvent::Modifiers::ControlModifier ) != 0 ? 0.1 : 1.0 );
 
  831      mCumulatedWheelY += scaling * wheel->angleDelta().y();
 
  833      if ( mCurrentOperation != MouseOperation::ZoomWheel )
 
  835        setMouseParameters( MouseOperation::ZoomWheel );
 
  840        handleTerrainNavigationWheelZoom();
 
  847      float wheelAmount = 
static_cast<float>( wheel->angleDelta().y() );
 
  848      float factor = abs( wheelAmount ) / 1000.f;
 
  849      float mulFactor = wheelAmount > 0 ? ( 1 - factor ) : ( 1 + factor );
 
  851      updateCameraFromPose();
 
  857void QgsCameraController::onMousePressed( Qt3DInput::QMouseEvent *mouse )
 
  859  if ( !mInputHandlersEnabled )
 
  862  mKeyboardHandler->setFocus( 
true );
 
  864  if ( mouse->button() == Qt3DInput::QMouseEvent::MiddleButton || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ShiftModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) || ( ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) )
 
  866    mMousePos = QPoint( mouse->x(), mouse->y() );
 
  868    if ( mCaptureFpsMouseMovements )
 
  869      mIgnoreNextMouseMove = 
true;
 
  871    const MouseOperation operation {
 
  872      ( mouse->modifiers() & Qt3DInput::QMouseEvent::Modifiers::ControlModifier ) != 0 && mouse->button() == Qt3DInput::QMouseEvent::LeftButton ? MouseOperation::RotationCamera : MouseOperation::RotationCenter
 
  874    setMouseParameters( operation, mMousePos );
 
  877  else if ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton || mouse->button() == Qt3DInput::QMouseEvent::RightButton )
 
  879    mMousePos = QPoint( mouse->x(), mouse->y() );
 
  881    if ( mCaptureFpsMouseMovements )
 
  882      mIgnoreNextMouseMove = 
true;
 
  884    const MouseOperation operation = ( mouse->button() == Qt3DInput::QMouseEvent::LeftButton ) ? MouseOperation::Translation : MouseOperation::Zoom;
 
  885    setMouseParameters( operation, mMousePos );
 
  889void QgsCameraController::onMouseReleased( Qt3DInput::QMouseEvent *mouse )
 
  892  if ( !mInputHandlersEnabled )
 
  896  setMouseParameters( MouseOperation::None );
 
  899bool QgsCameraController::onKeyPressedTerrainNavigation( QKeyEvent *event )
 
  901  const bool hasShift = ( 
event->modifiers() & Qt::ShiftModifier );
 
  902  const bool hasCtrl = ( 
event->modifiers() & Qt::ControlModifier );
 
  904  int tx = 0, ty = 0, tElev = 0;
 
  905  switch ( event->key() )
 
  921    case Qt::Key_PageDown:
 
  933    if ( !hasShift && !hasCtrl )
 
  937    else if ( hasShift && !hasCtrl )
 
  943    else if ( hasCtrl && !hasShift )
 
  946      const float diffPitch = ty; 
 
  947      const float diffYaw = -tx;  
 
  956    center.
set( center.
x(), center.
y(), center.
z() + tElev * 10 );
 
  959    updateCameraFromPose();
 
  965bool QgsCameraController::onKeyPressedGlobeTerrainNavigation( QKeyEvent *event )
 
  969  constexpr float MOVE_FACTOR = 0.000001f; 
 
  970  constexpr float ZOOM_FACTOR = 0.9f;
 
  972  const bool hasShift = ( 
event->modifiers() & Qt::ShiftModifier );
 
  974  switch ( event->key() )
 
 1000    case Qt::Key_PageDown:
 
 1003    case Qt::Key_PageUp:
 
 1012static const QSet<int> walkNavigationSavedKeys = {
 
 1027bool QgsCameraController::onKeyPressedFlyNavigation( QKeyEvent *event )
 
 1029  switch ( event->key() )
 
 1031    case Qt::Key_QuoteLeft:
 
 1034      mCaptureFpsMouseMovements = !mCaptureFpsMouseMovements;
 
 1035      mIgnoreNextMouseMove = 
true;
 
 1036      if ( mCaptureFpsMouseMovements )
 
 1038        qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );
 
 1042        qApp->restoreOverrideCursor();
 
 1048    case Qt::Key_Escape:
 
 1051      if ( mCaptureFpsMouseMovements )
 
 1053        mCaptureFpsMouseMovements = 
false;
 
 1054        mIgnoreNextMouseMove = 
true;
 
 1055        qApp->restoreOverrideCursor();
 
 1065  if ( walkNavigationSavedKeys.contains( event->key() ) )
 
 1067    if ( !event->isAutoRepeat() )
 
 1069      mDepressedKeys.insert( event->key() );
 
 1079  const QVector3D cameraUp = mCamera->upVector().normalized();
 
 1081  const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
 
 1083  QVector3D cameraPosDiff( 0.0f, 0.0f, 0.0f );
 
 1087    cameraPosDiff += 
static_cast<float>( tx ) * cameraFront;
 
 1091    cameraPosDiff += 
static_cast<float>( ty ) * cameraLeft;
 
 1095    cameraPosDiff += 
static_cast<float>( tz ) * QVector3D( 0.0f, 0.0f, 1.0f );
 
 1098  moveCameraPositionBy( cameraPosDiff );
 
 
 1101void QgsCameraController::applyFlyModeKeyMovements()
 
 1107  const bool shiftPressed = mDepressedKeys.contains( Qt::Key_Shift );
 
 1108  const bool ctrlPressed = mDepressedKeys.contains( Qt::Key_Control );
 
 1110  const double movementSpeed = mCameraMovementSpeed * ( shiftPressed ? 2 : 1 ) * ( ctrlPressed ? 0.1 : 1 );
 
 1112  bool changed = 
false;
 
 1116  if ( mDepressedKeys.contains( Qt::Key_Left ) || mDepressedKeys.contains( Qt::Key_A ) )
 
 1122  if ( mDepressedKeys.contains( Qt::Key_Right ) || mDepressedKeys.contains( Qt::Key_D ) )
 
 1128  if ( mDepressedKeys.contains( Qt::Key_Up ) || mDepressedKeys.contains( Qt::Key_W ) )
 
 1134  if ( mDepressedKeys.contains( Qt::Key_Down ) || mDepressedKeys.contains( Qt::Key_S ) )
 
 1142  static constexpr double ELEVATION_MOVEMENT_SCALE = 0.5;
 
 1143  if ( mDepressedKeys.contains( Qt::Key_PageUp ) || mDepressedKeys.contains( Qt::Key_E ) )
 
 1146    z += ELEVATION_MOVEMENT_SCALE * movementSpeed;
 
 1149  if ( mDepressedKeys.contains( Qt::Key_PageDown ) || mDepressedKeys.contains( Qt::Key_Q ) )
 
 1152    z -= ELEVATION_MOVEMENT_SCALE * movementSpeed;
 
 1159void QgsCameraController::onPositionChangedFlyNavigation( Qt3DInput::QMouseEvent *mouse )
 
 1161  const bool hasMiddleButton = ( mouse->buttons() & Qt::MiddleButton );
 
 1162  const bool hasRightButton = ( mouse->buttons() & Qt::RightButton );
 
 1164  const double dx = mCaptureFpsMouseMovements ? QCursor::pos().x() - mMousePos.x() : mouse->x() - mMousePos.x();
 
 1165  const double dy = mCaptureFpsMouseMovements ? QCursor::pos().y() - mMousePos.y() : mouse->y() - mMousePos.y();
 
 1166  mMousePos = mCaptureFpsMouseMovements ? QCursor::pos() : QPoint( mouse->x(), mouse->y() );
 
 1168  if ( mIgnoreNextMouseMove )
 
 1170    mIgnoreNextMouseMove = 
false;
 
 1174  if ( hasMiddleButton )
 
 1177    const QVector3D cameraUp = mCamera->upVector().normalized();
 
 1179    const QVector3D cameraLeft = QVector3D::crossProduct( cameraUp, cameraFront );
 
 1180    const QVector3D cameraPosDiff = -dx * cameraLeft - dy * cameraUp;
 
 1181    moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 10.0 );
 
 1183  else if ( hasRightButton )
 
 1187    const QVector3D cameraPosDiff = dy * cameraFront;
 
 1188    moveCameraPositionBy( mCameraMovementSpeed * cameraPosDiff / 5.0 );
 
 1192    if ( mCaptureFpsMouseMovements )
 
 1194      float diffPitch = -0.2f * dy;
 
 1195      switch ( mVerticalAxisInversion )
 
 1206      const float diffYaw = -0.2f * dx;
 
 1209    else if ( mouse->buttons() & Qt::LeftButton )
 
 1211      float diffPitch = -0.2f * dy;
 
 1212      switch ( mVerticalAxisInversion )
 
 1222      const float diffYaw = -0.2f * dx;
 
 1227  if ( mCaptureFpsMouseMovements )
 
 1229    mIgnoreNextMouseMove = 
true;
 
 1240  pitch -= deltaPitch; 
 
 1242  updateCameraFromPose();
 
 
 1251  updateCameraFromPose();
 
 
 1257  updateCameraFromPose();
 
 
 1264  const float x = tx * dist * 0.02f;
 
 1265  const float y = -ty * dist * 0.02f;
 
 1268  const float t = sqrt( x * x + y * y );
 
 1269  const float a = atan2( y, x ) - 
yaw * M_PI / 180;
 
 1270  const float dx = cos( a ) * t;
 
 1271  const float dy = sin( a ) * t;
 
 1274  center.
set( center.
x() + dx, center.
y() - dy, center.
z() );
 
 1276  updateCameraFromPose();
 
 
 1281  if ( !mInputHandlersEnabled )
 
 1284  if ( event->type() == QKeyEvent::Type::KeyRelease )
 
 1286    if ( !event->isAutoRepeat() && mDepressedKeys.contains( event->key() ) )
 
 1288      mDepressedKeys.remove( event->key() );
 
 1292  else if ( event->type() == QEvent::ShortcutOverride )
 
 1294    if ( event->modifiers() & Qt::ControlModifier )
 
 1296      switch ( event->key() )
 
 1298        case Qt::Key_QuoteLeft:
 
 1301          switch ( mCameraNavigationMode )
 
 1347    switch ( mCameraNavigationMode )
 
 1350        return onKeyPressedFlyNavigation( event );
 
 1353        return onKeyPressedTerrainNavigation( event );
 
 1356        return onKeyPressedGlobeTerrainNavigation( event );
 
 
 1364  mDepthBufferImage = depthImage;
 
 1365  mDepthBufferIsReady = 
true;
 
 1366  mDepthBufferNonVoidAverage = -1;
 
 1374  if ( mCurrentOperation == MouseOperation::ZoomWheel )
 
 1376    handleTerrainNavigationWheelZoom();
 
 
 1380bool QgsCameraController::isATranslationRotationSequence( MouseOperation newOperation )
 const 
 1382  return std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), newOperation ) != std::end( mTranslateOrRotate ) && std::find( mTranslateOrRotate.begin(), mTranslateOrRotate.end(), mCurrentOperation ) != std::end( mTranslateOrRotate );
 
 1385void QgsCameraController::setMouseParameters( 
const MouseOperation &newOperation, 
const QPoint &clickPoint )
 
 1387  if ( newOperation == mCurrentOperation )
 
 1392  if ( newOperation == MouseOperation::None )
 
 1394    mClickPoint = QPoint();
 
 1402  else if ( mClickPoint.isNull() || isATranslationRotationSequence( newOperation ) )
 
 1404    mClickPoint = clickPoint;
 
 1408  mCurrentOperation = newOperation;
 
 1409  mDepthBufferIsReady = 
false;
 
 1410  mRotationCenterCalculated = 
false;
 
 1411  mDragPointCalculated = 
false;
 
 1412  mZoomPointCalculated = 
false;
 
 1414  if ( mCurrentOperation != MouseOperation::None && mCurrentOperation != MouseOperation::RotationCamera )
 
 1416    mMousePressViewCenter = mCameraPose.
centerPoint() + mOrigin;
 
 1429  mCameraBefore->setPosition( ( 
QgsVector3D( mCameraBefore->position() ) - diff ).toVector3D() );
 
 1430  mCameraBefore->setViewCenter( ( 
QgsVector3D( mCameraBefore->viewCenter() ) - diff ).toVector3D() );
 
 1431  mDragPoint = ( 
QgsVector3D( mDragPoint ) - diff ).toVector3D();
 
 1432  mRotationCenter = ( 
QgsVector3D( mRotationCenter ) - diff ).toVector3D();
 
 1436  updateCameraFromPose();
 
 
 1439void QgsCameraController::rotateToRespectingTerrain( 
float pitch, 
float yaw )
 
 1442  double elevation = 0.0;
 
 1446    QVector3D camPos = mCamera->position();
 
 1447    QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCamera->farPlane() );
 
 1449    if ( !hits.isEmpty() )
 
 1451      elevation = hits.at( 0 ).pos.z();
 
 1452      QgsDebugMsgLevel( QString( 
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
 
 1459  pos.
set( pos.
x(), pos.
y(), elevation + mScene->
terrainEntity()->terrainElevationOffset() );
 
VerticalAxisInversion
Vertical axis inversion options for 3D views.
 
@ Always
Always invert vertical axis movements.
 
@ Never
Never invert vertical axis movements.
 
@ WhenDragging
Invert vertical axis movements when dragging in first person modes.
 
NavigationMode
The navigation mode used by 3D cameras.
 
@ TerrainBased
The default navigation based on the terrain.
 
@ Walk
Uses WASD keys or arrows to navigate in walking (first person) manner.
 
@ GlobeTerrainBased
Navigation similar to TerrainBased, but for use with globe.
 
@ Globe
Scene is represented as a globe using a geocentric CRS.
 
@ Local
Local scene based on a projected CRS.
 
@ Reverse
Reverse/inverse transform (from destination to source)
 
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
 
Qgs3DMapSettings * mapSettings() const
Returns the 3D map settings.
 
QgsAbstract3DEngine * engine() const
Returns the abstract 3D engine.
 
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be nullptr if using globe scene, terrain rendering is disabled or when te...
 
Qgis::NavigationMode cameraNavigationMode() const
Returns the navigation mode used by the camera.
 
Qgis::SceneMode sceneMode() const
Returns mode of the 3D scene - whether it is represented as a globe (when using Geocentric CRS such a...
 
QgsRectangle extent() const
Returns the 3D scene's 2D extent in the 3D scene's CRS.
 
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
 
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
 
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
 
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0).
 
static QQuaternion rotationFromPitchHeadingAngles(float pitchAngle, float headingAngle)
Returns rotation quaternion that performs rotation around X axis by pitchAngle, followed by rotation ...
 
static std::unique_ptr< Qt3DRender::QCamera > copyCamera(Qt3DRender::QCamera *cam)
Returns new camera object with copied properties.
 
static double decodeDepth(const QRgb &pixel)
Decodes the depth value from the pixel's color value The depth value is encoded from OpenGL side (the...
 
static QVector3D screenPointToWorldPos(const QPoint &screenPoint, double depth, const QSize &screenSize, Qt3DRender::QCamera *camera)
Converts the clicked mouse position to the corresponding 3D world coordinates.
 
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
 
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
 
void navigationModeChanged(Qgis::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
 
void rotateCameraToBottom()
Rotate to bottom-up view.
 
void setLookingAtMapPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets camera configuration like setLookingAtPoint(), but the point is given in map coordinates.
 
float pitch() const
Returns pitch angle in degrees (0 = looking from the top, 90 = looking from the side).
 
~QgsCameraController() override
 
float yaw() const
Returns yaw angle in degrees.
 
void requestDepthBufferCapture()
Emitted to ask for the depth buffer image.
 
void rotateCameraToTop()
Rotate to top-down view.
 
void tiltUpAroundViewCenter(float deltaPitch)
Tilt up the view by deltaPitch around the view center (camera moves)
 
void globeMoveCenterPoint(double latDiff, double lonDiff)
Orbits camera around the globe by the specified amount given as the difference in latitude/longitude ...
 
void rotateCameraToEast()
Rotate to view from the east.
 
void setVerticalAxisInversion(Qgis::VerticalAxisInversion inversion)
Sets the vertical axis inversion behavior.
 
const QgsVector3D origin() const
Returns the origin of the scene in map coordinates.
 
void rotateCameraToHome()
Rotate to diagonal view.
 
void rotateCameraToNorth()
Rotate to view from the north.
 
float distance() const
Returns distance of the camera from the point it is looking at.
 
void globeUpdatePitchAngle(float angleDiff)
Updates pitch angle by the specified amount given as the angular difference in degrees.
 
void zoomCameraAroundPivot(const QVector3D &oldCameraPosition, double zoomFactor, const QVector3D &pivotPoint)
Zooms camera by given zoom factor (>1 one means zoom in) while keeping the pivot point (given in worl...
 
void globeZoom(float factor)
Moves camera closer or further away from the globe.
 
void rotateCamera(float diffPitch, float diffYaw)
Rotates the camera on itself.
 
void rotateCameraToWest()
Rotate to view from the west.
 
void setCameraNavigationMode(Qgis::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
 
void cameraChanged()
Emitted when camera has been updated.
 
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
 
QgsCameraController(Qgs3DMapScene *scene)
Constructs the camera controller with optional parent node that will take ownership.
 
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
 
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates)
 
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
 
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
 
QgsVector3D lookingAtMapPoint() const
Returns the point in the map coordinates towards which the camera is looking.
 
void rotateCameraToSouth()
Rotate to view from the south.
 
QDomElement writeXml(QDomDocument &doc) const
Writes camera configuration to the given DOM element.
 
void setViewFromTop(float worldX, float worldY, float distance, float yaw=0)
Sets camera to look down towards given point in world coordinate, in given distance from plane with z...
 
bool keyboardEventFilter(QKeyEvent *event)
If the event is relevant, handles the event and returns true, otherwise false.
 
void resetGlobe(float distance, double lat=0, double lon=0)
Resets view of the globe to look at a particular location given as latitude and longitude (in degrees...
 
void 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.
 
float headingAngle() const
Returns heading (yaw) angle in degrees.
 
QgsVector3D centerPoint() const
Returns center point (towards which point the camera is looking)
 
float pitchAngle() const
Returns pitch angle in degrees.
 
void updateCameraGlobe(Qt3DRender::QCamera *camera, double lat, double lon)
Updates camera when using a globe scene.
 
float distanceFromCenterPoint() const
Returns distance of the camera from the center point.
 
void setPitchAngle(float pitch)
Sets pitch angle in degrees.
 
void setCenterPoint(const QgsVector3D &point)
Sets center point (towards which point the camera is looking)
 
void setHeadingAngle(float heading)
Sets heading (yaw) angle in degrees.
 
void setDistanceFromCenterPoint(float distance)
Sets distance of the camera from the center point.
 
void updateCamera(Qt3DRender::QCamera *camera)
Update Qt3D camera view matrix based on the pose.
 
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
 
Custom exception class for Coordinate Reference System related exceptions.
 
A representation of a ray in 3D.
 
QVector3D origin() const
Returns the origin of the ray.
 
QVector3D direction() const
Returns the direction of the ray see setDirection()
 
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
 
double y() const
Returns Y coordinate.
 
double z() const
Returns Z coordinate.
 
QVector3D toVector3D() const
Converts the current object to QVector3D.
 
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
 
double x() const
Returns X coordinate.
 
void setX(double x)
Sets X coordinate.
 
void set(double x, double y, double z)
Sets vector coordinates.
 
void setY(double y)
Sets Y coordinate.
 
double length() const
Returns the length of the vector.
 
On-screen 3D engine: it creates an OpenGL window (QWindow) and displays rendered 3D scenes there.
 
QWindow * window()
Returns the internal 3D window where all the rendered output is displayed.
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
Helper struct to store ray casting parameters.