17#include "moc_qgs3daxis.cpp"
19#include <Qt3DCore/QTransform>
20#include <Qt3DExtras/QCylinderMesh>
21#include <Qt3DExtras/QPhongMaterial>
22#include <Qt3DExtras/QConeMesh>
23#include <Qt3DRender/qcameralens.h>
24#include <Qt3DRender/QCameraSelector>
25#include <Qt3DRender/QClearBuffers>
26#include <Qt3DRender/QLayer>
27#include <Qt3DRender/QLayerFilter>
28#include <Qt3DRender/QPointLight>
29#include <Qt3DRender/QSortPolicy>
33#include <QFontDatabase>
35#include <QApplication>
36#include <QActionGroup>
52 , mMapScene( mapScene )
53 , mCameraController( cameraCtrl )
56 mViewport = constructAxisScene( parent3DScene );
59 constructLabelsScene( parent3DScene );
62 connect( mCanvas, &Qgs3DMapCanvas::widthChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
63 connect( mCanvas, &Qgs3DMapCanvas::heightChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
66 onAxisViewportSizeUpdate();
68 init3DObjectPicking();
70 createKeyboardShortCut();
100void Qgs3DAxis::init3DObjectPicking()
108 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
109 mScreenRayCaster->addLayer( mAxisObjectLayer );
110 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
111 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
113 mAxisSceneEntity->addComponent( mScreenRayCaster );
115 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
118 mCanvas->installEventFilter(
this );
121bool Qgs3DAxis::eventFilter( QObject *watched, QEvent *event )
123 if ( watched != mCanvas )
126 if ( event->type() == QEvent::MouseButtonPress )
130 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
131 mLastClickedPos = mouseEvent->pos();
135 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
137 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
140 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
146 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
153 else if ( !mIsDragging )
156 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
160 std::ostringstream os;
161 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mViewport->normalizedRect();
165 if ( mViewport->normalizedRect().contains( normalizedPos ) )
167 mLastClickedButton = mouseEvent->button();
168 mLastClickedPos = mouseEvent->pos();
171 mScreenRayCaster->trigger( mLastClickedPos );
177 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
179 mCanvas->setCursor( mPreviousCursor );
180 mPreviousCursor = Qt::ArrowCursor;
184 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
185 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
187 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
200void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
207 std::ostringstream os;
208 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
209 for (
int i = 0; i < hits.length(); ++i )
212 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
213 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
214 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
215 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
220 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
222 if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
229 if ( mLastClickedButton == Qt::NoButton )
231 if ( mHitsFound != -1 )
233 if ( mCanvas->cursor() != Qt::ArrowCursor )
235 mPreviousCursor = mCanvas->cursor();
236 mCanvas->setCursor( Qt::ArrowCursor );
240 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
242 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
248 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
250 displayMenuAt( mLastClickedPos );
252 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
256 if ( mHitsFound != -1 )
258 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
260 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
264 onCameraViewChangeEast();
269 onCameraViewChangeWest();
274 onCameraViewChangeNorth();
279 onCameraViewChangeSouth();
284 onCameraViewChangeTop();
289 onCameraViewChangeBottom();
300Qt3DRender::QViewport *Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
302 Qt3DRender::QViewport *axisViewport =
new Qt3DRender::QViewport;
306 mAxisSceneEntity =
new Qt3DCore::QEntity;
307 mAxisSceneEntity->setParent( parent3DScene );
308 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
310 mAxisObjectLayer =
new Qt3DRender::QLayer;
311 mAxisObjectLayer->setObjectName(
"3DAxis_ObjectLayer" );
312 mAxisObjectLayer->setParent( mAxisSceneEntity );
313 mAxisObjectLayer->setRecursive(
true );
315 mAxisCamera =
new Qt3DRender::QCamera;
316 mAxisCamera->setParent( mAxisSceneEntity );
317 mAxisCamera->setProjectionType( mCameraController->
camera()->projectionType() );
318 mAxisCamera->lens()->setFieldOfView( mCameraController->
camera()->lens()->fieldOfView() * 0.5f );
320 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
321 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
324 Qt3DRender::QLayerFilter *axisLayerFilter =
new Qt3DRender::QLayerFilter( axisViewport );
325 axisLayerFilter->addLayer( mAxisObjectLayer );
327 Qt3DRender::QCameraSelector *axisCameraSelector =
new Qt3DRender::QCameraSelector;
328 axisCameraSelector->setParent( axisLayerFilter );
329 axisCameraSelector->setCamera( mAxisCamera );
333 Qt3DRender::QSortPolicy *sortPolicy =
new Qt3DRender::QSortPolicy( axisCameraSelector );
334 QVector<Qt3DRender::QSortPolicy::SortType> sortTypes = QVector<Qt3DRender::QSortPolicy::SortType>();
335 sortTypes << Qt3DRender::QSortPolicy::BackToFront;
336 sortPolicy->setSortTypes( sortTypes );
338 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( sortPolicy );
339 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
345void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
347 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
348 mTwoDLabelSceneEntity->setParent( parent3DScene );
349 mTwoDLabelSceneEntity->setEnabled(
true );
351 mTwoDLabelCamera =
new Qt3DRender::QCamera;
352 mTwoDLabelCamera->setParent( mTwoDLabelSceneEntity );
353 mTwoDLabelCamera->setProjectionType( Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection );
356 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
357 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
359 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
361 Qt3DRender::QLayer *twoDLayer =
new Qt3DRender::QLayer;
362 twoDLayer->setObjectName(
"3DAxis_LabelsLayer" );
363 twoDLayer->setRecursive(
true );
364 mTwoDLabelSceneEntity->addComponent( twoDLayer );
366 Qt3DRender::QLayerFilter *twoDLayerFilter =
new Qt3DRender::QLayerFilter;
367 twoDLayerFilter->addLayer( twoDLayer );
369 Qt3DRender::QCameraSelector *twoDCameraSelector =
new Qt3DRender::QCameraSelector;
370 twoDCameraSelector->setParent( twoDLayerFilter );
371 twoDCameraSelector->setCamera( mTwoDLabelCamera );
375 Qt3DRender::QSortPolicy *sortPolicy =
new Qt3DRender::QSortPolicy( twoDCameraSelector );
376 QVector<Qt3DRender::QSortPolicy::SortType> sortTypes = QVector<Qt3DRender::QSortPolicy::SortType>();
377 sortTypes << Qt3DRender::QSortPolicy::BackToFront;
378 sortPolicy->setSortTypes( sortTypes );
380 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( sortPolicy );
381 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
383 twoDLayerFilter->setParent( mViewport );
388 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
389 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
390 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
392 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
393 destPos.setZ( 0.0f );
397void Qgs3DAxis::setEnableCube(
bool show )
399 mCubeRoot->setEnabled( show );
402 mCubeRoot->setParent( mAxisSceneEntity );
406 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
410void Qgs3DAxis::setEnableAxis(
bool show )
412 mAxisRoot->setEnabled( show );
415 mAxisRoot->setParent( mAxisSceneEntity );
419 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
422 mTextX->setEnabled( show );
423 mTextY->setEnabled( show );
424 mTextZ->setEnabled( show );
427void Qgs3DAxis::createAxisScene()
429 if ( !mAxisRoot || !mCubeRoot )
431 mAxisRoot =
new Qt3DCore::QEntity;
432 mAxisRoot->setParent( mAxisSceneEntity );
433 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
434 mAxisRoot->addComponent( mAxisObjectLayer );
436 createAxis( Qt::Axis::XAxis );
437 createAxis( Qt::Axis::YAxis );
438 createAxis( Qt::Axis::ZAxis );
440 mCubeRoot =
new Qt3DCore::QEntity;
441 mCubeRoot->setParent( mAxisSceneEntity );
442 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
443 mCubeRoot->addComponent( mAxisObjectLayer );
452 mAxisSceneEntity->setEnabled(
false );
453 setEnableAxis(
false );
454 setEnableCube(
false );
458 mAxisSceneEntity->setEnabled(
true );
461 setEnableCube(
false );
462 setEnableAxis(
true );
464 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.
axisOrdering();
466 if ( axisDirections.length() > 0 )
469 mTextX->setText(
"X?" );
471 if ( axisDirections.length() > 1 )
474 mTextY->setText(
"Y?" );
476 if ( axisDirections.length() > 2 )
479 mTextZ->setText( QStringLiteral(
"up" ) );
483 setEnableCube(
true );
484 setEnableAxis(
false );
488 setEnableCube(
false );
489 setEnableAxis(
true );
490 mTextX->setText(
"X?" );
491 mTextY->setText(
"Y?" );
492 mTextZ->setText(
"Z?" );
495 updateAxisLabelPosition();
499void Qgs3DAxis::createKeyboardShortCut()
504 QWidget *mapCanvas =
dynamic_cast<QWidget *
>( eng->parent() );
511 QShortcut *shortcutHome =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_1 ), mapCanvas );
512 connect( shortcutHome, &QShortcut::activated,
this, [
this]() { onCameraViewChangeHome(); } );
514 QShortcut *shortcutTop =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_5 ), mapCanvas );
515 connect( shortcutTop, &QShortcut::activated,
this, [
this]() { onCameraViewChangeTop(); } );
517 QShortcut *shortcutNorth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_8 ), mapCanvas );
518 connect( shortcutNorth, &QShortcut::activated,
this, [
this]() { onCameraViewChangeNorth(); } );
520 QShortcut *shortcutEast =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_6 ), mapCanvas );
521 connect( shortcutEast, &QShortcut::activated,
this, [
this]() { onCameraViewChangeEast(); } );
523 QShortcut *shortcutSouth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_2 ), mapCanvas );
524 connect( shortcutSouth, &QShortcut::activated,
this, [
this]() { onCameraViewChangeSouth(); } );
526 QShortcut *shortcutWest =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ), mapCanvas );
527 connect( shortcutWest, &QShortcut::activated,
this, [
this]() { onCameraViewChangeWest(); } );
532void Qgs3DAxis::createMenu()
537 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
538 typeOffAct->setCheckable(
true );
539 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
542 typeOffAct->setChecked(
true );
545 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
546 typeCrsAct->setCheckable(
true );
547 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
550 typeCrsAct->setChecked(
true );
553 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
554 typeCubeAct->setCheckable(
true );
555 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
558 typeCubeAct->setChecked(
true );
561 QActionGroup *typeGroup =
new QActionGroup( mMenu );
562 typeGroup->addAction( typeOffAct );
563 typeGroup->addAction( typeCrsAct );
564 typeGroup->addAction( typeCubeAct );
570 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
571 Q_ASSERT( typeMenu );
572 typeMenu->addAction( typeOffAct );
573 typeMenu->addAction( typeCrsAct );
574 typeMenu->addAction( typeCubeAct );
575 mMenu->addMenu( typeMenu );
578 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
579 hPosLeftAct->setCheckable(
true );
582 hPosLeftAct->setChecked(
true );
585 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
586 hPosMiddleAct->setCheckable(
true );
589 hPosMiddleAct->setChecked(
true );
592 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
593 hPosRightAct->setCheckable(
true );
596 hPosRightAct->setChecked(
true );
599 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
600 hPosGroup->addAction( hPosLeftAct );
601 hPosGroup->addAction( hPosMiddleAct );
602 hPosGroup->addAction( hPosRightAct );
604 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
605 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
606 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
608 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
609 horizPosMenu->addAction( hPosLeftAct );
610 horizPosMenu->addAction( hPosMiddleAct );
611 horizPosMenu->addAction( hPosRightAct );
612 mMenu->addMenu( horizPosMenu );
615 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
616 vPosTopAct->setCheckable(
true );
619 vPosTopAct->setChecked(
true );
622 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
623 vPosMiddleAct->setCheckable(
true );
626 vPosMiddleAct->setChecked(
true );
629 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
630 vPosBottomAct->setCheckable(
true );
633 vPosBottomAct->setChecked(
true );
636 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
637 vPosGroup->addAction( vPosTopAct );
638 vPosGroup->addAction( vPosMiddleAct );
639 vPosGroup->addAction( vPosBottomAct );
641 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { onAxisVertPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
642 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { onAxisVertPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
643 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { onAxisVertPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
645 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
646 vertPosMenu->addAction( vPosTopAct );
647 vertPosMenu->addAction( vPosMiddleAct );
648 vertPosMenu->addAction( vPosBottomAct );
649 mMenu->addMenu( vertPosMenu );
652 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+1", mMenu );
653 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+5", mMenu );
654 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
655 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
656 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
657 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
658 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
660 connect( viewHomeAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeHome );
661 connect( viewTopAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeTop );
662 connect( viewNorthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeNorth );
663 connect( viewEastAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeEast );
664 connect( viewSouthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeSouth );
665 connect( viewWestAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeWest );
666 connect( viewBottomAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeBottom );
668 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
669 viewMenu->addAction( viewHomeAct );
670 viewMenu->addAction( viewTopAct );
671 viewMenu->addAction( viewNorthAct );
672 viewMenu->addAction( viewEastAct );
673 viewMenu->addAction( viewSouthAct );
674 viewMenu->addAction( viewWestAct );
675 viewMenu->addAction( viewBottomAct );
676 mMenu->addMenu( viewMenu );
682void Qgs3DAxis::hideMenu()
684 if ( mMenu && mMenu->isVisible() )
688void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
695 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
705void Qgs3DAxis::onAxisHorizPositionChanged( Qt::AnchorPoint pos )
712void Qgs3DAxis::onAxisVertPositionChanged( Qt::AnchorPoint pos )
719void Qgs3DAxis::onCameraViewChange(
float pitch,
float yaw )
722 double elevation = 0.0;
726 QVector3D camPos = mCameraController->
camera()->position();
727 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCameraController->
camera()->farPlane() );
729 if ( !hits.isEmpty() )
731 elevation = hits.at( 0 ).pos.z();
732 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
745void Qgs3DAxis::createCube()
747 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
750 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
751 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
752 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
753 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
755 cubeLineEntity->addComponent( cubeLine );
757 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
758 cubeLineMaterial->setAmbient( Qt::white );
759 cubeLineEntity->addComponent( cubeLineMaterial );
762 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
763 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
764 cubeMesh->setXExtent( mCylinderLength );
765 cubeMesh->setYExtent( mCylinderLength );
766 cubeMesh->setZExtent( mCylinderLength );
767 mCubeRoot->addComponent( cubeMesh );
769 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
770 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
771 cubeMaterial->setShininess( 100 );
772 mCubeRoot->addComponent( cubeMaterial );
774 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
775 QMatrix4x4 transformMatrixcube;
777 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
778 cubeTransform->setMatrix( transformMatrixcube );
779 mCubeRoot->addComponent( cubeTransform );
783 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
784 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
786 const QFont font = createFont( fontSize );
789 text = QStringLiteral(
"top" );
790 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
791 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
793 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
797 text = QStringLiteral(
"btm" );
798 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
799 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
801 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
802 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
806 text = QStringLiteral(
"west" );
807 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
808 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
810 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
811 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
812 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
816 text = QStringLiteral(
"east" );
817 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
818 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
820 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
821 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
822 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
826 text = QStringLiteral(
"south" );
827 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
828 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
830 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
831 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
835 text = QStringLiteral(
"north" );
836 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
837 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
839 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
840 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
841 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
844 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
846 l->setParent( mCubeRoot );
850Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
852 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
853 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
854 textEntity->setFont( font );
855 textEntity->setHeight( textHeight );
856 textEntity->setWidth( textWidth );
857 textEntity->setColor( QColor( 192, 192, 192 ) );
858 textEntity->setText( text );
860 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
861 textFrontTransform->setMatrix( rotation );
862 textFrontTransform->setTranslation( translation );
863 textEntity->addComponent( textFrontTransform );
868void Qgs3DAxis::createAxis( Qt::Axis axisType )
870 float cylinderRadius = 0.05f * mCylinderLength;
871 float coneLength = 0.3f * mCylinderLength;
872 float coneBottomRadius = 0.1f * mCylinderLength;
874 QQuaternion rotation;
877 Qt3DExtras::QText2DEntity *text =
nullptr;
878 Qt3DCore::QTransform *textTransform =
nullptr;
883 case Qt::Axis::XAxis:
884 mTextX =
new Qt3DExtras::QText2DEntity();
885 mTextX->setParent( mTwoDLabelSceneEntity );
886 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
887 updateAxisLabelText( mTextX, text );
889 mTextTransformX =
new Qt3DCore::QTransform();
890 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
892 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
895 textTransform = mTextTransformX;
896 name =
"3DAxis_axisX";
899 case Qt::Axis::YAxis:
900 mTextY =
new Qt3DExtras::QText2DEntity();
901 mTextY->setParent( mTwoDLabelSceneEntity );
902 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
903 updateAxisLabelText( mTextY, text );
905 mTextTransformY =
new Qt3DCore::QTransform();
906 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
912 textTransform = mTextTransformY;
913 name =
"3DAxis_axisY";
916 case Qt::Axis::ZAxis:
917 mTextZ =
new Qt3DExtras::QText2DEntity();
918 mTextZ->setParent( mTwoDLabelSceneEntity );
919 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
920 updateAxisLabelText( mTextZ, text );
922 mTextTransformZ =
new Qt3DCore::QTransform();
923 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
925 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
928 textTransform = mTextTransformZ;
929 name =
"3DAxis_axisZ";
937 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
938 cylinder->setObjectName( name );
940 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
941 cylinderMesh->setRadius( cylinderRadius );
942 cylinderMesh->setLength( mCylinderLength );
943 cylinderMesh->setRings( 10 );
944 cylinderMesh->setSlices( 4 );
945 cylinder->addComponent( cylinderMesh );
947 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
948 cylinderMaterial->setAmbient( color );
949 cylinderMaterial->setShininess( 0 );
950 cylinder->addComponent( cylinderMaterial );
952 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
953 QMatrix4x4 transformMatrixCylinder;
954 transformMatrixCylinder.rotate( rotation );
955 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
956 cylinderTransform->setMatrix( transformMatrixCylinder );
957 cylinder->addComponent( cylinderTransform );
960 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
961 coneEntity->setObjectName( name );
962 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
963 coneMesh->setLength( coneLength );
964 coneMesh->setBottomRadius( coneBottomRadius );
965 coneMesh->setTopRadius( 0.0f );
966 coneMesh->setRings( 10 );
967 coneMesh->setSlices( 4 );
968 coneEntity->addComponent( coneMesh );
970 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
971 coneMaterial->setAmbient( color );
972 coneMaterial->setShininess( 0 );
973 coneEntity->addComponent( coneMaterial );
975 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
976 QMatrix4x4 transformMatrixCone;
977 transformMatrixCone.rotate( rotation );
978 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
979 coneTransform->setMatrix( transformMatrixCone );
980 coneEntity->addComponent( coneTransform );
983 text->setColor( QColor( 192, 192, 192, 192 ) );
984 text->addComponent( textTransform );
990 onAxisViewportSizeUpdate();
993void Qgs3DAxis::onAxisViewportSizeUpdate(
int )
997 double windowWidth = ( double ) mCanvas->width();
998 double windowHeight = ( double ) mCanvas->height();
1003 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window w/h: %1px / %2px" ).arg( windowWidth ).arg( windowHeight ), 2 );
1004 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window physicalDpi %1 (%2, %3)" ).arg( mCanvas->screen()->physicalDotsPerInch() ).arg( mCanvas->screen()->physicalDotsPerInchX() ).arg( mCanvas->screen()->physicalDotsPerInchY() ), 2 );
1005 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window logicalDotsPerInch %1 (%2, %3)" ).arg( mCanvas->screen()->logicalDotsPerInch() ).arg( mCanvas->screen()->logicalDotsPerInchX() ).arg( mCanvas->screen()->logicalDotsPerInchY() ), 2 );
1007 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window pixel ratio %1" ).arg( mCanvas->screen()->devicePixelRatio() ), 2 );
1015 double defaultViewportPixelSize = ( ( double ) settings.
defaultViewportSize() / 25.4 ) * 92.0;
1019 double viewportPixelSize = defaultViewportPixelSize + ( ( double ) settings.
defaultViewportSize() / 25.4 ) * ( mCanvas->screen()->physicalDotsPerInch() - 92.0 ) * 0.7;
1020 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate viewportPixelSize %1" ).arg( viewportPixelSize ), 2 );
1021 double widthRatio = viewportPixelSize / windowWidth;
1022 double heightRatio = widthRatio * windowWidth / windowHeight;
1024 QgsDebugMsgLevel( QString(
"3DAxis viewport ratios width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1026 if ( heightRatio * windowHeight < viewportPixelSize )
1028 heightRatio = viewportPixelSize / windowHeight;
1029 widthRatio = heightRatio * windowHeight / windowWidth;
1030 QgsDebugMsgLevel( QString(
"3DAxis viewport, height too small, ratios adjusted to width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1035 QgsDebugMsgLevel(
"viewport takes too much place into the 3d view, disabling it", 2 );
1037 mViewport->setEnabled(
false );
1038 setEnableCube(
false );
1039 setEnableAxis(
false );
1044 mAxisScaleFactor = viewportPixelSize / defaultViewportPixelSize;
1045 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
1047 if ( !mViewport->isEnabled() )
1050 setEnableAxis(
true );
1052 setEnableCube(
true );
1054 mViewport->setEnabled(
true );
1056 float xRatio = 1.0f;
1057 float yRatio = 1.0f;
1061 xRatio = 0.5f -
static_cast<float>( widthRatio ) / 2.0f;
1063 xRatio = 1.0f -
static_cast<float>( widthRatio );
1067 else if ( settings.
verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
1068 yRatio = 0.5f -
static_cast<float>( heightRatio ) / 2.0f;
1070 yRatio = 1.0f -
static_cast<float>( heightRatio );
1072 QgsDebugMsgLevel( QString(
"Qgs3DAxis: update viewport: %1 x %2 x %3 x %4" ).arg( xRatio ).arg( yRatio ).arg( widthRatio ).arg( heightRatio ), 2 );
1073 mViewport->setNormalizedRect( QRectF( xRatio, yRatio, widthRatio, heightRatio ) );
1077 const float halfWidthSize =
static_cast<float>( windowWidth * widthRatio / 2.0 );
1078 const float halfHeightSize =
static_cast<float>( windowWidth * widthRatio / 2.0 );
1079 mTwoDLabelCamera->lens()->setOrthographicProjection(
1080 -halfWidthSize, halfWidthSize,
1081 -halfHeightSize, halfHeightSize,
1082 mTwoDLabelCamera->lens()->nearPlane(), mTwoDLabelCamera->lens()->farPlane()
1085 updateAxisLabelPosition();
1090void Qgs3DAxis::onCameraUpdate()
1092 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
1094 if ( parentCamera->viewVector() != mPreviousVector
1095 && !std::isnan( parentCamera->viewVector().x() )
1096 && !std::isnan( parentCamera->viewVector().y() )
1097 && !std::isnan( parentCamera->viewVector().z() ) )
1099 mPreviousVector = parentCamera->viewVector();
1101 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
1102 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
1103 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
1105 if ( mAxisRoot->isEnabled() )
1107 updateAxisLabelPosition();
1112void Qgs3DAxis::updateAxisLabelPosition()
1114 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
1116 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1117 updateAxisLabelText( mTextX, mTextX->text() );
1119 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1120 updateAxisLabelText( mTextY, mTextY->text() );
1122 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1123 updateAxisLabelText( mTextZ, mTextZ->text() );
1127void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
1129 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
1130 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
1131 textEntity->setFont( font );
1132 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
1133 textEntity->setHeight( 1.5f * scaledFontSize );
1136QFont Qgs3DAxis::createFont(
int pointSize )
1138 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1139 font.setPointSize( pointSize );
1140 font.setWeight( QFont::Weight::Black );
1141 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
Contains the configuration of a 3d axis.
void setMode(Qgs3DAxisSettings::Mode type)
Sets the type of the 3daxis.
double maxViewportRatio() const
Returns the maximal axis viewport ratio (see Qt3DRender::QViewport::normalizedRect())
Mode
Axis representation enum.
@ Crs
Respect CRS directions.
@ Cube
Abstract cube mode.
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
void setHorizontalPosition(Qt::AnchorPoint position)
Sets the horizontal position for the 3d axis.
int defaultViewportSize() const
Returns the default axis viewport size in millimeters.
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
void setVerticalPosition(Qt::AnchorPoint position)
Sets the vertical position for the 3d axis.
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
QVector3D from3DTo2DLabelPosition(const QVector3D &sourcePos, Qt3DRender::QCamera *sourceCamera, Qt3DRender::QCamera *destCamera)
Project a 3D position from sourceCamera to a 2D position for destCamera.
Qgs3DAxis(Qgs3DMapCanvas *canvas, Qt3DCore::QEntity *parent3DScene, Qgs3DMapScene *mapScene, QgsCameraController *camera, Qgs3DMapSettings *map)
Default Qgs3DAxis constructor.
Qt3DRender::QFrameGraphNode * activeFrameGraph() const
Returns the node of the active frame graph.
QgsAbstract3DEngine * engine() const
Returns the abstract 3D engine.
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be temporarily nullptr)
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
void set3DAxisSettings(const Qgs3DAxisSettings &axisSettings, bool force=false)
Sets the current configuration of 3d axis.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void cameraChanged()
Emitted when camera has been updated.
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.
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
static void warning(const QString &msg)
Goes to qWarning.
The QgsMapSettings class contains configuration for rendering of the map.
double dpiTarget() const
Returns the target DPI (dots per inch) to be taken into consideration when rendering.
float devicePixelRatio() const
Returns the device pixel ratio.
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
double y() const
Returns Y coordinate.
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
void set(double x, double y, double z)
Sets vector coordinates.
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs
Helper struct to store ray casting parameters.