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::ShortcutOverride )
126 return handleKeyEvent(
static_cast<QKeyEvent *
>( event ) );
129 if ( event->type() == QEvent::MouseButtonPress )
133 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
134 mLastClickedPos = mouseEvent->pos();
138 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
140 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
143 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
149 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
156 else if ( !mIsDragging )
159 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
163 std::ostringstream os;
164 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->
viewport()->normalizedRect();
168 if ( mRenderView->
viewport()->normalizedRect().contains( normalizedPos ) )
170 mLastClickedButton = mouseEvent->button();
171 mLastClickedPos = mouseEvent->pos();
174 mScreenRayCaster->trigger( mLastClickedPos );
180 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
182 mCanvas->setCursor( mPreviousCursor );
183 mPreviousCursor = Qt::ArrowCursor;
187 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
188 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
190 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
203void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
210 std::ostringstream os;
211 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
212 for (
int i = 0; i < hits.length(); ++i )
215 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
216 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
217 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
218 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
223 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
225 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 ) )
232 if ( mLastClickedButton == Qt::NoButton )
234 if ( mHitsFound != -1 )
236 if ( mCanvas->cursor() != Qt::ArrowCursor )
238 mPreviousCursor = mCanvas->cursor();
239 mCanvas->setCursor( Qt::ArrowCursor );
243 if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
245 mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
251 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
253 displayMenuAt( mLastClickedPos );
255 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
259 if ( mHitsFound != -1 )
261 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
263 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
267 onCameraViewChangeEast();
272 onCameraViewChangeWest();
277 onCameraViewChangeNorth();
282 onCameraViewChangeSouth();
287 onCameraViewChangeTop();
292 onCameraViewChangeBottom();
303void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
305 mAxisSceneEntity =
new Qt3DCore::QEntity;
306 mAxisSceneEntity->setParent( parent3DScene );
307 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
310 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
311 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
315void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
317 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
318 mTwoDLabelSceneEntity->setParent( parent3DScene );
319 mTwoDLabelSceneEntity->setEnabled(
true );
322 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
323 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
324 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
329 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
330 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
331 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
333 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
334 destPos.setZ( 0.0f );
338void Qgs3DAxis::setEnableCube(
bool show )
340 mCubeRoot->setEnabled( show );
343 mCubeRoot->setParent( mAxisSceneEntity );
347 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
351void Qgs3DAxis::setEnableAxis(
bool show )
353 mAxisRoot->setEnabled( show );
356 mAxisRoot->setParent( mAxisSceneEntity );
360 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
363 mTextX->setEnabled( show );
364 mTextY->setEnabled( show );
365 mTextZ->setEnabled( show );
368void Qgs3DAxis::createAxisScene()
370 if ( !mAxisRoot || !mCubeRoot )
372 mAxisRoot =
new Qt3DCore::QEntity;
373 mAxisRoot->setParent( mAxisSceneEntity );
374 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
375 mAxisRoot->addComponent( mRenderView->
objectLayer() );
377 createAxis( Qt::Axis::XAxis );
378 createAxis( Qt::Axis::YAxis );
379 createAxis( Qt::Axis::ZAxis );
381 mCubeRoot =
new Qt3DCore::QEntity;
382 mCubeRoot->setParent( mAxisSceneEntity );
383 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
384 mCubeRoot->addComponent( mRenderView->
objectLayer() );
393 mAxisSceneEntity->setEnabled(
false );
394 setEnableAxis(
false );
395 setEnableCube(
false );
401 mAxisSceneEntity->setEnabled(
true );
404 setEnableCube(
false );
405 setEnableAxis(
true );
407 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.
axisOrdering();
409 if ( axisDirections.length() > 0 )
412 mTextX->setText(
"X?" );
414 if ( axisDirections.length() > 1 )
417 mTextY->setText(
"Y?" );
419 if ( axisDirections.length() > 2 )
422 mTextZ->setText( QStringLiteral(
"up" ) );
426 setEnableCube(
true );
427 setEnableAxis(
false );
431 setEnableCube(
false );
432 setEnableAxis(
true );
433 mTextX->setText(
"X?" );
434 mTextY->setText(
"Y?" );
435 mTextZ->setText(
"Z?" );
438 updateAxisLabelPosition();
442bool Qgs3DAxis::handleKeyEvent( QKeyEvent *keyEvent )
445 if ( keyEvent->modifiers() | Qt::ControlModifier )
448 switch ( keyEvent->key() )
451 onCameraViewChangeNorth();
455 onCameraViewChangeEast();
459 onCameraViewChangeSouth();
463 onCameraViewChangeWest();
467 onCameraViewChangeTop();
471 onCameraViewChangeBottom();
475 onCameraViewChangeHome();
486void Qgs3DAxis::createMenu()
491 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
492 typeOffAct->setCheckable(
true );
493 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
496 typeOffAct->setChecked(
true );
499 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
500 typeCrsAct->setCheckable(
true );
501 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
504 typeCrsAct->setChecked(
true );
507 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
508 typeCubeAct->setCheckable(
true );
509 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
512 typeCubeAct->setChecked(
true );
515 QActionGroup *typeGroup =
new QActionGroup( mMenu );
516 typeGroup->addAction( typeOffAct );
517 typeGroup->addAction( typeCrsAct );
518 typeGroup->addAction( typeCubeAct );
524 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
525 Q_ASSERT( typeMenu );
526 typeMenu->addAction( typeOffAct );
527 typeMenu->addAction( typeCrsAct );
528 typeMenu->addAction( typeCubeAct );
529 mMenu->addMenu( typeMenu );
532 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
533 hPosLeftAct->setCheckable(
true );
536 hPosLeftAct->setChecked(
true );
539 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
540 hPosMiddleAct->setCheckable(
true );
543 hPosMiddleAct->setChecked(
true );
546 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
547 hPosRightAct->setCheckable(
true );
550 hPosRightAct->setChecked(
true );
553 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
554 hPosGroup->addAction( hPosLeftAct );
555 hPosGroup->addAction( hPosMiddleAct );
556 hPosGroup->addAction( hPosRightAct );
558 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
559 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
560 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
562 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
563 horizPosMenu->addAction( hPosLeftAct );
564 horizPosMenu->addAction( hPosMiddleAct );
565 horizPosMenu->addAction( hPosRightAct );
566 mMenu->addMenu( horizPosMenu );
569 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
570 vPosTopAct->setCheckable(
true );
573 vPosTopAct->setChecked(
true );
576 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
577 vPosMiddleAct->setCheckable(
true );
580 vPosMiddleAct->setChecked(
true );
583 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
584 vPosBottomAct->setCheckable(
true );
587 vPosBottomAct->setChecked(
true );
590 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
591 vPosGroup->addAction( vPosTopAct );
592 vPosGroup->addAction( vPosMiddleAct );
593 vPosGroup->addAction( vPosBottomAct );
595 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
596 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
597 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
599 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
600 vertPosMenu->addAction( vPosTopAct );
601 vertPosMenu->addAction( vPosMiddleAct );
602 vertPosMenu->addAction( vPosBottomAct );
603 mMenu->addMenu( vertPosMenu );
606 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+1", mMenu );
607 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+5", mMenu );
608 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
609 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
610 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
611 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
612 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
614 connect( viewHomeAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeHome );
615 connect( viewTopAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeTop );
616 connect( viewNorthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeNorth );
617 connect( viewEastAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeEast );
618 connect( viewSouthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeSouth );
619 connect( viewWestAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeWest );
620 connect( viewBottomAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeBottom );
622 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
623 viewMenu->addAction( viewHomeAct );
624 viewMenu->addAction( viewTopAct );
625 viewMenu->addAction( viewNorthAct );
626 viewMenu->addAction( viewEastAct );
627 viewMenu->addAction( viewSouthAct );
628 viewMenu->addAction( viewWestAct );
629 viewMenu->addAction( viewBottomAct );
630 mMenu->addMenu( viewMenu );
636void Qgs3DAxis::hideMenu()
638 if ( mMenu && mMenu->isVisible() )
642void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
649 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
659void Qgs3DAxis::onCameraViewChange(
float pitch,
float yaw )
662 double elevation = 0.0;
666 QVector3D camPos = mCameraController->
camera()->position();
667 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCameraController->
camera()->farPlane() );
669 if ( !hits.isEmpty() )
671 elevation = hits.at( 0 ).pos.z();
672 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
685void Qgs3DAxis::createCube()
687 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
690 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
691 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
692 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
693 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
695 cubeLineEntity->addComponent( cubeLine );
697 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
698 cubeLineMaterial->setAmbient( Qt::white );
699 cubeLineEntity->addComponent( cubeLineMaterial );
702 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
703 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
704 cubeMesh->setXExtent( mCylinderLength );
705 cubeMesh->setYExtent( mCylinderLength );
706 cubeMesh->setZExtent( mCylinderLength );
707 mCubeRoot->addComponent( cubeMesh );
709 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
710 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
711 cubeMaterial->setShininess( 100 );
712 mCubeRoot->addComponent( cubeMaterial );
714 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
715 QMatrix4x4 transformMatrixcube;
717 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
718 cubeTransform->setMatrix( transformMatrixcube );
719 mCubeRoot->addComponent( cubeTransform );
723 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
724 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
726 const QFont font = createFont( fontSize );
729 text = QStringLiteral(
"top" );
730 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
731 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
733 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
737 text = QStringLiteral(
"btm" );
738 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
739 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
741 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
742 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
746 text = QStringLiteral(
"west" );
747 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
748 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
750 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
751 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
752 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
756 text = QStringLiteral(
"east" );
757 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
758 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
760 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
761 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
762 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
766 text = QStringLiteral(
"south" );
767 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
768 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
770 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
771 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
775 text = QStringLiteral(
"north" );
776 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
777 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
779 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
780 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
781 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
784 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
786 l->setParent( mCubeRoot );
790Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
792 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
793 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
794 textEntity->setFont( font );
795 textEntity->setHeight( textHeight );
796 textEntity->setWidth( textWidth );
797 textEntity->setColor( QColor( 192, 192, 192 ) );
798 textEntity->setText( text );
800 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
801 textFrontTransform->setMatrix( rotation );
802 textFrontTransform->setTranslation( translation );
803 textEntity->addComponent( textFrontTransform );
808void Qgs3DAxis::createAxis( Qt::Axis axisType )
810 float cylinderRadius = 0.05f * mCylinderLength;
811 float coneLength = 0.3f * mCylinderLength;
812 float coneBottomRadius = 0.1f * mCylinderLength;
814 QQuaternion rotation;
817 Qt3DExtras::QText2DEntity *text =
nullptr;
818 Qt3DCore::QTransform *textTransform =
nullptr;
823 case Qt::Axis::XAxis:
824 mTextX =
new Qt3DExtras::QText2DEntity();
825 mTextX->setParent( mTwoDLabelSceneEntity );
826 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
827 updateAxisLabelText( mTextX, text );
829 mTextTransformX =
new Qt3DCore::QTransform();
830 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
832 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
835 textTransform = mTextTransformX;
836 name =
"3DAxis_axisX";
839 case Qt::Axis::YAxis:
840 mTextY =
new Qt3DExtras::QText2DEntity();
841 mTextY->setParent( mTwoDLabelSceneEntity );
842 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
843 updateAxisLabelText( mTextY, text );
845 mTextTransformY =
new Qt3DCore::QTransform();
846 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
852 textTransform = mTextTransformY;
853 name =
"3DAxis_axisY";
856 case Qt::Axis::ZAxis:
857 mTextZ =
new Qt3DExtras::QText2DEntity();
858 mTextZ->setParent( mTwoDLabelSceneEntity );
859 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
860 updateAxisLabelText( mTextZ, text );
862 mTextTransformZ =
new Qt3DCore::QTransform();
863 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
865 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
868 textTransform = mTextTransformZ;
869 name =
"3DAxis_axisZ";
877 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
878 cylinder->setObjectName( name );
880 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
881 cylinderMesh->setRadius( cylinderRadius );
882 cylinderMesh->setLength( mCylinderLength );
883 cylinderMesh->setRings( 10 );
884 cylinderMesh->setSlices( 4 );
885 cylinder->addComponent( cylinderMesh );
887 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
888 cylinderMaterial->setAmbient( color );
889 cylinderMaterial->setShininess( 0 );
890 cylinder->addComponent( cylinderMaterial );
892 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
893 QMatrix4x4 transformMatrixCylinder;
894 transformMatrixCylinder.rotate( rotation );
895 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
896 cylinderTransform->setMatrix( transformMatrixCylinder );
897 cylinder->addComponent( cylinderTransform );
900 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
901 coneEntity->setObjectName( name );
902 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
903 coneMesh->setLength( coneLength );
904 coneMesh->setBottomRadius( coneBottomRadius );
905 coneMesh->setTopRadius( 0.0f );
906 coneMesh->setRings( 10 );
907 coneMesh->setSlices( 4 );
908 coneEntity->addComponent( coneMesh );
910 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
911 coneMaterial->setAmbient( color );
912 coneMaterial->setShininess( 0 );
913 coneEntity->addComponent( coneMaterial );
915 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
916 QMatrix4x4 transformMatrixCone;
917 transformMatrixCone.rotate( rotation );
918 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
919 coneTransform->setMatrix( transformMatrixCone );
920 coneEntity->addComponent( coneTransform );
923 text->setColor( QColor( 192, 192, 192, 192 ) );
924 text->addComponent( textTransform );
930 onAxisViewportSizeUpdate();
933void Qgs3DAxis::onAxisViewportSizeUpdate()
942 updateAxisLabelPosition();
949 if ( !mAxisRoot || !mCubeRoot )
954 if ( scaleFactor > 0.0 )
958 setEnableAxis(
true );
960 setEnableCube(
true );
962 mAxisScaleFactor = scaleFactor;
963 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
967 setEnableCube(
false );
968 setEnableAxis(
false );
972void Qgs3DAxis::onCameraUpdate()
974 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
976 if ( parentCamera->viewVector() != mPreviousVector
977 && !std::isnan( parentCamera->viewVector().x() )
978 && !std::isnan( parentCamera->viewVector().y() )
979 && !std::isnan( parentCamera->viewVector().z() ) )
981 mPreviousVector = parentCamera->viewVector();
983 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
984 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
985 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
987 if ( mAxisRoot->isEnabled() )
989 updateAxisLabelPosition();
994void Qgs3DAxis::updateAxisLabelPosition()
996 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
998 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
999 updateAxisLabelText( mTextX, mTextX->text() );
1001 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1002 updateAxisLabelText( mTextY, mTextY->text() );
1004 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1005 updateAxisLabelText( mTextZ, mTextZ->text() );
1009void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
1011 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
1012 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
1013 textEntity->setFont( font );
1014 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
1015 textEntity->setHeight( 1.5f * scaledFontSize );
1018QFont Qgs3DAxis::createFont(
int pointSize )
1020 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1021 font.setPointSize( pointSize );
1022 font.setWeight( QFont::Weight::Black );
1023 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.
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be nullptr if using globe scene, terrain rendering is disabled or when te...
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
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.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
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.
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
Object that controls camera movement based on user input.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void cameraChanged()
Emitted when camera has been updated.
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
void set(double x, double y, double z)
Sets vector coordinates.
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs
Helper struct to store ray casting parameters.