17#include "moc_qgs3daxis.cpp"
19#include <Qt3DCore/QTransform>
20#include <Qt3DExtras/QCylinderMesh>
21#include <Qt3DExtras/QPhongMaterial>
22#include <Qt3DExtras/QConeMesh>
23#include <Qt3DRender/qcameralens.h>
24#include <Qt3DRender/QPointLight>
25#include <Qt3DRender/QSortPolicy>
28#include <QFontDatabase>
30#include <QApplication>
31#include <QActionGroup>
48 , mMapScene( mapScene )
49 , mCameraController( cameraCtrl )
54 mCanvas, mCameraController, mMapSettings,
60 Q_ASSERT( mRenderView );
61 constructAxisScene( parent3DScene );
62 constructLabelsScene( parent3DScene );
64 mTwoDLabelSceneEntity->addComponent( mRenderView->
labelLayer() );
69 onAxisViewportSizeUpdate();
71 init3DObjectPicking();
103void Qgs3DAxis::init3DObjectPicking()
111 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
112 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
113 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
114 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
116 mAxisSceneEntity->addComponent( mScreenRayCaster );
118 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
124 if ( event->type() == QEvent::MouseButtonPress )
128 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
129 mLastClickedPos = mouseEvent->pos();
133 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
135 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
138 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
144 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
151 else if ( !mIsDragging )
154 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
158 std::ostringstream os;
159 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->
viewport()->normalizedRect();
163 if ( mRenderView->
viewport()->normalizedRect().contains( normalizedPos ) )
165 mLastClickedButton = mouseEvent->button();
166 mLastClickedPos = mouseEvent->pos();
169 mScreenRayCaster->trigger( mLastClickedPos );
175 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
177 mCanvas->setCursor( mPreviousCursor );
178 mPreviousCursor = Qt::ArrowCursor;
182 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
183 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
185 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
198void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
205 std::ostringstream os;
206 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
207 for (
int i = 0; i < hits.length(); ++i )
210 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
211 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
212 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
213 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
218 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
220 if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
227 if ( mLastClickedButton == Qt::NoButton )
229 if ( mHitsFound != -1 )
231 if ( mCanvas->cursor() != Qt::ArrowCursor )
233 mPreviousCursor = mCanvas->cursor();
234 mCanvas->setCursor( Qt::ArrowCursor );
238 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
240 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
246 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
248 displayMenuAt( mLastClickedPos );
250 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
254 if ( mHitsFound != -1 )
256 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
258 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
298void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
300 mAxisSceneEntity =
new Qt3DCore::QEntity;
301 mAxisSceneEntity->setParent( parent3DScene );
302 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
305 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
306 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
310void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
312 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
313 mTwoDLabelSceneEntity->setParent( parent3DScene );
314 mTwoDLabelSceneEntity->setEnabled(
true );
317 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
318 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
319 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
324 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
325 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
326 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
328 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
329 destPos.setZ( 0.0f );
333void Qgs3DAxis::setEnableCube(
bool show )
335 mCubeRoot->setEnabled( show );
338 mCubeRoot->setParent( mAxisSceneEntity );
342 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
346void Qgs3DAxis::setEnableAxis(
bool show )
348 mAxisRoot->setEnabled( show );
351 mAxisRoot->setParent( mAxisSceneEntity );
355 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
358 mTextX->setEnabled( show );
359 mTextY->setEnabled( show );
360 mTextZ->setEnabled( show );
363void Qgs3DAxis::createAxisScene()
365 if ( !mAxisRoot || !mCubeRoot )
367 mAxisRoot =
new Qt3DCore::QEntity;
368 mAxisRoot->setParent( mAxisSceneEntity );
369 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
370 mAxisRoot->addComponent( mRenderView->
objectLayer() );
372 createAxis( Qt::Axis::XAxis );
373 createAxis( Qt::Axis::YAxis );
374 createAxis( Qt::Axis::ZAxis );
376 mCubeRoot =
new Qt3DCore::QEntity;
377 mCubeRoot->setParent( mAxisSceneEntity );
378 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
379 mCubeRoot->addComponent( mRenderView->
objectLayer() );
388 mAxisSceneEntity->setEnabled(
false );
389 setEnableAxis(
false );
390 setEnableCube(
false );
396 mAxisSceneEntity->setEnabled(
true );
399 setEnableCube(
false );
400 setEnableAxis(
true );
402 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.
axisOrdering();
404 if ( axisDirections.length() > 0 )
407 mTextX->setText(
"X?" );
409 if ( axisDirections.length() > 1 )
412 mTextY->setText(
"Y?" );
414 if ( axisDirections.length() > 2 )
417 mTextZ->setText( QStringLiteral(
"up" ) );
421 setEnableCube(
true );
422 setEnableAxis(
false );
426 setEnableCube(
false );
427 setEnableAxis(
true );
428 mTextX->setText(
"X?" );
429 mTextY->setText(
"Y?" );
430 mTextZ->setText(
"Z?" );
433 updateAxisLabelPosition();
437void Qgs3DAxis::createMenu()
442 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
443 typeOffAct->setCheckable(
true );
444 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
447 typeOffAct->setChecked(
true );
450 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
451 typeCrsAct->setCheckable(
true );
452 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
455 typeCrsAct->setChecked(
true );
458 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
459 typeCubeAct->setCheckable(
true );
460 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
463 typeCubeAct->setChecked(
true );
466 QActionGroup *typeGroup =
new QActionGroup( mMenu );
467 typeGroup->addAction( typeOffAct );
468 typeGroup->addAction( typeCrsAct );
469 typeGroup->addAction( typeCubeAct );
475 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
476 Q_ASSERT( typeMenu );
477 typeMenu->addAction( typeOffAct );
478 typeMenu->addAction( typeCrsAct );
479 typeMenu->addAction( typeCubeAct );
480 mMenu->addMenu( typeMenu );
483 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
484 hPosLeftAct->setCheckable(
true );
487 hPosLeftAct->setChecked(
true );
490 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
491 hPosMiddleAct->setCheckable(
true );
494 hPosMiddleAct->setChecked(
true );
497 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
498 hPosRightAct->setCheckable(
true );
501 hPosRightAct->setChecked(
true );
504 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
505 hPosGroup->addAction( hPosLeftAct );
506 hPosGroup->addAction( hPosMiddleAct );
507 hPosGroup->addAction( hPosRightAct );
509 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
510 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
511 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
513 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
514 horizPosMenu->addAction( hPosLeftAct );
515 horizPosMenu->addAction( hPosMiddleAct );
516 horizPosMenu->addAction( hPosRightAct );
517 mMenu->addMenu( horizPosMenu );
520 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
521 vPosTopAct->setCheckable(
true );
524 vPosTopAct->setChecked(
true );
527 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
528 vPosMiddleAct->setCheckable(
true );
531 vPosMiddleAct->setChecked(
true );
534 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
535 vPosBottomAct->setCheckable(
true );
538 vPosBottomAct->setChecked(
true );
541 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
542 vPosGroup->addAction( vPosTopAct );
543 vPosGroup->addAction( vPosMiddleAct );
544 vPosGroup->addAction( vPosBottomAct );
546 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
547 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
548 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
550 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
551 vertPosMenu->addAction( vPosTopAct );
552 vertPosMenu->addAction( vPosMiddleAct );
553 vertPosMenu->addAction( vPosBottomAct );
554 mMenu->addMenu( vertPosMenu );
558 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu );
559 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu );
560 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
561 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
562 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
563 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
564 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu );
574 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
575 viewMenu->addAction( viewHomeAct );
576 viewMenu->addAction( viewTopAct );
577 viewMenu->addAction( viewNorthAct );
578 viewMenu->addAction( viewEastAct );
579 viewMenu->addAction( viewSouthAct );
580 viewMenu->addAction( viewWestAct );
581 viewMenu->addAction( viewBottomAct );
582 mMenu->addMenu( viewMenu );
588void Qgs3DAxis::hideMenu()
590 if ( mMenu && mMenu->isVisible() )
594void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
601 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
611void Qgs3DAxis::createCube()
613 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
616 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
617 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
618 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
619 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
621 cubeLineEntity->addComponent( cubeLine );
623 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
624 cubeLineMaterial->setAmbient( Qt::white );
625 cubeLineEntity->addComponent( cubeLineMaterial );
628 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
629 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
630 cubeMesh->setXExtent( mCylinderLength );
631 cubeMesh->setYExtent( mCylinderLength );
632 cubeMesh->setZExtent( mCylinderLength );
633 mCubeRoot->addComponent( cubeMesh );
635 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
636 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
637 cubeMaterial->setShininess( 100 );
638 mCubeRoot->addComponent( cubeMaterial );
640 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
641 QMatrix4x4 transformMatrixcube;
643 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
644 cubeTransform->setMatrix( transformMatrixcube );
645 mCubeRoot->addComponent( cubeTransform );
649 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
650 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
652 const QFont font = createFont( fontSize );
655 text = QStringLiteral(
"top" );
656 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
657 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
659 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
663 text = QStringLiteral(
"btm" );
664 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
665 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
667 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
668 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
672 text = QStringLiteral(
"west" );
673 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
674 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
676 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
677 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
678 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
682 text = QStringLiteral(
"east" );
683 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
684 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
686 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
687 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
688 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
692 text = QStringLiteral(
"south" );
693 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
694 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
696 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
697 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
701 text = QStringLiteral(
"north" );
702 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
703 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
705 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
706 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
707 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
710 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
712 l->setParent( mCubeRoot );
716Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
718 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
719 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
720 textEntity->setFont( font );
721 textEntity->setHeight( textHeight );
722 textEntity->setWidth( textWidth );
723 textEntity->setColor( QColor( 192, 192, 192 ) );
724 textEntity->setText( text );
726 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
727 textFrontTransform->setMatrix( rotation );
728 textFrontTransform->setTranslation( translation );
729 textEntity->addComponent( textFrontTransform );
734void Qgs3DAxis::createAxis( Qt::Axis axisType )
736 float cylinderRadius = 0.05f * mCylinderLength;
737 float coneLength = 0.3f * mCylinderLength;
738 float coneBottomRadius = 0.1f * mCylinderLength;
740 QQuaternion rotation;
743 Qt3DExtras::QText2DEntity *text =
nullptr;
744 Qt3DCore::QTransform *textTransform =
nullptr;
749 case Qt::Axis::XAxis:
750 mTextX =
new Qt3DExtras::QText2DEntity();
751 mTextX->setParent( mTwoDLabelSceneEntity );
752 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
753 updateAxisLabelText( mTextX, text );
755 mTextTransformX =
new Qt3DCore::QTransform();
756 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
758 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
761 textTransform = mTextTransformX;
762 name =
"3DAxis_axisX";
765 case Qt::Axis::YAxis:
766 mTextY =
new Qt3DExtras::QText2DEntity();
767 mTextY->setParent( mTwoDLabelSceneEntity );
768 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
769 updateAxisLabelText( mTextY, text );
771 mTextTransformY =
new Qt3DCore::QTransform();
772 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
778 textTransform = mTextTransformY;
779 name =
"3DAxis_axisY";
782 case Qt::Axis::ZAxis:
783 mTextZ =
new Qt3DExtras::QText2DEntity();
784 mTextZ->setParent( mTwoDLabelSceneEntity );
785 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
786 updateAxisLabelText( mTextZ, text );
788 mTextTransformZ =
new Qt3DCore::QTransform();
789 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
791 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
794 textTransform = mTextTransformZ;
795 name =
"3DAxis_axisZ";
803 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
804 cylinder->setObjectName( name );
806 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
807 cylinderMesh->setRadius( cylinderRadius );
808 cylinderMesh->setLength( mCylinderLength );
809 cylinderMesh->setRings( 10 );
810 cylinderMesh->setSlices( 4 );
811 cylinder->addComponent( cylinderMesh );
813 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
814 cylinderMaterial->setAmbient( color );
815 cylinderMaterial->setShininess( 0 );
816 cylinder->addComponent( cylinderMaterial );
818 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
819 QMatrix4x4 transformMatrixCylinder;
820 transformMatrixCylinder.rotate( rotation );
821 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
822 cylinderTransform->setMatrix( transformMatrixCylinder );
823 cylinder->addComponent( cylinderTransform );
826 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
827 coneEntity->setObjectName( name );
828 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
829 coneMesh->setLength( coneLength );
830 coneMesh->setBottomRadius( coneBottomRadius );
831 coneMesh->setTopRadius( 0.0f );
832 coneMesh->setRings( 10 );
833 coneMesh->setSlices( 4 );
834 coneEntity->addComponent( coneMesh );
836 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
837 coneMaterial->setAmbient( color );
838 coneMaterial->setShininess( 0 );
839 coneEntity->addComponent( coneMaterial );
841 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
842 QMatrix4x4 transformMatrixCone;
843 transformMatrixCone.rotate( rotation );
844 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
845 coneTransform->setMatrix( transformMatrixCone );
846 coneEntity->addComponent( coneTransform );
849 text->setColor( QColor( 192, 192, 192, 192 ) );
850 text->addComponent( textTransform );
856 onAxisViewportSizeUpdate();
859void Qgs3DAxis::onAxisViewportSizeUpdate()
868 updateAxisLabelPosition();
875 if ( !mAxisRoot || !mCubeRoot )
880 if ( scaleFactor > 0.0 )
884 setEnableAxis(
true );
886 setEnableCube(
true );
888 mAxisScaleFactor = scaleFactor;
889 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
893 setEnableCube(
false );
894 setEnableAxis(
false );
898void Qgs3DAxis::onCameraUpdate()
900 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
902 if ( parentCamera->viewVector() != mPreviousVector
903 && !std::isnan( parentCamera->viewVector().x() )
904 && !std::isnan( parentCamera->viewVector().y() )
905 && !std::isnan( parentCamera->viewVector().z() ) )
907 mPreviousVector = parentCamera->viewVector();
909 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
910 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
911 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
913 if ( mAxisRoot->isEnabled() )
915 updateAxisLabelPosition();
920void Qgs3DAxis::updateAxisLabelPosition()
922 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
924 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
925 updateAxisLabelText( mTextX, mTextX->text() );
927 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
928 updateAxisLabelText( mTextY, mTextY->text() );
930 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
931 updateAxisLabelText( mTextZ, mTextZ->text() );
935void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
937 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
938 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
939 textEntity->setFont( font );
940 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
941 textEntity->setHeight( 1.5f * scaledFontSize );
944QFont Qgs3DAxis::createFont(
int pointSize )
946 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
947 font.setPointSize( pointSize );
948 font.setWeight( QFont::Weight::Black );
949 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
Qt3DRender::QLayer * labelLayer() const
Returns the layer to be used by entities to be included in the label renderpass.
void onVerticalPositionChanged(Qt::AnchorPoint position)
Updates viewport vertical position.
Qt3DRender::QCamera * labelCamera() const
Returns camera used for billboarded labels.
Qt3DRender::QLayer * objectLayer() const
Returns main object layer.
Qt3DRender::QViewport * viewport() const
Returns the viewport associated to this renderview.
void onViewportSizeUpdate(int width=-1, int height=-1)
Updates viewport size. Uses canvas size by default.
Qt3DRender::QCamera * objectCamera() const
Returns main object camera (used for axis or cube)
void onHorizontalPositionChanged(Qt::AnchorPoint position)
Updates viewport horizontal position.
Contains the configuration of a 3d axis.
void setMode(Qgs3DAxisSettings::Mode type)
Sets the type of the 3daxis.
Mode
Axis representation enum.
@ Crs
Respect CRS directions.
@ Cube
Abstract cube mode.
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
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 set3DAxisSettings(const Qgs3DAxisSettings &axisSettings, bool force=false)
Sets the current configuration of 3d axis.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
Axis-aligned bounding box - in world coords.
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
QgsFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
virtual void setEnabled(bool enable)
Enable or disable via enable the render view sub tree.
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.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
bool registerRenderView(std::unique_ptr< QgsAbstractRenderView > renderView, const QString &name, Qt3DRender::QFrameGraphNode *topNode=nullptr)
Registers a new the render view renderView with name name.
QgsAbstractRenderView * renderView(const QString &name)
Returns the render view named name, if any.
static const QString AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs