30#include <QActionGroup>
31#include <QApplication>
32#include <QFontDatabase>
35#include <Qt3DCore/QEntity>
36#include <Qt3DCore/QTransform>
37#include <Qt3DExtras/QConeMesh>
38#include <Qt3DExtras/QCuboidMesh>
39#include <Qt3DExtras/QCylinderMesh>
40#include <Qt3DExtras/QPhongMaterial>
41#include <Qt3DExtras/QText2DEntity>
42#include <Qt3DRender/QCamera>
43#include <Qt3DRender/QPointLight>
44#include <Qt3DRender/QRenderSettings>
45#include <Qt3DRender/QScreenRayCaster>
46#include <Qt3DRender/QSortPolicy>
48#include "moc_qgs3daxis.cpp"
50using namespace Qt::StringLiterals;
54 Qt3DCore::QEntity *parent3DScene,
62 , mMapScene( mapScene )
63 , mCameraController( cameraCtrl )
66 mMapScene->engine()->frameGraph()->registerRenderView( std::make_unique<Qgs3DAxisRenderView>(
68 mCanvas, mCameraController, mMapSettings,
74 Q_ASSERT( mRenderView );
75 constructAxisScene( parent3DScene );
76 constructLabelsScene( parent3DScene );
78 mTwoDLabelSceneEntity->addComponent( mRenderView->labelLayer() );
83 onAxisViewportSizeUpdate();
85 init3DObjectPicking();
93 switch ( mMapSettings->get3DAxisSettings().mode() )
114void Qgs3DAxis::init3DObjectPicking()
122 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
123 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
124 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
125 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
127 mAxisSceneEntity->addComponent( mScreenRayCaster );
129 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
135 if ( event->type() == QEvent::MouseButtonPress )
139 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
140 mLastClickedPos = mouseEvent->pos();
144 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
146 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
149 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
155 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
162 else if ( !mIsDragging )
165 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
169 std::ostringstream os;
170 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->viewport()->normalizedRect();
174 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
176 mLastClickedButton = mouseEvent->button();
177 mLastClickedPos = mouseEvent->pos();
180 mScreenRayCaster->trigger( mLastClickedPos );
186 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
188 mCanvas->setCursor( mPreviousCursor );
189 mPreviousCursor = Qt::ArrowCursor;
193 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
194 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
196 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
209void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
211 int hitFoundIdx = -1;
216 std::ostringstream os;
217 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
218 for (
int i = 0; i < hits.length(); ++i )
221 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
222 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
223 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
224 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
229 for (
int i = 0; i < hits.length() && hitFoundIdx == -1; ++i )
231 Qt3DCore::QEntity *hitEntity = hits.at( i ).entity();
234 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
236 hitEntity = hitEntity->parentEntity();
238 if ( hits.at( i ).distance() < 500.0f && hitEntity && ( hitEntity == mCubeRoot || hitEntity == mAxisRoot || hitEntity->parent() == mCubeRoot || hitEntity->parent() == mAxisRoot ) )
245 if ( mLastClickedButton == Qt::NoButton )
247 if ( hitFoundIdx != -1 )
249 if ( mCanvas->cursor() != Qt::ArrowCursor )
251 mPreviousCursor = mCanvas->cursor();
252 mCanvas->setCursor( Qt::ArrowCursor );
256 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
258 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
264 else if ( mLastClickedButton == Qt::MouseButton::RightButton && hitFoundIdx != -1 )
266 displayMenuAt( mLastClickedPos );
268 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
272 if ( hitFoundIdx != -1 )
274 Qt3DCore::QEntity *hitEntity = hits.at( hitFoundIdx ).entity();
275 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
277 hitEntity = hitEntity->parentEntity();
279 if ( hitEntity && ( hitEntity == mCubeRoot || hitEntity->parent() == mCubeRoot ) )
281 switch ( hits.at( hitFoundIdx ).primitiveIndex() / 2 )
285 mCameraController->rotateCameraToEast();
290 mCameraController->rotateCameraToWest();
295 mCameraController->rotateCameraToNorth();
300 mCameraController->rotateCameraToSouth();
305 mCameraController->rotateCameraToTop();
310 mCameraController->rotateCameraToBottom();
321void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
323 mAxisSceneEntity =
new Qt3DCore::QEntity;
324 mAxisSceneEntity->setParent( parent3DScene );
325 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
327 mAxisCamera = mRenderView->objectCamera();
328 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
329 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
333void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
335 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
336 mTwoDLabelSceneEntity->setParent( parent3DScene );
337 mTwoDLabelSceneEntity->setEnabled(
true );
339 mTwoDLabelCamera = mRenderView->labelCamera();
340 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
341 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
342 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
347 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
348 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
349 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
351 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
352 destPos.setZ( 0.0f );
356void Qgs3DAxis::setEnableCube(
bool show )
358 mCubeRoot->setEnabled( show );
361 mCubeRoot->setParent( mAxisSceneEntity );
365 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
369void Qgs3DAxis::setEnableAxis(
bool show )
371 mAxisRoot->setEnabled( show );
374 mAxisRoot->setParent( mAxisSceneEntity );
378 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
381 mTextX->setEnabled( show );
382 mTextY->setEnabled( show );
383 mTextZ->setEnabled( show );
386void Qgs3DAxis::createAxisScene()
388 if ( !mAxisRoot || !mCubeRoot )
390 mAxisRoot =
new Qt3DCore::QEntity;
391 mAxisRoot->setParent( mAxisSceneEntity );
392 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
393 mAxisRoot->addComponent( mRenderView->objectLayer() );
395 createAxis( Qt::Axis::XAxis );
396 createAxis( Qt::Axis::YAxis );
397 createAxis( Qt::Axis::ZAxis );
399 mCubeRoot =
new Qt3DCore::QEntity;
400 mCubeRoot->setParent( mAxisSceneEntity );
401 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
402 mCubeRoot->addComponent( mRenderView->objectLayer() );
411 mAxisSceneEntity->setEnabled(
false );
412 setEnableAxis(
false );
413 setEnableCube(
false );
414 mRenderView->setEnabled(
false );
418 mRenderView->setEnabled(
true );
419 mAxisSceneEntity->setEnabled(
true );
422 setEnableCube(
false );
423 setEnableAxis(
true );
425 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
427 if ( axisDirections.length() > 0 )
430 mTextX->setText(
"X?" );
432 if ( axisDirections.length() > 1 )
435 mTextY->setText(
"Y?" );
437 if ( axisDirections.length() > 2 )
440 mTextZ->setText( u
"up"_s );
444 setEnableCube(
true );
445 setEnableAxis(
false );
449 setEnableCube(
false );
450 setEnableAxis(
true );
451 mTextX->setText(
"X?" );
452 mTextY->setText(
"Y?" );
453 mTextZ->setText(
"Z?" );
456 updateAxisLabelPosition();
460void Qgs3DAxis::createMenu()
465 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu.get() );
466 typeOffAct->setCheckable(
true );
467 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
470 typeOffAct->setChecked(
true );
473 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu.get() );
474 typeCrsAct->setCheckable(
true );
475 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
478 typeCrsAct->setChecked(
true );
481 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu.get() );
482 typeCubeAct->setCheckable(
true );
483 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
486 typeCubeAct->setChecked(
true );
489 QActionGroup *typeGroup =
new QActionGroup( mMenu.get() );
490 typeGroup->addAction( typeOffAct );
491 typeGroup->addAction( typeCrsAct );
492 typeGroup->addAction( typeCubeAct );
498 QMenu *typeMenu =
new QMenu( u
"Axis Type"_s, mMenu.get() );
499 Q_ASSERT( typeMenu );
500 typeMenu->addAction( typeOffAct );
501 typeMenu->addAction( typeCrsAct );
502 typeMenu->addAction( typeCubeAct );
503 mMenu->addMenu( typeMenu );
506 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu.get() );
507 hPosLeftAct->setCheckable(
true );
509 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
510 hPosLeftAct->setChecked(
true );
513 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu.get() );
514 hPosMiddleAct->setCheckable(
true );
516 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
517 hPosMiddleAct->setChecked(
true );
520 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu.get() );
521 hPosRightAct->setCheckable(
true );
523 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
524 hPosRightAct->setChecked(
true );
527 QActionGroup *hPosGroup =
new QActionGroup( mMenu.get() );
528 hPosGroup->addAction( hPosLeftAct );
529 hPosGroup->addAction( hPosMiddleAct );
530 hPosGroup->addAction( hPosRightAct );
532 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
533 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
534 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
536 QMenu *horizPosMenu =
new QMenu( u
"Horizontal Position"_s, mMenu.get() );
537 horizPosMenu->addAction( hPosLeftAct );
538 horizPosMenu->addAction( hPosMiddleAct );
539 horizPosMenu->addAction( hPosRightAct );
540 mMenu->addMenu( horizPosMenu );
543 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu.get() );
544 vPosTopAct->setCheckable(
true );
546 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
547 vPosTopAct->setChecked(
true );
550 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu.get() );
551 vPosMiddleAct->setCheckable(
true );
553 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
554 vPosMiddleAct->setChecked(
true );
557 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu.get() );
558 vPosBottomAct->setCheckable(
true );
560 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
561 vPosBottomAct->setChecked(
true );
564 QActionGroup *vPosGroup =
new QActionGroup( mMenu.get() );
565 vPosGroup->addAction( vPosTopAct );
566 vPosGroup->addAction( vPosMiddleAct );
567 vPosGroup->addAction( vPosBottomAct );
569 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
570 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
571 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
573 QMenu *vertPosMenu =
new QMenu( u
"Vertical Position"_s, mMenu.get() );
574 vertPosMenu->addAction( vPosTopAct );
575 vertPosMenu->addAction( vPosMiddleAct );
576 vertPosMenu->addAction( vPosBottomAct );
577 mMenu->addMenu( vertPosMenu );
581 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu.get() );
582 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu.get() );
583 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu.get() );
584 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu.get() );
585 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu.get() );
586 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu.get() );
587 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu.get() );
597 QMenu *viewMenu =
new QMenu( u
"Camera View"_s, mMenu.get() );
598 viewMenu->addAction( viewHomeAct );
599 viewMenu->addAction( viewTopAct );
600 viewMenu->addAction( viewNorthAct );
601 viewMenu->addAction( viewEastAct );
602 viewMenu->addAction( viewSouthAct );
603 viewMenu->addAction( viewWestAct );
604 viewMenu->addAction( viewBottomAct );
605 mMenu->addMenu( viewMenu );
608 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(),
true );
611void Qgs3DAxis::hideMenu()
613 if ( mMenu && mMenu->isVisible() )
617void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
624 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
629 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
631 mMapSettings->set3DAxisSettings( s );
634void Qgs3DAxis::createCube()
636 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
639 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
640 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
641 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
642 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
644 cubeLineEntity->addComponent( cubeLine );
646 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
647 cubeLineMaterial->setAmbient( Qt::white );
648 cubeLineEntity->addComponent( cubeLineMaterial );
651 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
652 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
653 cubeMesh->setXExtent( mCylinderLength );
654 cubeMesh->setYExtent( mCylinderLength );
655 cubeMesh->setZExtent( mCylinderLength );
656 mCubeRoot->addComponent( cubeMesh );
658 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
659 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
660 cubeMaterial->setShininess( 100 );
661 mCubeRoot->addComponent( cubeMaterial );
663 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
664 QMatrix4x4 transformMatrixcube;
666 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
667 cubeTransform->setMatrix( transformMatrixcube );
668 mCubeRoot->addComponent( cubeTransform );
672 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
673 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
675 const QFont font = createFont( fontSize );
679 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
680 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
682 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
687 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
688 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
690 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
691 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
696 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
697 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
699 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
700 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
701 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
706 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
707 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
709 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
710 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
711 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
716 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
717 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
719 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
720 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
725 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
726 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
728 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
729 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
730 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
733 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
735 l->setParent( mCubeRoot );
739Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
741 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
742 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
743 textEntity->setFont( font );
744 textEntity->setHeight( textHeight );
745 textEntity->setWidth( textWidth );
746 textEntity->setColor( QColor( 192, 192, 192 ) );
747 textEntity->setText( text );
749 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
750 textFrontTransform->setMatrix( rotation );
751 textFrontTransform->setTranslation( translation );
752 textEntity->addComponent( textFrontTransform );
757void Qgs3DAxis::createAxis( Qt::Axis axisType )
759 float cylinderRadius = 0.05f * mCylinderLength;
760 float coneLength = 0.3f * mCylinderLength;
761 float coneBottomRadius = 0.1f * mCylinderLength;
763 QQuaternion rotation;
766 Qt3DExtras::QText2DEntity *text =
nullptr;
767 Qt3DCore::QTransform *textTransform =
nullptr;
772 case Qt::Axis::XAxis:
773 mTextX =
new Qt3DExtras::QText2DEntity();
774 mTextX->setParent( mTwoDLabelSceneEntity );
775 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextX, text ); } );
776 mTextTransformX =
new Qt3DCore::QTransform();
777 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
779 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
782 textTransform = mTextTransformX;
783 name =
"3DAxis_axisX";
786 case Qt::Axis::YAxis:
787 mTextY =
new Qt3DExtras::QText2DEntity();
788 mTextY->setParent( mTwoDLabelSceneEntity );
789 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextY, text ); } );
790 mTextTransformY =
new Qt3DCore::QTransform();
791 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
797 textTransform = mTextTransformY;
798 name =
"3DAxis_axisY";
801 case Qt::Axis::ZAxis:
802 mTextZ =
new Qt3DExtras::QText2DEntity();
803 mTextZ->setParent( mTwoDLabelSceneEntity );
804 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextZ, text ); } );
805 mTextTransformZ =
new Qt3DCore::QTransform();
806 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
808 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
811 textTransform = mTextTransformZ;
812 name =
"3DAxis_axisZ";
820 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
821 cylinder->setObjectName( name );
823 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
824 cylinderMesh->setRadius( cylinderRadius );
825 cylinderMesh->setLength( mCylinderLength );
826 cylinderMesh->setRings( 10 );
827 cylinderMesh->setSlices( 4 );
828 cylinder->addComponent( cylinderMesh );
830 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
831 cylinderMaterial->setAmbient( color );
832 cylinderMaterial->setShininess( 0 );
833 cylinder->addComponent( cylinderMaterial );
835 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
836 QMatrix4x4 transformMatrixCylinder;
837 transformMatrixCylinder.rotate( rotation );
838 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
839 cylinderTransform->setMatrix( transformMatrixCylinder );
840 cylinder->addComponent( cylinderTransform );
843 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
844 coneEntity->setObjectName( name );
845 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
846 coneMesh->setLength( coneLength );
847 coneMesh->setBottomRadius( coneBottomRadius );
848 coneMesh->setTopRadius( 0.0f );
849 coneMesh->setRings( 10 );
850 coneMesh->setSlices( 4 );
851 coneEntity->addComponent( coneMesh );
853 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
854 coneMaterial->setAmbient( color );
855 coneMaterial->setShininess( 0 );
856 coneEntity->addComponent( coneMaterial );
858 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
859 QMatrix4x4 transformMatrixCone;
860 transformMatrixCone.rotate( rotation );
861 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
862 coneTransform->setMatrix( transformMatrixCone );
863 coneEntity->addComponent( coneTransform );
866 text->setColor( QColor( 192, 192, 192, 192 ) );
867 text->addComponent( textTransform );
873 onAxisViewportSizeUpdate();
876void Qgs3DAxis::onAxisViewportSizeUpdate()
885 updateAxisLabelPosition();
892 if ( !mAxisRoot || !mCubeRoot )
897 if ( scaleFactor > 0.0 )
901 setEnableAxis(
true );
903 setEnableCube(
true );
905 mAxisScaleFactor = scaleFactor;
906 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
910 setEnableCube(
false );
911 setEnableAxis(
false );
915void Qgs3DAxis::onCameraUpdate()
917 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
919 if ( parentCamera->viewVector() != mPreviousVector && !std::isnan( parentCamera->viewVector().x() ) && !std::isnan( parentCamera->viewVector().y() ) && !std::isnan( parentCamera->viewVector().z() ) )
921 mPreviousVector = parentCamera->viewVector();
923 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
924 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
925 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
927 if ( mAxisRoot->isEnabled() )
929 updateAxisLabelPosition();
934void Qgs3DAxis::updateAxisLabelPosition()
936 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
938 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
939 updateAxisLabelText( mTextX, mTextX->text() );
941 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
942 updateAxisLabelText( mTextY, mTextY->text() );
944 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
945 updateAxisLabelText( mTextZ, mTextZ->text() );
949void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
951 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
952 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
953 textEntity->setFont( font );
954 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
955 textEntity->setHeight( 1.5f * scaledFontSize );
958QFont Qgs3DAxis::createFont(
int pointSize )
960 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
961 font.setPointSize( pointSize );
962 font.setWeight( QFont::Weight::Black );
963 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
Qt3DRender::QLayer * objectLayer() const
Returns main object layer.
void onViewportSizeUpdate(int width=-1, int height=-1)
Updates viewport size. Uses canvas size by default.
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.
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
bool handleEvent(QEvent *event)
Returns if the 3D axis controller will handle the specified event.
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 SIP_SKIP
Returns the abstract 3D engine.
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
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).
Object that controls camera movement based on user input.
void rotateCameraToBottom()
Rotate to bottom-up view.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void rotateCameraToTop()
Rotate to top-down view.
void rotateCameraToEast()
Rotate to view from the east.
void rotateCameraToHome()
Rotate to diagonal view.
void rotateCameraToNorth()
Rotate to view from the north.
void rotateCameraToWest()
Rotate to view from the west.
void cameraChanged()
Emitted when camera has been updated.
void rotateCameraToSouth()
Rotate to view from the south.
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
static const QString sAxiS3DRenderView
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
#define QgsDebugMsgLevel(str, level)
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.