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();
96 switch ( mMapSettings->get3DAxisSettings().mode() )
117void Qgs3DAxis::init3DObjectPicking()
125 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
126 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
127 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
128 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
130 mAxisSceneEntity->addComponent( mScreenRayCaster );
132 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
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 )
214 int hitFoundIdx = -1;
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() && hitFoundIdx == -1; ++i )
234 Qt3DCore::QEntity *hitEntity = hits.at( i ).entity();
237 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
239 hitEntity = hitEntity->parentEntity();
241 if ( hits.at( i ).distance() < 500.0f && hitEntity && ( hitEntity == mCubeRoot || hitEntity == mAxisRoot || hitEntity->parent() == mCubeRoot || hitEntity->parent() == mAxisRoot ) )
248 if ( mLastClickedButton == Qt::NoButton )
250 if ( hitFoundIdx != -1 )
252 if ( mCanvas->cursor() != Qt::ArrowCursor )
254 mPreviousCursor = mCanvas->cursor();
255 mCanvas->setCursor( Qt::ArrowCursor );
259 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
261 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
267 else if ( mLastClickedButton == Qt::MouseButton::RightButton && hitFoundIdx != -1 )
269 displayMenuAt( mLastClickedPos );
271 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
275 if ( hitFoundIdx != -1 )
277 Qt3DCore::QEntity *hitEntity = hits.at( hitFoundIdx ).entity();
278 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
280 hitEntity = hitEntity->parentEntity();
282 if ( hitEntity && ( hitEntity == mCubeRoot || hitEntity->parent() == mCubeRoot ) )
284 switch ( hits.at( hitFoundIdx ).primitiveIndex() / 2 )
288 mCameraController->rotateCameraToEast();
293 mCameraController->rotateCameraToWest();
298 mCameraController->rotateCameraToNorth();
303 mCameraController->rotateCameraToSouth();
308 mCameraController->rotateCameraToTop();
313 mCameraController->rotateCameraToBottom();
324void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
326 mAxisSceneEntity =
new Qt3DCore::QEntity;
327 mAxisSceneEntity->setParent( parent3DScene );
328 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
330 mAxisCamera = mRenderView->objectCamera();
331 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
332 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
336void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
338 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
339 mTwoDLabelSceneEntity->setParent( parent3DScene );
340 mTwoDLabelSceneEntity->setEnabled(
true );
342 mTwoDLabelCamera = mRenderView->labelCamera();
343 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
344 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
345 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
350 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
351 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
352 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
354 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
355 destPos.setZ( 0.0f );
359void Qgs3DAxis::setEnableCube(
bool show )
361 mCubeRoot->setEnabled( show );
364 mCubeRoot->setParent( mAxisSceneEntity );
368 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
372void Qgs3DAxis::setEnableAxis(
bool show )
374 mAxisRoot->setEnabled( show );
377 mAxisRoot->setParent( mAxisSceneEntity );
381 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
384 mTextX->setEnabled( show );
385 mTextY->setEnabled( show );
386 mTextZ->setEnabled( show );
389void Qgs3DAxis::createAxisScene()
391 if ( !mAxisRoot || !mCubeRoot )
393 mAxisRoot =
new Qt3DCore::QEntity;
394 mAxisRoot->setParent( mAxisSceneEntity );
395 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
396 mAxisRoot->addComponent( mRenderView->objectLayer() );
398 createAxis( Qt::Axis::XAxis );
399 createAxis( Qt::Axis::YAxis );
400 createAxis( Qt::Axis::ZAxis );
402 mCubeRoot =
new Qt3DCore::QEntity;
403 mCubeRoot->setParent( mAxisSceneEntity );
404 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
405 mCubeRoot->addComponent( mRenderView->objectLayer() );
414 mAxisSceneEntity->setEnabled(
false );
415 setEnableAxis(
false );
416 setEnableCube(
false );
417 mRenderView->setEnabled(
false );
421 mRenderView->setEnabled(
true );
422 mAxisSceneEntity->setEnabled(
true );
425 setEnableCube(
false );
426 setEnableAxis(
true );
428 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
430 if ( axisDirections.length() > 0 )
433 mTextX->setText(
"X?" );
435 if ( axisDirections.length() > 1 )
438 mTextY->setText(
"Y?" );
440 if ( axisDirections.length() > 2 )
443 mTextZ->setText( u
"up"_s );
447 setEnableCube(
true );
448 setEnableAxis(
false );
452 setEnableCube(
false );
453 setEnableAxis(
true );
454 mTextX->setText(
"X?" );
455 mTextY->setText(
"Y?" );
456 mTextZ->setText(
"Z?" );
459 updateAxisLabelPosition();
463void Qgs3DAxis::createMenu()
468 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
469 typeOffAct->setCheckable(
true );
470 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
473 typeOffAct->setChecked(
true );
476 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
477 typeCrsAct->setCheckable(
true );
478 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
481 typeCrsAct->setChecked(
true );
484 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
485 typeCubeAct->setCheckable(
true );
486 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
489 typeCubeAct->setChecked(
true );
492 QActionGroup *typeGroup =
new QActionGroup( mMenu );
493 typeGroup->addAction( typeOffAct );
494 typeGroup->addAction( typeCrsAct );
495 typeGroup->addAction( typeCubeAct );
501 QMenu *typeMenu =
new QMenu( u
"Axis Type"_s, mMenu );
502 Q_ASSERT( typeMenu );
503 typeMenu->addAction( typeOffAct );
504 typeMenu->addAction( typeCrsAct );
505 typeMenu->addAction( typeCubeAct );
506 mMenu->addMenu( typeMenu );
509 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
510 hPosLeftAct->setCheckable(
true );
512 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
513 hPosLeftAct->setChecked(
true );
516 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
517 hPosMiddleAct->setCheckable(
true );
519 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
520 hPosMiddleAct->setChecked(
true );
523 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
524 hPosRightAct->setCheckable(
true );
526 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
527 hPosRightAct->setChecked(
true );
530 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
531 hPosGroup->addAction( hPosLeftAct );
532 hPosGroup->addAction( hPosMiddleAct );
533 hPosGroup->addAction( hPosRightAct );
535 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
536 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
537 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
539 QMenu *horizPosMenu =
new QMenu( u
"Horizontal Position"_s, mMenu );
540 horizPosMenu->addAction( hPosLeftAct );
541 horizPosMenu->addAction( hPosMiddleAct );
542 horizPosMenu->addAction( hPosRightAct );
543 mMenu->addMenu( horizPosMenu );
546 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
547 vPosTopAct->setCheckable(
true );
549 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
550 vPosTopAct->setChecked(
true );
553 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
554 vPosMiddleAct->setCheckable(
true );
556 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
557 vPosMiddleAct->setChecked(
true );
560 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
561 vPosBottomAct->setCheckable(
true );
563 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
564 vPosBottomAct->setChecked(
true );
567 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
568 vPosGroup->addAction( vPosTopAct );
569 vPosGroup->addAction( vPosMiddleAct );
570 vPosGroup->addAction( vPosBottomAct );
572 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
573 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
574 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
576 QMenu *vertPosMenu =
new QMenu( u
"Vertical Position"_s, mMenu );
577 vertPosMenu->addAction( vPosTopAct );
578 vertPosMenu->addAction( vPosMiddleAct );
579 vertPosMenu->addAction( vPosBottomAct );
580 mMenu->addMenu( vertPosMenu );
584 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu );
585 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu );
586 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
587 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
588 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
589 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
590 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu );
600 QMenu *viewMenu =
new QMenu( u
"Camera View"_s, mMenu );
601 viewMenu->addAction( viewHomeAct );
602 viewMenu->addAction( viewTopAct );
603 viewMenu->addAction( viewNorthAct );
604 viewMenu->addAction( viewEastAct );
605 viewMenu->addAction( viewSouthAct );
606 viewMenu->addAction( viewWestAct );
607 viewMenu->addAction( viewBottomAct );
608 mMenu->addMenu( viewMenu );
611 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(),
true );
614void Qgs3DAxis::hideMenu()
616 if ( mMenu && mMenu->isVisible() )
620void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
627 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
632 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
634 mMapSettings->set3DAxisSettings( s );
637void Qgs3DAxis::createCube()
639 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
642 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
643 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
644 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
645 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
647 cubeLineEntity->addComponent( cubeLine );
649 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
650 cubeLineMaterial->setAmbient( Qt::white );
651 cubeLineEntity->addComponent( cubeLineMaterial );
654 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
655 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
656 cubeMesh->setXExtent( mCylinderLength );
657 cubeMesh->setYExtent( mCylinderLength );
658 cubeMesh->setZExtent( mCylinderLength );
659 mCubeRoot->addComponent( cubeMesh );
661 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
662 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
663 cubeMaterial->setShininess( 100 );
664 mCubeRoot->addComponent( cubeMaterial );
666 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
667 QMatrix4x4 transformMatrixcube;
669 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
670 cubeTransform->setMatrix( transformMatrixcube );
671 mCubeRoot->addComponent( cubeTransform );
675 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
676 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
678 const QFont font = createFont( fontSize );
682 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
683 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
685 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
690 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
691 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
693 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
694 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
699 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
700 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
702 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
703 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
704 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
709 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
710 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
712 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
713 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
714 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
719 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
720 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
722 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
723 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
728 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
729 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
731 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
732 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
733 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
736 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
738 l->setParent( mCubeRoot );
742Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
744 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
745 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
746 textEntity->setFont( font );
747 textEntity->setHeight( textHeight );
748 textEntity->setWidth( textWidth );
749 textEntity->setColor( QColor( 192, 192, 192 ) );
750 textEntity->setText( text );
752 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
753 textFrontTransform->setMatrix( rotation );
754 textFrontTransform->setTranslation( translation );
755 textEntity->addComponent( textFrontTransform );
760void Qgs3DAxis::createAxis( Qt::Axis axisType )
762 float cylinderRadius = 0.05f * mCylinderLength;
763 float coneLength = 0.3f * mCylinderLength;
764 float coneBottomRadius = 0.1f * mCylinderLength;
766 QQuaternion rotation;
769 Qt3DExtras::QText2DEntity *text =
nullptr;
770 Qt3DCore::QTransform *textTransform =
nullptr;
775 case Qt::Axis::XAxis:
776 mTextX =
new Qt3DExtras::QText2DEntity();
777 mTextX->setParent( mTwoDLabelSceneEntity );
778 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextX, text ); } );
779 mTextTransformX =
new Qt3DCore::QTransform();
780 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
782 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
785 textTransform = mTextTransformX;
786 name =
"3DAxis_axisX";
789 case Qt::Axis::YAxis:
790 mTextY =
new Qt3DExtras::QText2DEntity();
791 mTextY->setParent( mTwoDLabelSceneEntity );
792 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextY, text ); } );
793 mTextTransformY =
new Qt3DCore::QTransform();
794 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
800 textTransform = mTextTransformY;
801 name =
"3DAxis_axisY";
804 case Qt::Axis::ZAxis:
805 mTextZ =
new Qt3DExtras::QText2DEntity();
806 mTextZ->setParent( mTwoDLabelSceneEntity );
807 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) { updateAxisLabelText( mTextZ, text ); } );
808 mTextTransformZ =
new Qt3DCore::QTransform();
809 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
811 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
814 textTransform = mTextTransformZ;
815 name =
"3DAxis_axisZ";
823 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
824 cylinder->setObjectName( name );
826 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
827 cylinderMesh->setRadius( cylinderRadius );
828 cylinderMesh->setLength( mCylinderLength );
829 cylinderMesh->setRings( 10 );
830 cylinderMesh->setSlices( 4 );
831 cylinder->addComponent( cylinderMesh );
833 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
834 cylinderMaterial->setAmbient( color );
835 cylinderMaterial->setShininess( 0 );
836 cylinder->addComponent( cylinderMaterial );
838 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
839 QMatrix4x4 transformMatrixCylinder;
840 transformMatrixCylinder.rotate( rotation );
841 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
842 cylinderTransform->setMatrix( transformMatrixCylinder );
843 cylinder->addComponent( cylinderTransform );
846 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
847 coneEntity->setObjectName( name );
848 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
849 coneMesh->setLength( coneLength );
850 coneMesh->setBottomRadius( coneBottomRadius );
851 coneMesh->setTopRadius( 0.0f );
852 coneMesh->setRings( 10 );
853 coneMesh->setSlices( 4 );
854 coneEntity->addComponent( coneMesh );
856 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
857 coneMaterial->setAmbient( color );
858 coneMaterial->setShininess( 0 );
859 coneEntity->addComponent( coneMaterial );
861 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
862 QMatrix4x4 transformMatrixCone;
863 transformMatrixCone.rotate( rotation );
864 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
865 coneTransform->setMatrix( transformMatrixCone );
866 coneEntity->addComponent( coneTransform );
869 text->setColor( QColor( 192, 192, 192, 192 ) );
870 text->addComponent( textTransform );
876 onAxisViewportSizeUpdate();
879void Qgs3DAxis::onAxisViewportSizeUpdate()
888 updateAxisLabelPosition();
895 if ( !mAxisRoot || !mCubeRoot )
900 if ( scaleFactor > 0.0 )
904 setEnableAxis(
true );
906 setEnableCube(
true );
908 mAxisScaleFactor = scaleFactor;
909 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
913 setEnableCube(
false );
914 setEnableAxis(
false );
918void Qgs3DAxis::onCameraUpdate()
920 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
922 if ( parentCamera->viewVector() != mPreviousVector && !std::isnan( parentCamera->viewVector().x() ) && !std::isnan( parentCamera->viewVector().y() ) && !std::isnan( parentCamera->viewVector().z() ) )
924 mPreviousVector = parentCamera->viewVector();
926 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
927 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
928 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
930 if ( mAxisRoot->isEnabled() )
932 updateAxisLabelPosition();
937void Qgs3DAxis::updateAxisLabelPosition()
939 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
941 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
942 updateAxisLabelText( mTextX, mTextX->text() );
944 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
945 updateAxisLabelText( mTextY, mTextY->text() );
947 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
948 updateAxisLabelText( mTextZ, mTextZ->text() );
952void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
954 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
955 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
956 textEntity->setFont( font );
957 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
958 textEntity->setHeight( 1.5f * scaledFontSize );
961QFont Qgs3DAxis::createFont(
int pointSize )
963 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
964 font.setPointSize( pointSize );
965 font.setWeight( QFont::Weight::Black );
966 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 AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
#define QgsDebugMsgLevel(str, level)