29#include <QActionGroup>
30#include <QApplication>
31#include <QFontDatabase>
35#include <Qt3DCore/QTransform>
36#include <Qt3DExtras/QConeMesh>
37#include <Qt3DExtras/QCylinderMesh>
38#include <Qt3DExtras/QPhongMaterial>
39#include <Qt3DRender/QPointLight>
40#include <Qt3DRender/QSortPolicy>
41#include <Qt3DRender/qcameralens.h>
43#include "moc_qgs3daxis.cpp"
45using namespace Qt::StringLiterals;
52 , mMapScene( mapScene )
53 , mCameraController( cameraCtrl )
56 mMapScene->engine()->frameGraph()->registerRenderView( std::make_unique<Qgs3DAxisRenderView>(
58 mCanvas, mCameraController, mMapSettings,
64 Q_ASSERT( mRenderView );
65 constructAxisScene( parent3DScene );
66 constructLabelsScene( parent3DScene );
68 mTwoDLabelSceneEntity->addComponent( mRenderView->labelLayer() );
73 onAxisViewportSizeUpdate();
75 init3DObjectPicking();
86 switch ( mMapSettings->get3DAxisSettings().mode() )
107void Qgs3DAxis::init3DObjectPicking()
115 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
116 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
117 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
118 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
120 mAxisSceneEntity->addComponent( mScreenRayCaster );
122 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
128 if ( event->type() == QEvent::MouseButtonPress )
132 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
133 mLastClickedPos = mouseEvent->pos();
137 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
139 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
142 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
148 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
155 else if ( !mIsDragging )
158 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
162 std::ostringstream os;
163 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->viewport()->normalizedRect();
167 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
169 mLastClickedButton = mouseEvent->button();
170 mLastClickedPos = mouseEvent->pos();
173 mScreenRayCaster->trigger( mLastClickedPos );
179 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
181 mCanvas->setCursor( mPreviousCursor );
182 mPreviousCursor = Qt::ArrowCursor;
186 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
187 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
189 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
202void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
204 int hitFoundIdx = -1;
209 std::ostringstream os;
210 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
211 for (
int i = 0; i < hits.length(); ++i )
214 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
215 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
216 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
217 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
222 for (
int i = 0; i < hits.length() && hitFoundIdx == -1; ++i )
224 Qt3DCore::QEntity *hitEntity = hits.at( i ).entity();
227 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
229 hitEntity = hitEntity->parentEntity();
231 if ( hits.at( i ).distance() < 500.0f && hitEntity && ( hitEntity == mCubeRoot || hitEntity == mAxisRoot || hitEntity->parent() == mCubeRoot || hitEntity->parent() == mAxisRoot ) )
238 if ( mLastClickedButton == Qt::NoButton )
240 if ( hitFoundIdx != -1 )
242 if ( mCanvas->cursor() != Qt::ArrowCursor )
244 mPreviousCursor = mCanvas->cursor();
245 mCanvas->setCursor( Qt::ArrowCursor );
249 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
251 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
257 else if ( mLastClickedButton == Qt::MouseButton::RightButton && hitFoundIdx != -1 )
259 displayMenuAt( mLastClickedPos );
261 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
265 if ( hitFoundIdx != -1 )
267 Qt3DCore::QEntity *hitEntity = hits.at( hitFoundIdx ).entity();
268 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
270 hitEntity = hitEntity->parentEntity();
272 if ( hitEntity && ( hitEntity == mCubeRoot || hitEntity->parent() == mCubeRoot ) )
274 switch ( hits.at( hitFoundIdx ).primitiveIndex() / 2 )
278 mCameraController->rotateCameraToEast();
283 mCameraController->rotateCameraToWest();
288 mCameraController->rotateCameraToNorth();
293 mCameraController->rotateCameraToSouth();
298 mCameraController->rotateCameraToTop();
303 mCameraController->rotateCameraToBottom();
314void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
316 mAxisSceneEntity =
new Qt3DCore::QEntity;
317 mAxisSceneEntity->setParent( parent3DScene );
318 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
320 mAxisCamera = mRenderView->objectCamera();
321 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
322 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
326void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
328 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
329 mTwoDLabelSceneEntity->setParent( parent3DScene );
330 mTwoDLabelSceneEntity->setEnabled(
true );
332 mTwoDLabelCamera = mRenderView->labelCamera();
333 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
334 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
335 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
340 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
341 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
342 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
344 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
345 destPos.setZ( 0.0f );
349void Qgs3DAxis::setEnableCube(
bool show )
351 mCubeRoot->setEnabled( show );
354 mCubeRoot->setParent( mAxisSceneEntity );
358 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
362void Qgs3DAxis::setEnableAxis(
bool show )
364 mAxisRoot->setEnabled( show );
367 mAxisRoot->setParent( mAxisSceneEntity );
371 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
374 mTextX->setEnabled( show );
375 mTextY->setEnabled( show );
376 mTextZ->setEnabled( show );
379void Qgs3DAxis::createAxisScene()
381 if ( !mAxisRoot || !mCubeRoot )
383 mAxisRoot =
new Qt3DCore::QEntity;
384 mAxisRoot->setParent( mAxisSceneEntity );
385 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
386 mAxisRoot->addComponent( mRenderView->objectLayer() );
388 createAxis( Qt::Axis::XAxis );
389 createAxis( Qt::Axis::YAxis );
390 createAxis( Qt::Axis::ZAxis );
392 mCubeRoot =
new Qt3DCore::QEntity;
393 mCubeRoot->setParent( mAxisSceneEntity );
394 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
395 mCubeRoot->addComponent( mRenderView->objectLayer() );
404 mAxisSceneEntity->setEnabled(
false );
405 setEnableAxis(
false );
406 setEnableCube(
false );
407 mRenderView->setEnabled(
false );
411 mRenderView->setEnabled(
true );
412 mAxisSceneEntity->setEnabled(
true );
415 setEnableCube(
false );
416 setEnableAxis(
true );
418 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
420 if ( axisDirections.length() > 0 )
423 mTextX->setText(
"X?" );
425 if ( axisDirections.length() > 1 )
428 mTextY->setText(
"Y?" );
430 if ( axisDirections.length() > 2 )
433 mTextZ->setText( u
"up"_s );
437 setEnableCube(
true );
438 setEnableAxis(
false );
442 setEnableCube(
false );
443 setEnableAxis(
true );
444 mTextX->setText(
"X?" );
445 mTextY->setText(
"Y?" );
446 mTextZ->setText(
"Z?" );
449 updateAxisLabelPosition();
453void Qgs3DAxis::createMenu()
458 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
459 typeOffAct->setCheckable(
true );
460 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
463 typeOffAct->setChecked(
true );
466 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
467 typeCrsAct->setCheckable(
true );
468 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
471 typeCrsAct->setChecked(
true );
474 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
475 typeCubeAct->setCheckable(
true );
476 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
479 typeCubeAct->setChecked(
true );
482 QActionGroup *typeGroup =
new QActionGroup( mMenu );
483 typeGroup->addAction( typeOffAct );
484 typeGroup->addAction( typeCrsAct );
485 typeGroup->addAction( typeCubeAct );
491 QMenu *typeMenu =
new QMenu( u
"Axis Type"_s, mMenu );
492 Q_ASSERT( typeMenu );
493 typeMenu->addAction( typeOffAct );
494 typeMenu->addAction( typeCrsAct );
495 typeMenu->addAction( typeCubeAct );
496 mMenu->addMenu( typeMenu );
499 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
500 hPosLeftAct->setCheckable(
true );
502 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
503 hPosLeftAct->setChecked(
true );
506 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
507 hPosMiddleAct->setCheckable(
true );
509 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
510 hPosMiddleAct->setChecked(
true );
513 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
514 hPosRightAct->setCheckable(
true );
516 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
517 hPosRightAct->setChecked(
true );
520 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
521 hPosGroup->addAction( hPosLeftAct );
522 hPosGroup->addAction( hPosMiddleAct );
523 hPosGroup->addAction( hPosRightAct );
525 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
526 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
527 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
529 QMenu *horizPosMenu =
new QMenu( u
"Horizontal Position"_s, mMenu );
530 horizPosMenu->addAction( hPosLeftAct );
531 horizPosMenu->addAction( hPosMiddleAct );
532 horizPosMenu->addAction( hPosRightAct );
533 mMenu->addMenu( horizPosMenu );
536 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
537 vPosTopAct->setCheckable(
true );
539 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
540 vPosTopAct->setChecked(
true );
543 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
544 vPosMiddleAct->setCheckable(
true );
546 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
547 vPosMiddleAct->setChecked(
true );
550 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
551 vPosBottomAct->setCheckable(
true );
553 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
554 vPosBottomAct->setChecked(
true );
557 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
558 vPosGroup->addAction( vPosTopAct );
559 vPosGroup->addAction( vPosMiddleAct );
560 vPosGroup->addAction( vPosBottomAct );
562 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
563 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
564 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
566 QMenu *vertPosMenu =
new QMenu( u
"Vertical Position"_s, mMenu );
567 vertPosMenu->addAction( vPosTopAct );
568 vertPosMenu->addAction( vPosMiddleAct );
569 vertPosMenu->addAction( vPosBottomAct );
570 mMenu->addMenu( vertPosMenu );
574 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu );
575 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu );
576 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
577 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
578 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
579 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
580 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu );
590 QMenu *viewMenu =
new QMenu( u
"Camera View"_s, mMenu );
591 viewMenu->addAction( viewHomeAct );
592 viewMenu->addAction( viewTopAct );
593 viewMenu->addAction( viewNorthAct );
594 viewMenu->addAction( viewEastAct );
595 viewMenu->addAction( viewSouthAct );
596 viewMenu->addAction( viewWestAct );
597 viewMenu->addAction( viewBottomAct );
598 mMenu->addMenu( viewMenu );
601 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(),
true );
604void Qgs3DAxis::hideMenu()
606 if ( mMenu && mMenu->isVisible() )
610void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
617 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
622 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
624 mMapSettings->set3DAxisSettings( s );
627void Qgs3DAxis::createCube()
629 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
632 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
633 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
634 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
635 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
637 cubeLineEntity->addComponent( cubeLine );
639 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
640 cubeLineMaterial->setAmbient( Qt::white );
641 cubeLineEntity->addComponent( cubeLineMaterial );
644 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
645 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
646 cubeMesh->setXExtent( mCylinderLength );
647 cubeMesh->setYExtent( mCylinderLength );
648 cubeMesh->setZExtent( mCylinderLength );
649 mCubeRoot->addComponent( cubeMesh );
651 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
652 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
653 cubeMaterial->setShininess( 100 );
654 mCubeRoot->addComponent( cubeMaterial );
656 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
657 QMatrix4x4 transformMatrixcube;
659 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
660 cubeTransform->setMatrix( transformMatrixcube );
661 mCubeRoot->addComponent( cubeTransform );
665 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
666 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
668 const QFont font = createFont( fontSize );
672 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
673 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
675 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
680 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
681 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
683 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
684 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
689 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
690 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
692 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
693 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.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 * 1.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 * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
712 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
713 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
718 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
719 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
721 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
722 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
723 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
726 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
728 l->setParent( mCubeRoot );
732Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
734 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
735 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
736 textEntity->setFont( font );
737 textEntity->setHeight( textHeight );
738 textEntity->setWidth( textWidth );
739 textEntity->setColor( QColor( 192, 192, 192 ) );
740 textEntity->setText( text );
742 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
743 textFrontTransform->setMatrix( rotation );
744 textFrontTransform->setTranslation( translation );
745 textEntity->addComponent( textFrontTransform );
750void Qgs3DAxis::createAxis( Qt::Axis axisType )
752 float cylinderRadius = 0.05f * mCylinderLength;
753 float coneLength = 0.3f * mCylinderLength;
754 float coneBottomRadius = 0.1f * mCylinderLength;
756 QQuaternion rotation;
759 Qt3DExtras::QText2DEntity *text =
nullptr;
760 Qt3DCore::QTransform *textTransform =
nullptr;
765 case Qt::Axis::XAxis:
766 mTextX =
new Qt3DExtras::QText2DEntity();
767 mTextX->setParent( mTwoDLabelSceneEntity );
768 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
769 updateAxisLabelText( mTextX, text );
771 mTextTransformX =
new Qt3DCore::QTransform();
772 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
774 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
777 textTransform = mTextTransformX;
778 name =
"3DAxis_axisX";
781 case Qt::Axis::YAxis:
782 mTextY =
new Qt3DExtras::QText2DEntity();
783 mTextY->setParent( mTwoDLabelSceneEntity );
784 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
785 updateAxisLabelText( mTextY, text );
787 mTextTransformY =
new Qt3DCore::QTransform();
788 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
794 textTransform = mTextTransformY;
795 name =
"3DAxis_axisY";
798 case Qt::Axis::ZAxis:
799 mTextZ =
new Qt3DExtras::QText2DEntity();
800 mTextZ->setParent( mTwoDLabelSceneEntity );
801 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
802 updateAxisLabelText( mTextZ, text );
804 mTextTransformZ =
new Qt3DCore::QTransform();
805 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
807 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
810 textTransform = mTextTransformZ;
811 name =
"3DAxis_axisZ";
819 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
820 cylinder->setObjectName( name );
822 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
823 cylinderMesh->setRadius( cylinderRadius );
824 cylinderMesh->setLength( mCylinderLength );
825 cylinderMesh->setRings( 10 );
826 cylinderMesh->setSlices( 4 );
827 cylinder->addComponent( cylinderMesh );
829 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
830 cylinderMaterial->setAmbient( color );
831 cylinderMaterial->setShininess( 0 );
832 cylinder->addComponent( cylinderMaterial );
834 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
835 QMatrix4x4 transformMatrixCylinder;
836 transformMatrixCylinder.rotate( rotation );
837 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
838 cylinderTransform->setMatrix( transformMatrixCylinder );
839 cylinder->addComponent( cylinderTransform );
842 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
843 coneEntity->setObjectName( name );
844 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
845 coneMesh->setLength( coneLength );
846 coneMesh->setBottomRadius( coneBottomRadius );
847 coneMesh->setTopRadius( 0.0f );
848 coneMesh->setRings( 10 );
849 coneMesh->setSlices( 4 );
850 coneEntity->addComponent( coneMesh );
852 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
853 coneMaterial->setAmbient( color );
854 coneMaterial->setShininess( 0 );
855 coneEntity->addComponent( coneMaterial );
857 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
858 QMatrix4x4 transformMatrixCone;
859 transformMatrixCone.rotate( rotation );
860 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
861 coneTransform->setMatrix( transformMatrixCone );
862 coneEntity->addComponent( coneTransform );
865 text->setColor( QColor( 192, 192, 192, 192 ) );
866 text->addComponent( textTransform );
872 onAxisViewportSizeUpdate();
875void Qgs3DAxis::onAxisViewportSizeUpdate()
884 updateAxisLabelPosition();
891 if ( !mAxisRoot || !mCubeRoot )
896 if ( scaleFactor > 0.0 )
900 setEnableAxis(
true );
902 setEnableCube(
true );
904 mAxisScaleFactor = scaleFactor;
905 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
909 setEnableCube(
false );
910 setEnableAxis(
false );
914void Qgs3DAxis::onCameraUpdate()
916 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
918 if ( parentCamera->viewVector() != mPreviousVector
919 && !std::isnan( parentCamera->viewVector().x() )
920 && !std::isnan( parentCamera->viewVector().y() )
921 && !std::isnan( parentCamera->viewVector().z() ) )
923 mPreviousVector = parentCamera->viewVector();
925 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
926 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
927 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
929 if ( mAxisRoot->isEnabled() )
931 updateAxisLabelPosition();
936void Qgs3DAxis::updateAxisLabelPosition()
938 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
940 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
941 updateAxisLabelText( mTextX, mTextX->text() );
943 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
944 updateAxisLabelText( mTextY, mTextY->text() );
946 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
947 updateAxisLabelText( mTextZ, mTextZ->text() );
951void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
953 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
954 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
955 textEntity->setFont( font );
956 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
957 textEntity->setHeight( 1.5f * scaledFontSize );
960QFont Qgs3DAxis::createFont(
int pointSize )
962 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
963 font.setPointSize( pointSize );
964 font.setWeight( QFont::Weight::Black );
965 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)