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>
55 , mMapScene( mapScene )
56 , mCameraController( cameraCtrl )
61 mCanvas, mCameraController, mMapSettings,
67 Q_ASSERT( mRenderView );
68 constructAxisScene( parent3DScene );
69 constructLabelsScene( parent3DScene );
71 mTwoDLabelSceneEntity->addComponent( mRenderView->
labelLayer() );
76 onAxisViewportSizeUpdate();
78 init3DObjectPicking();
80 createKeyboardShortCut();
112void Qgs3DAxis::init3DObjectPicking()
120 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
121 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
122 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
123 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
125 mAxisSceneEntity->addComponent( mScreenRayCaster );
127 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
130 mCanvas->installEventFilter(
this );
133bool Qgs3DAxis::eventFilter( QObject *watched, QEvent *event )
135 if ( watched != mCanvas )
138 if ( event->type() == QEvent::MouseButtonPress )
142 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
143 mLastClickedPos = mouseEvent->pos();
147 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
149 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
152 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
158 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
165 else if ( !mIsDragging )
168 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
172 std::ostringstream os;
173 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->
viewport()->normalizedRect();
177 if ( mRenderView->
viewport()->normalizedRect().contains( normalizedPos ) )
179 mLastClickedButton = mouseEvent->button();
180 mLastClickedPos = mouseEvent->pos();
183 mScreenRayCaster->trigger( mLastClickedPos );
189 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
191 mCanvas->setCursor( mPreviousCursor );
192 mPreviousCursor = Qt::ArrowCursor;
196 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
197 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
199 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
212void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
219 std::ostringstream os;
220 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
221 for (
int i = 0; i < hits.length(); ++i )
224 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
225 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
226 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
227 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
232 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
234 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 ) )
241 if ( mLastClickedButton == Qt::NoButton )
243 if ( mHitsFound != -1 )
245 if ( mCanvas->cursor() != Qt::ArrowCursor )
247 mPreviousCursor = mCanvas->cursor();
248 mCanvas->setCursor( Qt::ArrowCursor );
252 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
254 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
260 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
262 displayMenuAt( mLastClickedPos );
264 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
268 if ( mHitsFound != -1 )
270 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
272 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
276 onCameraViewChangeEast();
281 onCameraViewChangeWest();
286 onCameraViewChangeNorth();
291 onCameraViewChangeSouth();
296 onCameraViewChangeTop();
301 onCameraViewChangeBottom();
312void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
314 mAxisSceneEntity =
new Qt3DCore::QEntity;
315 mAxisSceneEntity->setParent( parent3DScene );
316 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
319 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
320 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
324void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
326 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
327 mTwoDLabelSceneEntity->setParent( parent3DScene );
328 mTwoDLabelSceneEntity->setEnabled(
true );
331 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
332 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
333 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
338 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
339 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
340 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
342 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
343 destPos.setZ( 0.0f );
347void Qgs3DAxis::setEnableCube(
bool show )
349 mCubeRoot->setEnabled( show );
352 mCubeRoot->setParent( mAxisSceneEntity );
356 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
360void Qgs3DAxis::setEnableAxis(
bool show )
362 mAxisRoot->setEnabled( show );
365 mAxisRoot->setParent( mAxisSceneEntity );
369 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
372 mTextX->setEnabled( show );
373 mTextY->setEnabled( show );
374 mTextZ->setEnabled( show );
377void Qgs3DAxis::createAxisScene()
379 if ( !mAxisRoot || !mCubeRoot )
381 mAxisRoot =
new Qt3DCore::QEntity;
382 mAxisRoot->setParent( mAxisSceneEntity );
383 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
384 mAxisRoot->addComponent( mRenderView->
objectLayer() );
386 createAxis( Qt::Axis::XAxis );
387 createAxis( Qt::Axis::YAxis );
388 createAxis( Qt::Axis::ZAxis );
390 mCubeRoot =
new Qt3DCore::QEntity;
391 mCubeRoot->setParent( mAxisSceneEntity );
392 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
393 mCubeRoot->addComponent( mRenderView->
objectLayer() );
402 mAxisSceneEntity->setEnabled(
false );
403 setEnableAxis(
false );
404 setEnableCube(
false );
410 mAxisSceneEntity->setEnabled(
true );
413 setEnableCube(
false );
414 setEnableAxis(
true );
416 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.
axisOrdering();
418 if ( axisDirections.length() > 0 )
421 mTextX->setText(
"X?" );
423 if ( axisDirections.length() > 1 )
426 mTextY->setText(
"Y?" );
428 if ( axisDirections.length() > 2 )
431 mTextZ->setText( QStringLiteral(
"up" ) );
435 setEnableCube(
true );
436 setEnableAxis(
false );
440 setEnableCube(
false );
441 setEnableAxis(
true );
442 mTextX->setText(
"X?" );
443 mTextY->setText(
"Y?" );
444 mTextZ->setText(
"Z?" );
447 updateAxisLabelPosition();
451void Qgs3DAxis::createKeyboardShortCut()
456 QWidget *mapCanvas =
dynamic_cast<QWidget *
>( eng->parent() );
463 QShortcut *shortcutHome =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_1 ), mapCanvas );
464 connect( shortcutHome, &QShortcut::activated,
this, [
this]() { onCameraViewChangeHome(); } );
466 QShortcut *shortcutTop =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_5 ), mapCanvas );
467 connect( shortcutTop, &QShortcut::activated,
this, [
this]() { onCameraViewChangeTop(); } );
469 QShortcut *shortcutNorth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_8 ), mapCanvas );
470 connect( shortcutNorth, &QShortcut::activated,
this, [
this]() { onCameraViewChangeNorth(); } );
472 QShortcut *shortcutEast =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_6 ), mapCanvas );
473 connect( shortcutEast, &QShortcut::activated,
this, [
this]() { onCameraViewChangeEast(); } );
475 QShortcut *shortcutSouth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_2 ), mapCanvas );
476 connect( shortcutSouth, &QShortcut::activated,
this, [
this]() { onCameraViewChangeSouth(); } );
478 QShortcut *shortcutWest =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ), mapCanvas );
479 connect( shortcutWest, &QShortcut::activated,
this, [
this]() { onCameraViewChangeWest(); } );
484void Qgs3DAxis::createMenu()
489 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
490 typeOffAct->setCheckable(
true );
491 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
494 typeOffAct->setChecked(
true );
497 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
498 typeCrsAct->setCheckable(
true );
499 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
502 typeCrsAct->setChecked(
true );
505 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
506 typeCubeAct->setCheckable(
true );
507 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
510 typeCubeAct->setChecked(
true );
513 QActionGroup *typeGroup =
new QActionGroup( mMenu );
514 typeGroup->addAction( typeOffAct );
515 typeGroup->addAction( typeCrsAct );
516 typeGroup->addAction( typeCubeAct );
522 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
523 Q_ASSERT( typeMenu );
524 typeMenu->addAction( typeOffAct );
525 typeMenu->addAction( typeCrsAct );
526 typeMenu->addAction( typeCubeAct );
527 mMenu->addMenu( typeMenu );
530 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
531 hPosLeftAct->setCheckable(
true );
534 hPosLeftAct->setChecked(
true );
537 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
538 hPosMiddleAct->setCheckable(
true );
541 hPosMiddleAct->setChecked(
true );
544 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
545 hPosRightAct->setCheckable(
true );
548 hPosRightAct->setChecked(
true );
551 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
552 hPosGroup->addAction( hPosLeftAct );
553 hPosGroup->addAction( hPosMiddleAct );
554 hPosGroup->addAction( hPosRightAct );
556 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
557 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
558 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
560 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
561 horizPosMenu->addAction( hPosLeftAct );
562 horizPosMenu->addAction( hPosMiddleAct );
563 horizPosMenu->addAction( hPosRightAct );
564 mMenu->addMenu( horizPosMenu );
567 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
568 vPosTopAct->setCheckable(
true );
571 vPosTopAct->setChecked(
true );
574 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
575 vPosMiddleAct->setCheckable(
true );
578 vPosMiddleAct->setChecked(
true );
581 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
582 vPosBottomAct->setCheckable(
true );
585 vPosBottomAct->setChecked(
true );
588 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
589 vPosGroup->addAction( vPosTopAct );
590 vPosGroup->addAction( vPosMiddleAct );
591 vPosGroup->addAction( vPosBottomAct );
593 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
594 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
595 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
597 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
598 vertPosMenu->addAction( vPosTopAct );
599 vertPosMenu->addAction( vPosMiddleAct );
600 vertPosMenu->addAction( vPosBottomAct );
601 mMenu->addMenu( vertPosMenu );
604 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+1", mMenu );
605 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+5", mMenu );
606 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
607 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
608 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
609 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
610 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
612 connect( viewHomeAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeHome );
613 connect( viewTopAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeTop );
614 connect( viewNorthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeNorth );
615 connect( viewEastAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeEast );
616 connect( viewSouthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeSouth );
617 connect( viewWestAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeWest );
618 connect( viewBottomAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeBottom );
620 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
621 viewMenu->addAction( viewHomeAct );
622 viewMenu->addAction( viewTopAct );
623 viewMenu->addAction( viewNorthAct );
624 viewMenu->addAction( viewEastAct );
625 viewMenu->addAction( viewSouthAct );
626 viewMenu->addAction( viewWestAct );
627 viewMenu->addAction( viewBottomAct );
628 mMenu->addMenu( viewMenu );
634void Qgs3DAxis::hideMenu()
636 if ( mMenu && mMenu->isVisible() )
640void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
647 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
657void Qgs3DAxis::onCameraViewChange(
float pitch,
float yaw )
660 double elevation = 0.0;
664 QVector3D camPos = mCameraController->
camera()->position();
665 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCameraController->
camera()->farPlane() );
667 if ( !hits.isEmpty() )
669 elevation = hits.at( 0 ).pos.z();
670 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
683void Qgs3DAxis::createCube()
685 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
688 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
689 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
690 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
691 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
693 cubeLineEntity->addComponent( cubeLine );
695 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
696 cubeLineMaterial->setAmbient( Qt::white );
697 cubeLineEntity->addComponent( cubeLineMaterial );
700 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
701 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
702 cubeMesh->setXExtent( mCylinderLength );
703 cubeMesh->setYExtent( mCylinderLength );
704 cubeMesh->setZExtent( mCylinderLength );
705 mCubeRoot->addComponent( cubeMesh );
707 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
708 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
709 cubeMaterial->setShininess( 100 );
710 mCubeRoot->addComponent( cubeMaterial );
712 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
713 QMatrix4x4 transformMatrixcube;
715 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
716 cubeTransform->setMatrix( transformMatrixcube );
717 mCubeRoot->addComponent( cubeTransform );
721 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
722 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
724 const QFont font = createFont( fontSize );
727 text = QStringLiteral(
"top" );
728 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
729 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
731 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
735 text = QStringLiteral(
"btm" );
736 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
737 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
739 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
740 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
744 text = QStringLiteral(
"west" );
745 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
746 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
748 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
749 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
750 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
754 text = QStringLiteral(
"east" );
755 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
756 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
758 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
759 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
760 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
764 text = QStringLiteral(
"south" );
765 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
766 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
768 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
769 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
773 text = QStringLiteral(
"north" );
774 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
775 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
777 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
778 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
779 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
782 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
784 l->setParent( mCubeRoot );
788Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
790 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
791 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
792 textEntity->setFont( font );
793 textEntity->setHeight( textHeight );
794 textEntity->setWidth( textWidth );
795 textEntity->setColor( QColor( 192, 192, 192 ) );
796 textEntity->setText( text );
798 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
799 textFrontTransform->setMatrix( rotation );
800 textFrontTransform->setTranslation( translation );
801 textEntity->addComponent( textFrontTransform );
806void Qgs3DAxis::createAxis( Qt::Axis axisType )
808 float cylinderRadius = 0.05f * mCylinderLength;
809 float coneLength = 0.3f * mCylinderLength;
810 float coneBottomRadius = 0.1f * mCylinderLength;
812 QQuaternion rotation;
815 Qt3DExtras::QText2DEntity *text =
nullptr;
816 Qt3DCore::QTransform *textTransform =
nullptr;
821 case Qt::Axis::XAxis:
822 mTextX =
new Qt3DExtras::QText2DEntity();
823 mTextX->setParent( mTwoDLabelSceneEntity );
824 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
825 updateAxisLabelText( mTextX, text );
827 mTextTransformX =
new Qt3DCore::QTransform();
828 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
830 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
833 textTransform = mTextTransformX;
834 name =
"3DAxis_axisX";
837 case Qt::Axis::YAxis:
838 mTextY =
new Qt3DExtras::QText2DEntity();
839 mTextY->setParent( mTwoDLabelSceneEntity );
840 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
841 updateAxisLabelText( mTextY, text );
843 mTextTransformY =
new Qt3DCore::QTransform();
844 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
850 textTransform = mTextTransformY;
851 name =
"3DAxis_axisY";
854 case Qt::Axis::ZAxis:
855 mTextZ =
new Qt3DExtras::QText2DEntity();
856 mTextZ->setParent( mTwoDLabelSceneEntity );
857 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
858 updateAxisLabelText( mTextZ, text );
860 mTextTransformZ =
new Qt3DCore::QTransform();
861 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
863 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
866 textTransform = mTextTransformZ;
867 name =
"3DAxis_axisZ";
875 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
876 cylinder->setObjectName( name );
878 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
879 cylinderMesh->setRadius( cylinderRadius );
880 cylinderMesh->setLength( mCylinderLength );
881 cylinderMesh->setRings( 10 );
882 cylinderMesh->setSlices( 4 );
883 cylinder->addComponent( cylinderMesh );
885 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
886 cylinderMaterial->setAmbient( color );
887 cylinderMaterial->setShininess( 0 );
888 cylinder->addComponent( cylinderMaterial );
890 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
891 QMatrix4x4 transformMatrixCylinder;
892 transformMatrixCylinder.rotate( rotation );
893 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
894 cylinderTransform->setMatrix( transformMatrixCylinder );
895 cylinder->addComponent( cylinderTransform );
898 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
899 coneEntity->setObjectName( name );
900 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
901 coneMesh->setLength( coneLength );
902 coneMesh->setBottomRadius( coneBottomRadius );
903 coneMesh->setTopRadius( 0.0f );
904 coneMesh->setRings( 10 );
905 coneMesh->setSlices( 4 );
906 coneEntity->addComponent( coneMesh );
908 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
909 coneMaterial->setAmbient( color );
910 coneMaterial->setShininess( 0 );
911 coneEntity->addComponent( coneMaterial );
913 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
914 QMatrix4x4 transformMatrixCone;
915 transformMatrixCone.rotate( rotation );
916 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
917 coneTransform->setMatrix( transformMatrixCone );
918 coneEntity->addComponent( coneTransform );
921 text->setColor( QColor( 192, 192, 192, 192 ) );
922 text->addComponent( textTransform );
928 onAxisViewportSizeUpdate();
931void Qgs3DAxis::onAxisViewportSizeUpdate()
940 updateAxisLabelPosition();
947 if ( !mAxisRoot || !mCubeRoot )
952 if ( scaleFactor > 0.0 )
956 setEnableAxis(
true );
958 setEnableCube(
true );
960 mAxisScaleFactor = scaleFactor;
961 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
965 setEnableCube(
false );
966 setEnableAxis(
false );
970void Qgs3DAxis::onCameraUpdate()
972 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
974 if ( parentCamera->viewVector() != mPreviousVector
975 && !std::isnan( parentCamera->viewVector().x() )
976 && !std::isnan( parentCamera->viewVector().y() )
977 && !std::isnan( parentCamera->viewVector().z() ) )
979 mPreviousVector = parentCamera->viewVector();
981 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
982 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
983 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
985 if ( mAxisRoot->isEnabled() )
987 updateAxisLabelPosition();
992void Qgs3DAxis::updateAxisLabelPosition()
994 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
996 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
997 updateAxisLabelText( mTextX, mTextX->text() );
999 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1000 updateAxisLabelText( mTextY, mTextY->text() );
1002 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1003 updateAxisLabelText( mTextZ, mTextZ->text() );
1007void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
1009 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
1010 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
1011 textEntity->setFont( font );
1012 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
1013 textEntity->setHeight( 1.5f * scaledFontSize );
1016QFont Qgs3DAxis::createFont(
int pointSize )
1018 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1019 font.setPointSize( pointSize );
1020 font.setWeight( QFont::Weight::Black );
1021 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
Qt3DRender::QLayer * labelLayer() const
Returns the layer to be used by entities to be included in the label renderpass.
void onVerticalPositionChanged(Qt::AnchorPoint position)
Updates viewport vertical position.
Qt3DRender::QCamera * labelCamera() const
Returns camera used for billboarded labels.
Qt3DRender::QLayer * objectLayer() const
Returns main object layer.
Qt3DRender::QViewport * viewport() const
Returns the viewport associated to this renderview.
void onViewportSizeUpdate(int width=-1, int height=-1)
Updates viewport size. Uses canvas size by default.
Qt3DRender::QCamera * objectCamera() const
Returns main object camera (used for axis or cube)
void onHorizontalPositionChanged(Qt::AnchorPoint position)
Updates viewport horizontal position.
Contains the configuration of a 3d axis.
void setMode(Qgs3DAxisSettings::Mode type)
Sets the type of the 3daxis.
Mode
Axis representation enum.
@ Crs
Respect CRS directions.
@ Cube
Abstract cube mode.
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
void onViewportScaleFactorChanged(double scaleFactor)
Used as callback from renderview when viewport scale factor changes.
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.
Convenience wrapper to simplify the creation of a 3D window ready to be used with QGIS.
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
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...
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.
Axis-aligned bounding box - in world coords.
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
QgsFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
virtual void setEnabled(bool enable)
Enable or disable via enable the render view sub tree.
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
Object that controls camera movement based on user input.
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.
QgsAbstractRenderView * renderView(const QString &name)
Returns the render view named name, if any.
bool registerRenderView(std::unique_ptr< QgsAbstractRenderView > renderView, const QString &name)
Registers a new the render view renderView with name name.
static const QString AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
static void warning(const QString &msg)
Goes to qWarning.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
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.
On-screen 3D engine: it creates an OpenGL window (QWindow) and displays rendered 3D scenes there.
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs
Helper struct to store ray casting parameters.