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>
48 Qt3DCore::QEntity *parent3DScene,
55 , mMapScene( mapScene )
56 , mCameraController( cameraCtrl )
59 mViewport = constructAxisScene( parent3DScene );
62 constructLabelsScene( parent3DScene );
65 connect( mCanvas, &Qgs3DMapCanvas::widthChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
66 connect( mCanvas, &Qgs3DMapCanvas::heightChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
69 onAxisViewportSizeUpdate();
71 init3DObjectPicking();
73 createKeyboardShortCut();
103void Qgs3DAxis::init3DObjectPicking( )
111 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
112 mScreenRayCaster->addLayer( mAxisObjectLayer );
113 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
114 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
116 mAxisSceneEntity->addComponent( mScreenRayCaster );
118 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
121 mCanvas->installEventFilter(
this );
124bool Qgs3DAxis::eventFilter( QObject *watched, QEvent *event )
126 if ( watched != mCanvas )
129 if ( event->type() == QEvent::MouseButtonPress )
133 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
134 mLastClickedPos = mouseEvent->pos();
138 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
140 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
143 if ( event->type() == QEvent::MouseMove &&
144 ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
150 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
157 else if ( ! mIsDragging )
160 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
161 static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
165 std::ostringstream os;
166 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mViewport->normalizedRect();
170 if ( mViewport->normalizedRect().contains( normalizedPos ) )
172 mLastClickedButton = mouseEvent->button();
173 mLastClickedPos = mouseEvent->pos();
176 mScreenRayCaster->trigger( mLastClickedPos );
182 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
184 mCanvas->setCursor( mPreviousCursor );
185 mPreviousCursor = Qt::ArrowCursor;
189 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
190 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
192 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
205void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
212 std::ostringstream os;
213 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
214 for (
int i = 0; i < hits.length(); ++i )
217 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
218 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
219 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
220 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
225 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
227 if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() &&
228 ( hits.at( i ).entity() == mCubeRoot ||
229 hits.at( i ).entity() == mAxisRoot ||
230 hits.at( i ).entity()->parent() == mCubeRoot ||
231 hits.at( i ).entity()->parent() == mAxisRoot ) )
238 if ( mLastClickedButton == Qt::NoButton )
240 if ( mHitsFound != -1 )
242 if ( mCanvas->cursor() != Qt::ArrowCursor )
244 mPreviousCursor = mCanvas->cursor();
245 mCanvas->setCursor( Qt::ArrowCursor );
249 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking &&
250 mCubeRoot->isEnabled() )
252 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
258 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
260 displayMenuAt( mLastClickedPos );
262 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
266 if ( mHitsFound != -1 )
268 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
270 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
274 onCameraViewChangeEast();
279 onCameraViewChangeWest();
284 onCameraViewChangeNorth();
289 onCameraViewChangeSouth();
294 onCameraViewChangeTop();
299 onCameraViewChangeBottom();
310Qt3DRender::QViewport *Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
312 Qt3DRender::QViewport *axisViewport =
new Qt3DRender::QViewport;
316 mAxisSceneEntity =
new Qt3DCore::QEntity;
317 mAxisSceneEntity->setParent( parent3DScene );
318 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
320 mAxisObjectLayer =
new Qt3DRender::QLayer;
321 mAxisObjectLayer->setObjectName(
"3DAxis_ObjectLayer" );
322 mAxisObjectLayer->setParent( mAxisSceneEntity );
323 mAxisObjectLayer->setRecursive(
true );
325 mAxisCamera =
new Qt3DRender::QCamera;
326 mAxisCamera->setParent( mAxisSceneEntity );
327 mAxisCamera->setProjectionType( mCameraController->
camera()->projectionType() );
328 mAxisCamera->lens()->setFieldOfView( mCameraController->
camera()->lens()->fieldOfView() * 0.5f );
330 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
331 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
334 Qt3DRender::QLayerFilter *axisLayerFilter =
new Qt3DRender::QLayerFilter( axisViewport );
335 axisLayerFilter->addLayer( mAxisObjectLayer );
337 Qt3DRender::QCameraSelector *axisCameraSelector =
new Qt3DRender::QCameraSelector;
338 axisCameraSelector->setParent( axisLayerFilter );
339 axisCameraSelector->setCamera( mAxisCamera );
343 Qt3DRender::QSortPolicy *sortPolicy =
new Qt3DRender::QSortPolicy( axisCameraSelector );
344 QVector<Qt3DRender::QSortPolicy::SortType> sortTypes = QVector<Qt3DRender::QSortPolicy::SortType>();
345 sortTypes << Qt3DRender::QSortPolicy::BackToFront;
346 sortPolicy->setSortTypes( sortTypes );
348 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( sortPolicy );
349 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
355void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
357 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
358 mTwoDLabelSceneEntity->setParent( parent3DScene );
359 mTwoDLabelSceneEntity->setEnabled(
true );
361 mTwoDLabelCamera =
new Qt3DRender::QCamera;
362 mTwoDLabelCamera->setParent( mTwoDLabelSceneEntity );
363 mTwoDLabelCamera->setProjectionType( Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection );
366 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
367 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
369 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
371 Qt3DRender::QLayer *twoDLayer =
new Qt3DRender::QLayer;
372 twoDLayer->setObjectName(
"3DAxis_LabelsLayer" );
373 twoDLayer->setRecursive(
true );
374 mTwoDLabelSceneEntity->addComponent( twoDLayer );
376 Qt3DRender::QLayerFilter *twoDLayerFilter =
new Qt3DRender::QLayerFilter;
377 twoDLayerFilter->addLayer( twoDLayer );
379 Qt3DRender::QCameraSelector *twoDCameraSelector =
new Qt3DRender::QCameraSelector;
380 twoDCameraSelector->setParent( twoDLayerFilter );
381 twoDCameraSelector->setCamera( mTwoDLabelCamera );
385 Qt3DRender::QSortPolicy *sortPolicy =
new Qt3DRender::QSortPolicy( twoDCameraSelector );
386 QVector<Qt3DRender::QSortPolicy::SortType> sortTypes = QVector<Qt3DRender::QSortPolicy::SortType>();
387 sortTypes << Qt3DRender::QSortPolicy::BackToFront;
388 sortPolicy->setSortTypes( sortTypes );
390 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( sortPolicy );
391 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
393 twoDLayerFilter->setParent( mViewport );
398 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
399 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
400 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ),
401 viewportWidth, viewportHeight );
403 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
404 destPos.setZ( 0.0f );
408void Qgs3DAxis::setEnableCube(
bool show )
410 mCubeRoot->setEnabled( show );
413 mCubeRoot->setParent( mAxisSceneEntity );
417 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
421void Qgs3DAxis::setEnableAxis(
bool show )
423 mAxisRoot->setEnabled( show );
426 mAxisRoot->setParent( mAxisSceneEntity );
430 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
433 mTextX->setEnabled( show );
434 mTextY->setEnabled( show );
435 mTextZ->setEnabled( show );
438void Qgs3DAxis::createAxisScene()
440 if ( !mAxisRoot || !mCubeRoot )
442 mAxisRoot =
new Qt3DCore::QEntity;
443 mAxisRoot->setParent( mAxisSceneEntity );
444 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
445 mAxisRoot->addComponent( mAxisObjectLayer );
447 createAxis( Qt::Axis::XAxis );
448 createAxis( Qt::Axis::YAxis );
449 createAxis( Qt::Axis::ZAxis );
451 mCubeRoot =
new Qt3DCore::QEntity;
452 mCubeRoot->setParent( mAxisSceneEntity );
453 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
454 mCubeRoot->addComponent( mAxisObjectLayer );
463 mAxisSceneEntity->setEnabled(
false );
464 setEnableAxis(
false );
465 setEnableCube(
false );
469 mAxisSceneEntity->setEnabled(
true );
472 setEnableCube(
false );
473 setEnableAxis(
true );
475 const QList< Qgis::CrsAxisDirection > axisDirections = mCrs.
axisOrdering();
477 if ( axisDirections.length() > 0 )
480 mTextY->setText(
"X?" );
482 if ( axisDirections.length() > 1 )
485 mTextY->setText(
"Y?" );
487 if ( axisDirections.length() > 2 )
490 mTextZ->setText( QStringLiteral(
"up" ) );
494 setEnableCube(
true );
495 setEnableAxis(
false );
499 setEnableCube(
false );
500 setEnableAxis(
true );
501 mTextX->setText(
"X?" );
502 mTextY->setText(
"Y?" );
503 mTextZ->setText(
"Z?" );
506 updateAxisLabelPosition();
510void Qgs3DAxis::createKeyboardShortCut()
515 QWidget *mapCanvas =
dynamic_cast<QWidget *
>( eng->parent() );
522 QShortcut *shortcutHome =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_1 ), mapCanvas );
523 connect( shortcutHome, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeHome();} );
525 QShortcut *shortcutTop =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_5 ), mapCanvas );
526 connect( shortcutTop, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeTop();} );
528 QShortcut *shortcutNorth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_8 ), mapCanvas );
529 connect( shortcutNorth, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeNorth();} );
531 QShortcut *shortcutEast =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_6 ), mapCanvas );
532 connect( shortcutEast, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeEast();} );
534 QShortcut *shortcutSouth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_2 ), mapCanvas );
535 connect( shortcutSouth, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeSouth();} );
537 QShortcut *shortcutWest =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ), mapCanvas );
538 connect( shortcutWest, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeWest();} );
543void Qgs3DAxis::createMenu()
548 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
549 typeOffAct->setCheckable(
true );
550 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
554 typeOffAct->setChecked(
true );
557 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
558 typeCrsAct->setCheckable(
true );
559 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
563 typeCrsAct->setChecked(
true );
566 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
567 typeCubeAct->setCheckable(
true );
568 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
572 typeCubeAct->setChecked(
true );
575 QActionGroup *typeGroup =
new QActionGroup( mMenu );
576 typeGroup->addAction( typeOffAct );
577 typeGroup->addAction( typeCrsAct );
578 typeGroup->addAction( typeCubeAct );
584 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
585 Q_ASSERT( typeMenu );
586 typeMenu->addAction( typeOffAct );
587 typeMenu->addAction( typeCrsAct );
588 typeMenu->addAction( typeCubeAct );
589 mMenu->addMenu( typeMenu );
592 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
593 hPosLeftAct->setCheckable(
true );
597 hPosLeftAct->setChecked(
true );
600 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
601 hPosMiddleAct->setCheckable(
true );
605 hPosMiddleAct->setChecked(
true );
608 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
609 hPosRightAct->setCheckable(
true );
613 hPosRightAct->setChecked(
true );
616 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
617 hPosGroup->addAction( hPosLeftAct );
618 hPosGroup->addAction( hPosMiddleAct );
619 hPosGroup->addAction( hPosRightAct );
621 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorLeft );} );
622 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter );} );
623 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorRight );} );
625 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
626 horizPosMenu->addAction( hPosLeftAct );
627 horizPosMenu->addAction( hPosMiddleAct );
628 horizPosMenu->addAction( hPosRightAct );
629 mMenu->addMenu( horizPosMenu );
632 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
633 vPosTopAct->setCheckable(
true );
637 vPosTopAct->setChecked(
true );
640 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
641 vPosMiddleAct->setCheckable(
true );
645 vPosMiddleAct->setChecked(
true );
648 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
649 vPosBottomAct->setCheckable(
true );
653 vPosBottomAct->setChecked(
true );
656 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
657 vPosGroup->addAction( vPosTopAct );
658 vPosGroup->addAction( vPosMiddleAct );
659 vPosGroup->addAction( vPosBottomAct );
661 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorTop );} );
662 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter );} );
663 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorBottom );} );
665 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
666 vertPosMenu->addAction( vPosTopAct );
667 vertPosMenu->addAction( vPosMiddleAct );
668 vertPosMenu->addAction( vPosBottomAct );
669 mMenu->addMenu( vertPosMenu );
672 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+1", mMenu );
673 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+5", mMenu );
674 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
675 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
676 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
677 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
678 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
680 connect( viewHomeAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeHome );
681 connect( viewTopAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeTop );
682 connect( viewNorthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeNorth );
683 connect( viewEastAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeEast );
684 connect( viewSouthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeSouth );
685 connect( viewWestAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeWest );
686 connect( viewBottomAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeBottom );
688 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
689 viewMenu->addAction( viewHomeAct );
690 viewMenu->addAction( viewTopAct );
691 viewMenu->addAction( viewNorthAct );
692 viewMenu->addAction( viewEastAct );
693 viewMenu->addAction( viewSouthAct );
694 viewMenu->addAction( viewWestAct );
695 viewMenu->addAction( viewBottomAct );
696 mMenu->addMenu( viewMenu );
702void Qgs3DAxis::hideMenu()
704 if ( mMenu && mMenu->isVisible() )
708void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
715 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
725void Qgs3DAxis::onAxisHorizPositionChanged( Qt::AnchorPoint pos )
732void Qgs3DAxis::onAxisVertPositionChanged( Qt::AnchorPoint pos )
739void Qgs3DAxis::onCameraViewChange(
float pitch,
float yaw )
742 double elevation = 0.0;
746 QVector3D camPos = mCameraController->
camera()->position();
747 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCameraController->
camera()->farPlane() );
749 if ( !hits.isEmpty() )
751 elevation = hits.at( 0 ).pos.z();
752 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
766void Qgs3DAxis::createCube( )
768 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
771 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
772 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
773 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
774 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f,
775 mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
777 cubeLineEntity->addComponent( cubeLine );
779 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
780 cubeLineMaterial->setAmbient( Qt::white );
781 cubeLineEntity->addComponent( cubeLineMaterial );
784 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
785 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
786 cubeMesh->setXExtent( mCylinderLength );
787 cubeMesh->setYExtent( mCylinderLength );
788 cubeMesh->setZExtent( mCylinderLength );
789 mCubeRoot->addComponent( cubeMesh );
791 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
792 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
793 cubeMaterial->setShininess( 100 );
794 mCubeRoot->addComponent( cubeMaterial );
796 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
797 QMatrix4x4 transformMatrixcube;
799 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
800 cubeTransform->setMatrix( transformMatrixcube );
801 mCubeRoot->addComponent( cubeTransform );
805 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
806 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
808 const QFont font = createFont( fontSize );
811 text = QStringLiteral(
"top" );
812 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
813 QVector3D translation = minPos + QVector3D(
814 mCylinderLength * 0.5f - textWidth / 2.0f,
815 mCylinderLength * 0.5f - textHeight / 2.0f,
816 mCylinderLength * 1.01f );
818 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
822 text = QStringLiteral(
"btm" );
823 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
824 QVector3D translation = minPos + QVector3D(
825 mCylinderLength * 0.5f - textWidth / 2.0f,
826 mCylinderLength * 0.5f + textHeight / 2.0f,
827 -mCylinderLength * 0.01f );
829 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
830 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
834 text = QStringLiteral(
"west" );
835 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
836 QVector3D translation = minPos + QVector3D(
837 - mCylinderLength * 0.01f,
838 mCylinderLength * 0.5f + textWidth / 2.0f,
839 mCylinderLength * 0.5f - textHeight / 2.0f );
841 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
842 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
843 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
847 text = QStringLiteral(
"east" );
848 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
849 QVector3D translation = minPos + QVector3D(
850 mCylinderLength * 1.01f,
851 mCylinderLength * 0.5f - textWidth / 2.0f,
852 mCylinderLength * 0.5f - textHeight / 2.0f );
854 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
855 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
856 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
860 text = QStringLiteral(
"south" );
861 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
862 QVector3D translation = minPos + QVector3D(
863 mCylinderLength * 0.5f - textWidth / 2.0f,
864 - mCylinderLength * 0.01f,
865 mCylinderLength * 0.5f - textHeight / 2.0f );
867 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
868 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
872 text = QStringLiteral(
"north" );
873 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
874 QVector3D translation = minPos + QVector3D(
875 mCylinderLength * 0.5f + textWidth / 2.0f,
876 mCylinderLength * 1.01f,
877 mCylinderLength * 0.5f - textHeight / 2.0f );
879 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
880 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
881 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
884 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
886 l->setParent( mCubeRoot );
890Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
892 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
893 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
894 textEntity->setFont( font );
895 textEntity->setHeight( textHeight );
896 textEntity->setWidth( textWidth );
897 textEntity->setColor( QColor( 192, 192, 192 ) );
898 textEntity->setText( text );
900 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
901 textFrontTransform->setMatrix( rotation );
902 textFrontTransform->setTranslation( translation );
903 textEntity->addComponent( textFrontTransform );
908void Qgs3DAxis::createAxis( Qt::Axis axisType )
910 float cylinderRadius = 0.05f * mCylinderLength;
911 float coneLength = 0.3f * mCylinderLength;
912 float coneBottomRadius = 0.1f * mCylinderLength;
914 QQuaternion rotation;
917 Qt3DExtras::QText2DEntity *text =
nullptr;
918 Qt3DCore::QTransform *textTransform =
nullptr;
923 case Qt::Axis::XAxis:
924 mTextX =
new Qt3DExtras::QText2DEntity( );
925 mTextX->setParent( mTwoDLabelSceneEntity );
926 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString & text )
928 updateAxisLabelText( mTextX, text );
930 mTextTransformX =
new Qt3DCore::QTransform();
931 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
933 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
936 textTransform = mTextTransformX;
937 name =
"3DAxis_axisX";
940 case Qt::Axis::YAxis:
941 mTextY =
new Qt3DExtras::QText2DEntity( );
942 mTextY->setParent( mTwoDLabelSceneEntity );
943 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString & text )
945 updateAxisLabelText( mTextY, text );
947 mTextTransformY =
new Qt3DCore::QTransform();
948 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
954 textTransform = mTextTransformY;
955 name =
"3DAxis_axisY";
958 case Qt::Axis::ZAxis:
959 mTextZ =
new Qt3DExtras::QText2DEntity( );
960 mTextZ->setParent( mTwoDLabelSceneEntity );
961 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString & text )
963 updateAxisLabelText( mTextZ, text );
965 mTextTransformZ =
new Qt3DCore::QTransform();
966 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
968 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
971 textTransform = mTextTransformZ;
972 name =
"3DAxis_axisZ";
980 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
981 cylinder->setObjectName( name );
983 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
984 cylinderMesh->setRadius( cylinderRadius );
985 cylinderMesh->setLength( mCylinderLength );
986 cylinderMesh->setRings( 10 );
987 cylinderMesh->setSlices( 4 );
988 cylinder->addComponent( cylinderMesh );
990 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
991 cylinderMaterial->setAmbient( color );
992 cylinderMaterial->setShininess( 0 );
993 cylinder->addComponent( cylinderMaterial );
995 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
996 QMatrix4x4 transformMatrixCylinder;
997 transformMatrixCylinder.rotate( rotation );
998 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
999 cylinderTransform->setMatrix( transformMatrixCylinder );
1000 cylinder->addComponent( cylinderTransform );
1003 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
1004 coneEntity->setObjectName( name );
1005 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
1006 coneMesh->setLength( coneLength );
1007 coneMesh->setBottomRadius( coneBottomRadius );
1008 coneMesh->setTopRadius( 0.0f );
1009 coneMesh->setRings( 10 );
1010 coneMesh->setSlices( 4 );
1011 coneEntity->addComponent( coneMesh );
1013 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
1014 coneMaterial->setAmbient( color );
1015 coneMaterial->setShininess( 0 );
1016 coneEntity->addComponent( coneMaterial );
1018 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
1019 QMatrix4x4 transformMatrixCone;
1020 transformMatrixCone.rotate( rotation );
1021 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
1022 coneTransform->setMatrix( transformMatrixCone );
1023 coneEntity->addComponent( coneTransform );
1026 text->setColor( QColor( 192, 192, 192, 192 ) );
1027 text->addComponent( textTransform );
1033 onAxisViewportSizeUpdate();
1036void Qgs3DAxis::onAxisViewportSizeUpdate(
int )
1040 double windowWidth = ( double )mCanvas->width();
1041 double windowHeight = ( double )mCanvas->height();
1046 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window w/h: %1px / %2px" )
1047 .arg( windowWidth ).arg( windowHeight ), 2 );
1048 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window physicalDpi %1 (%2, %3)" )
1049 .arg( mCanvas->screen()->physicalDotsPerInch() )
1050 .arg( mCanvas->screen()->physicalDotsPerInchX() )
1051 .arg( mCanvas->screen()->physicalDotsPerInchY() ), 2 );
1052 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window logicalDotsPerInch %1 (%2, %3)" )
1053 .arg( mCanvas->screen()->logicalDotsPerInch() )
1054 .arg( mCanvas->screen()->logicalDotsPerInchX() )
1055 .arg( mCanvas->screen()->logicalDotsPerInchY() ), 2 );
1057 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window pixel ratio %1" )
1058 .arg( mCanvas->screen()->devicePixelRatio() ), 2 );
1069 double defaultViewportPixelSize = ( ( double )settings.
defaultViewportSize() / 25.4 ) * 92.0;
1073 double viewportPixelSize = defaultViewportPixelSize + ( ( double )settings.
defaultViewportSize() / 25.4 )
1074 * ( mCanvas->screen()->physicalDotsPerInch() - 92.0 ) * 0.7;
1075 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate viewportPixelSize %1" ).arg( viewportPixelSize ), 2 );
1076 double widthRatio = viewportPixelSize / windowWidth;
1077 double heightRatio = widthRatio * windowWidth / windowHeight;
1079 QgsDebugMsgLevel( QString(
"3DAxis viewport ratios width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1081 if ( heightRatio * windowHeight < viewportPixelSize )
1083 heightRatio = viewportPixelSize / windowHeight;
1084 widthRatio = heightRatio * windowHeight / windowWidth;
1085 QgsDebugMsgLevel( QString(
"3DAxis viewport, height too small, ratios adjusted to width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1090 QgsDebugMsgLevel(
"viewport takes too much place into the 3d view, disabling it", 2 );
1092 mViewport->setEnabled(
false );
1093 setEnableCube(
false );
1094 setEnableAxis(
false );
1099 mAxisScaleFactor = viewportPixelSize / defaultViewportPixelSize;
1100 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
1102 if ( ! mViewport->isEnabled() )
1105 setEnableAxis(
true );
1107 setEnableCube(
true );
1109 mViewport->setEnabled(
true );
1111 float xRatio = 1.0f;
1112 float yRatio = 1.0f;
1116 xRatio = 0.5f -
static_cast<float>( widthRatio ) / 2.0f;
1118 xRatio = 1.0f -
static_cast<float>( widthRatio );
1122 else if ( settings.
verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
1123 yRatio = 0.5f -
static_cast<float>( heightRatio ) / 2.0f;
1125 yRatio = 1.0f -
static_cast<float>( heightRatio );
1127 QgsDebugMsgLevel( QString(
"Qgs3DAxis: update viewport: %1 x %2 x %3 x %4" ).arg( xRatio ).arg( yRatio ).arg( widthRatio ).arg( heightRatio ), 2 );
1128 mViewport->setNormalizedRect( QRectF( xRatio, yRatio, widthRatio, heightRatio ) );
1132 const float halfWidthSize =
static_cast<float>( windowWidth * widthRatio / 2.0 );
1133 const float halfHeightSize =
static_cast<float>( windowWidth * widthRatio / 2.0 );
1134 mTwoDLabelCamera->lens()->setOrthographicProjection(
1135 -halfWidthSize, halfWidthSize,
1136 -halfHeightSize, halfHeightSize,
1137 mTwoDLabelCamera->lens()->nearPlane(), mTwoDLabelCamera->lens()->farPlane() );
1139 updateAxisLabelPosition();
1144void Qgs3DAxis::onCameraUpdate( )
1146 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
1148 if ( parentCamera->viewVector() != mPreviousVector
1149 && !std::isnan( parentCamera->viewVector().x() )
1150 && !std::isnan( parentCamera->viewVector().y() )
1151 && !std::isnan( parentCamera->viewVector().z() ) )
1153 mPreviousVector = parentCamera->viewVector();
1155 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
1156 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
1157 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
1159 if ( mAxisRoot->isEnabled() )
1161 updateAxisLabelPosition();
1166void Qgs3DAxis::updateAxisLabelPosition()
1168 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
1170 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1171 updateAxisLabelText( mTextX, mTextX->text() );
1173 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1174 updateAxisLabelText( mTextY, mTextY->text() );
1176 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1177 updateAxisLabelText( mTextZ, mTextZ->text() );
1181void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
1183 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
1184 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
1185 textEntity->setFont( font );
1186 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
1187 textEntity->setHeight( 1.5f * scaledFontSize );
1190QFont Qgs3DAxis::createFont(
int pointSize )
1192 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1193 font.setPointSize( pointSize );
1194 font.setWeight( QFont::Weight::Black );
1195 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)
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
float terrainElevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down)
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)
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.