29#include <QActionGroup>
30#include <QApplication>
31#include <QFontDatabase>
34#include <Qt3DCore/QTransform>
35#include <Qt3DExtras/QConeMesh>
36#include <Qt3DExtras/QCylinderMesh>
37#include <Qt3DExtras/QPhongMaterial>
38#include <Qt3DRender/QPointLight>
39#include <Qt3DRender/QSortPolicy>
40#include <Qt3DRender/qcameralens.h>
42#include "moc_qgs3daxis.cpp"
49 , mMapScene( mapScene )
50 , mCameraController( cameraCtrl )
53 mMapScene->engine()->frameGraph()->registerRenderView( std::make_unique<Qgs3DAxisRenderView>(
55 mCanvas, mCameraController, mMapSettings,
61 Q_ASSERT( mRenderView );
62 constructAxisScene( parent3DScene );
63 constructLabelsScene( parent3DScene );
65 mTwoDLabelSceneEntity->addComponent( mRenderView->labelLayer() );
70 onAxisViewportSizeUpdate();
72 init3DObjectPicking();
83 switch ( mMapSettings->get3DAxisSettings().mode() )
104void Qgs3DAxis::init3DObjectPicking()
112 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
113 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
114 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
115 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
117 mAxisSceneEntity->addComponent( mScreenRayCaster );
119 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
125 if ( event->type() == QEvent::MouseButtonPress )
129 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
130 mLastClickedPos = mouseEvent->pos();
134 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
136 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
139 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
145 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
152 else if ( !mIsDragging )
155 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
159 std::ostringstream os;
160 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->viewport()->normalizedRect();
164 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
166 mLastClickedButton = mouseEvent->button();
167 mLastClickedPos = mouseEvent->pos();
170 mScreenRayCaster->trigger( mLastClickedPos );
176 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
178 mCanvas->setCursor( mPreviousCursor );
179 mPreviousCursor = Qt::ArrowCursor;
183 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
184 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
186 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
199void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
201 int hitFoundIdx = -1;
206 std::ostringstream os;
207 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
208 for (
int i = 0; i < hits.length(); ++i )
211 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
212 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
213 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
214 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
219 for (
int i = 0; i < hits.length() && hitFoundIdx == -1; ++i )
221 Qt3DCore::QEntity *hitEntity = hits.at( i ).entity();
224 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
226 hitEntity = hitEntity->parentEntity();
228 if ( hits.at( i ).distance() < 500.0f && hitEntity && ( hitEntity == mCubeRoot || hitEntity == mAxisRoot || hitEntity->parent() == mCubeRoot || hitEntity->parent() == mAxisRoot ) )
235 if ( mLastClickedButton == Qt::NoButton )
237 if ( hitFoundIdx != -1 )
239 if ( mCanvas->cursor() != Qt::ArrowCursor )
241 mPreviousCursor = mCanvas->cursor();
242 mCanvas->setCursor( Qt::ArrowCursor );
246 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
248 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
254 else if ( mLastClickedButton == Qt::MouseButton::RightButton && hitFoundIdx != -1 )
256 displayMenuAt( mLastClickedPos );
258 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
262 if ( hitFoundIdx != -1 )
264 Qt3DCore::QEntity *hitEntity = hits.at( hitFoundIdx ).entity();
265 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
267 hitEntity = hitEntity->parentEntity();
269 if ( hitEntity && ( hitEntity == mCubeRoot || hitEntity->parent() == mCubeRoot ) )
271 switch ( hits.at( hitFoundIdx ).primitiveIndex() / 2 )
275 mCameraController->rotateCameraToEast();
280 mCameraController->rotateCameraToWest();
285 mCameraController->rotateCameraToNorth();
290 mCameraController->rotateCameraToSouth();
295 mCameraController->rotateCameraToTop();
300 mCameraController->rotateCameraToBottom();
311void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
313 mAxisSceneEntity =
new Qt3DCore::QEntity;
314 mAxisSceneEntity->setParent( parent3DScene );
315 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
317 mAxisCamera = mRenderView->objectCamera();
318 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
319 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
323void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
325 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
326 mTwoDLabelSceneEntity->setParent( parent3DScene );
327 mTwoDLabelSceneEntity->setEnabled(
true );
329 mTwoDLabelCamera = mRenderView->labelCamera();
330 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
331 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
332 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
337 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
338 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
339 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
341 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
342 destPos.setZ( 0.0f );
346void Qgs3DAxis::setEnableCube(
bool show )
348 mCubeRoot->setEnabled( show );
351 mCubeRoot->setParent( mAxisSceneEntity );
355 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
359void Qgs3DAxis::setEnableAxis(
bool show )
361 mAxisRoot->setEnabled( show );
364 mAxisRoot->setParent( mAxisSceneEntity );
368 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
371 mTextX->setEnabled( show );
372 mTextY->setEnabled( show );
373 mTextZ->setEnabled( show );
376void Qgs3DAxis::createAxisScene()
378 if ( !mAxisRoot || !mCubeRoot )
380 mAxisRoot =
new Qt3DCore::QEntity;
381 mAxisRoot->setParent( mAxisSceneEntity );
382 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
383 mAxisRoot->addComponent( mRenderView->objectLayer() );
385 createAxis( Qt::Axis::XAxis );
386 createAxis( Qt::Axis::YAxis );
387 createAxis( Qt::Axis::ZAxis );
389 mCubeRoot =
new Qt3DCore::QEntity;
390 mCubeRoot->setParent( mAxisSceneEntity );
391 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
392 mCubeRoot->addComponent( mRenderView->objectLayer() );
401 mAxisSceneEntity->setEnabled(
false );
402 setEnableAxis(
false );
403 setEnableCube(
false );
404 mRenderView->setEnabled(
false );
408 mRenderView->setEnabled(
true );
409 mAxisSceneEntity->setEnabled(
true );
412 setEnableCube(
false );
413 setEnableAxis(
true );
415 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
417 if ( axisDirections.length() > 0 )
420 mTextX->setText(
"X?" );
422 if ( axisDirections.length() > 1 )
425 mTextY->setText(
"Y?" );
427 if ( axisDirections.length() > 2 )
430 mTextZ->setText( QStringLiteral(
"up" ) );
434 setEnableCube(
true );
435 setEnableAxis(
false );
439 setEnableCube(
false );
440 setEnableAxis(
true );
441 mTextX->setText(
"X?" );
442 mTextY->setText(
"Y?" );
443 mTextZ->setText(
"Z?" );
446 updateAxisLabelPosition();
450void Qgs3DAxis::createMenu()
455 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
456 typeOffAct->setCheckable(
true );
457 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
460 typeOffAct->setChecked(
true );
463 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
464 typeCrsAct->setCheckable(
true );
465 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
468 typeCrsAct->setChecked(
true );
471 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
472 typeCubeAct->setCheckable(
true );
473 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
476 typeCubeAct->setChecked(
true );
479 QActionGroup *typeGroup =
new QActionGroup( mMenu );
480 typeGroup->addAction( typeOffAct );
481 typeGroup->addAction( typeCrsAct );
482 typeGroup->addAction( typeCubeAct );
488 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
489 Q_ASSERT( typeMenu );
490 typeMenu->addAction( typeOffAct );
491 typeMenu->addAction( typeCrsAct );
492 typeMenu->addAction( typeCubeAct );
493 mMenu->addMenu( typeMenu );
496 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
497 hPosLeftAct->setCheckable(
true );
499 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
500 hPosLeftAct->setChecked(
true );
503 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
504 hPosMiddleAct->setCheckable(
true );
506 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
507 hPosMiddleAct->setChecked(
true );
510 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
511 hPosRightAct->setCheckable(
true );
513 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
514 hPosRightAct->setChecked(
true );
517 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
518 hPosGroup->addAction( hPosLeftAct );
519 hPosGroup->addAction( hPosMiddleAct );
520 hPosGroup->addAction( hPosRightAct );
522 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
523 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
524 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
526 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
527 horizPosMenu->addAction( hPosLeftAct );
528 horizPosMenu->addAction( hPosMiddleAct );
529 horizPosMenu->addAction( hPosRightAct );
530 mMenu->addMenu( horizPosMenu );
533 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
534 vPosTopAct->setCheckable(
true );
536 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
537 vPosTopAct->setChecked(
true );
540 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
541 vPosMiddleAct->setCheckable(
true );
543 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
544 vPosMiddleAct->setChecked(
true );
547 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
548 vPosBottomAct->setCheckable(
true );
550 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
551 vPosBottomAct->setChecked(
true );
554 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
555 vPosGroup->addAction( vPosTopAct );
556 vPosGroup->addAction( vPosMiddleAct );
557 vPosGroup->addAction( vPosBottomAct );
559 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
560 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
561 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
563 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
564 vertPosMenu->addAction( vPosTopAct );
565 vertPosMenu->addAction( vPosMiddleAct );
566 vertPosMenu->addAction( vPosBottomAct );
567 mMenu->addMenu( vertPosMenu );
571 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu );
572 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu );
573 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
574 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
575 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
576 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
577 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu );
587 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
588 viewMenu->addAction( viewHomeAct );
589 viewMenu->addAction( viewTopAct );
590 viewMenu->addAction( viewNorthAct );
591 viewMenu->addAction( viewEastAct );
592 viewMenu->addAction( viewSouthAct );
593 viewMenu->addAction( viewWestAct );
594 viewMenu->addAction( viewBottomAct );
595 mMenu->addMenu( viewMenu );
598 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(),
true );
601void Qgs3DAxis::hideMenu()
603 if ( mMenu && mMenu->isVisible() )
607void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
614 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
619 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
621 mMapSettings->set3DAxisSettings( s );
624void Qgs3DAxis::createCube()
626 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
629 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
630 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
631 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
632 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
634 cubeLineEntity->addComponent( cubeLine );
636 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
637 cubeLineMaterial->setAmbient( Qt::white );
638 cubeLineEntity->addComponent( cubeLineMaterial );
641 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
642 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
643 cubeMesh->setXExtent( mCylinderLength );
644 cubeMesh->setYExtent( mCylinderLength );
645 cubeMesh->setZExtent( mCylinderLength );
646 mCubeRoot->addComponent( cubeMesh );
648 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
649 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
650 cubeMaterial->setShininess( 100 );
651 mCubeRoot->addComponent( cubeMaterial );
653 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
654 QMatrix4x4 transformMatrixcube;
656 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
657 cubeTransform->setMatrix( transformMatrixcube );
658 mCubeRoot->addComponent( cubeTransform );
662 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
663 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
665 const QFont font = createFont( fontSize );
668 text = QStringLiteral(
"top" );
669 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
670 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
672 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
676 text = QStringLiteral(
"btm" );
677 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
678 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
680 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
681 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
685 text = QStringLiteral(
"west" );
686 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
687 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
689 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
690 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
691 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
695 text = QStringLiteral(
"east" );
696 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
697 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.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 );
705 text = QStringLiteral(
"south" );
706 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
707 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
709 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
710 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
714 text = QStringLiteral(
"north" );
715 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
716 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
718 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
719 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
720 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
723 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
725 l->setParent( mCubeRoot );
729Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
731 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
732 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
733 textEntity->setFont( font );
734 textEntity->setHeight( textHeight );
735 textEntity->setWidth( textWidth );
736 textEntity->setColor( QColor( 192, 192, 192 ) );
737 textEntity->setText( text );
739 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
740 textFrontTransform->setMatrix( rotation );
741 textFrontTransform->setTranslation( translation );
742 textEntity->addComponent( textFrontTransform );
747void Qgs3DAxis::createAxis( Qt::Axis axisType )
749 float cylinderRadius = 0.05f * mCylinderLength;
750 float coneLength = 0.3f * mCylinderLength;
751 float coneBottomRadius = 0.1f * mCylinderLength;
753 QQuaternion rotation;
756 Qt3DExtras::QText2DEntity *text =
nullptr;
757 Qt3DCore::QTransform *textTransform =
nullptr;
762 case Qt::Axis::XAxis:
763 mTextX =
new Qt3DExtras::QText2DEntity();
764 mTextX->setParent( mTwoDLabelSceneEntity );
765 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
766 updateAxisLabelText( mTextX, text );
768 mTextTransformX =
new Qt3DCore::QTransform();
769 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
771 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
774 textTransform = mTextTransformX;
775 name =
"3DAxis_axisX";
778 case Qt::Axis::YAxis:
779 mTextY =
new Qt3DExtras::QText2DEntity();
780 mTextY->setParent( mTwoDLabelSceneEntity );
781 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
782 updateAxisLabelText( mTextY, text );
784 mTextTransformY =
new Qt3DCore::QTransform();
785 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
791 textTransform = mTextTransformY;
792 name =
"3DAxis_axisY";
795 case Qt::Axis::ZAxis:
796 mTextZ =
new Qt3DExtras::QText2DEntity();
797 mTextZ->setParent( mTwoDLabelSceneEntity );
798 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
799 updateAxisLabelText( mTextZ, text );
801 mTextTransformZ =
new Qt3DCore::QTransform();
802 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
804 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
807 textTransform = mTextTransformZ;
808 name =
"3DAxis_axisZ";
816 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
817 cylinder->setObjectName( name );
819 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
820 cylinderMesh->setRadius( cylinderRadius );
821 cylinderMesh->setLength( mCylinderLength );
822 cylinderMesh->setRings( 10 );
823 cylinderMesh->setSlices( 4 );
824 cylinder->addComponent( cylinderMesh );
826 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
827 cylinderMaterial->setAmbient( color );
828 cylinderMaterial->setShininess( 0 );
829 cylinder->addComponent( cylinderMaterial );
831 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
832 QMatrix4x4 transformMatrixCylinder;
833 transformMatrixCylinder.rotate( rotation );
834 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
835 cylinderTransform->setMatrix( transformMatrixCylinder );
836 cylinder->addComponent( cylinderTransform );
839 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
840 coneEntity->setObjectName( name );
841 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
842 coneMesh->setLength( coneLength );
843 coneMesh->setBottomRadius( coneBottomRadius );
844 coneMesh->setTopRadius( 0.0f );
845 coneMesh->setRings( 10 );
846 coneMesh->setSlices( 4 );
847 coneEntity->addComponent( coneMesh );
849 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
850 coneMaterial->setAmbient( color );
851 coneMaterial->setShininess( 0 );
852 coneEntity->addComponent( coneMaterial );
854 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
855 QMatrix4x4 transformMatrixCone;
856 transformMatrixCone.rotate( rotation );
857 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
858 coneTransform->setMatrix( transformMatrixCone );
859 coneEntity->addComponent( coneTransform );
862 text->setColor( QColor( 192, 192, 192, 192 ) );
863 text->addComponent( textTransform );
869 onAxisViewportSizeUpdate();
872void Qgs3DAxis::onAxisViewportSizeUpdate()
881 updateAxisLabelPosition();
888 if ( !mAxisRoot || !mCubeRoot )
893 if ( scaleFactor > 0.0 )
897 setEnableAxis(
true );
899 setEnableCube(
true );
901 mAxisScaleFactor = scaleFactor;
902 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
906 setEnableCube(
false );
907 setEnableAxis(
false );
911void Qgs3DAxis::onCameraUpdate()
913 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
915 if ( parentCamera->viewVector() != mPreviousVector
916 && !std::isnan( parentCamera->viewVector().x() )
917 && !std::isnan( parentCamera->viewVector().y() )
918 && !std::isnan( parentCamera->viewVector().z() ) )
920 mPreviousVector = parentCamera->viewVector();
922 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
923 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
924 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
926 if ( mAxisRoot->isEnabled() )
928 updateAxisLabelPosition();
933void Qgs3DAxis::updateAxisLabelPosition()
935 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
937 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
938 updateAxisLabelText( mTextX, mTextX->text() );
940 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
941 updateAxisLabelText( mTextY, mTextY->text() );
943 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
944 updateAxisLabelText( mTextZ, mTextZ->text() );
948void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
950 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
951 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
952 textEntity->setFont( font );
953 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
954 textEntity->setHeight( 1.5f * scaledFontSize );
957QFont Qgs3DAxis::createFont(
int pointSize )
959 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
960 font.setPointSize( pointSize );
961 font.setWeight( QFont::Weight::Black );
962 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
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 AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
#define QgsDebugMsgLevel(str, level)