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;
57 , mMapScene( mapScene )
58 , mCameraController( cameraCtrl )
61 mMapScene->engine()->frameGraph()->registerRenderView( std::make_unique<Qgs3DAxisRenderView>(
63 mCanvas, mCameraController, mMapSettings,
69 Q_ASSERT( mRenderView );
70 constructAxisScene( parent3DScene );
71 constructLabelsScene( parent3DScene );
73 mTwoDLabelSceneEntity->addComponent( mRenderView->labelLayer() );
78 onAxisViewportSizeUpdate();
80 init3DObjectPicking();
91 switch ( mMapSettings->get3DAxisSettings().mode() )
112void Qgs3DAxis::init3DObjectPicking()
120 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
121 mScreenRayCaster->addLayer( mRenderView->
objectLayer() );
122 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
123 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
125 mAxisSceneEntity->addComponent( mScreenRayCaster );
127 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
133 if ( event->type() == QEvent::MouseButtonPress )
137 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
138 mLastClickedPos = mouseEvent->pos();
142 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
144 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
147 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
153 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
160 else if ( !mIsDragging )
163 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) /
static_cast<float>( mCanvas->width() ),
static_cast<float>( mouseEvent->pos().y() ) /
static_cast<float>( mCanvas->height() ) );
167 std::ostringstream os;
168 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mRenderView->viewport()->normalizedRect();
172 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
174 mLastClickedButton = mouseEvent->button();
175 mLastClickedPos = mouseEvent->pos();
178 mScreenRayCaster->trigger( mLastClickedPos );
184 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
186 mCanvas->setCursor( mPreviousCursor );
187 mPreviousCursor = Qt::ArrowCursor;
191 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
192 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
194 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
207void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
209 int hitFoundIdx = -1;
214 std::ostringstream os;
215 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
216 for (
int i = 0; i < hits.length(); ++i )
219 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
220 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
221 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
222 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
227 for (
int i = 0; i < hits.length() && hitFoundIdx == -1; ++i )
229 Qt3DCore::QEntity *hitEntity = hits.at( i ).entity();
232 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
234 hitEntity = hitEntity->parentEntity();
236 if ( hits.at( i ).distance() < 500.0f && hitEntity && ( hitEntity == mCubeRoot || hitEntity == mAxisRoot || hitEntity->parent() == mCubeRoot || hitEntity->parent() == mAxisRoot ) )
243 if ( mLastClickedButton == Qt::NoButton )
245 if ( hitFoundIdx != -1 )
247 if ( mCanvas->cursor() != Qt::ArrowCursor )
249 mPreviousCursor = mCanvas->cursor();
250 mCanvas->setCursor( Qt::ArrowCursor );
254 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
256 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
262 else if ( mLastClickedButton == Qt::MouseButton::RightButton && hitFoundIdx != -1 )
264 displayMenuAt( mLastClickedPos );
266 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
270 if ( hitFoundIdx != -1 )
272 Qt3DCore::QEntity *hitEntity = hits.at( hitFoundIdx ).entity();
273 if ( hitEntity && qobject_cast<Qt3DExtras::QText2DEntity *>( hitEntity->parentEntity() ) )
275 hitEntity = hitEntity->parentEntity();
277 if ( hitEntity && ( hitEntity == mCubeRoot || hitEntity->parent() == mCubeRoot ) )
279 switch ( hits.at( hitFoundIdx ).primitiveIndex() / 2 )
283 mCameraController->rotateCameraToEast();
288 mCameraController->rotateCameraToWest();
293 mCameraController->rotateCameraToNorth();
298 mCameraController->rotateCameraToSouth();
303 mCameraController->rotateCameraToTop();
308 mCameraController->rotateCameraToBottom();
319void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
321 mAxisSceneEntity =
new Qt3DCore::QEntity;
322 mAxisSceneEntity->setParent( parent3DScene );
323 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
325 mAxisCamera = mRenderView->objectCamera();
326 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
327 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
331void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
333 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
334 mTwoDLabelSceneEntity->setParent( parent3DScene );
335 mTwoDLabelSceneEntity->setEnabled(
true );
337 mTwoDLabelCamera = mRenderView->labelCamera();
338 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
339 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
340 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
345 const int viewportWidth =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
346 const int viewportHeight =
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
347 QRect viewportRect(
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ),
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
349 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
350 destPos.setZ( 0.0f );
354void Qgs3DAxis::setEnableCube(
bool show )
356 mCubeRoot->setEnabled( show );
359 mCubeRoot->setParent( mAxisSceneEntity );
363 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
367void Qgs3DAxis::setEnableAxis(
bool show )
369 mAxisRoot->setEnabled( show );
372 mAxisRoot->setParent( mAxisSceneEntity );
376 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
379 mTextX->setEnabled( show );
380 mTextY->setEnabled( show );
381 mTextZ->setEnabled( show );
384void Qgs3DAxis::createAxisScene()
386 if ( !mAxisRoot || !mCubeRoot )
388 mAxisRoot =
new Qt3DCore::QEntity;
389 mAxisRoot->setParent( mAxisSceneEntity );
390 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
391 mAxisRoot->addComponent( mRenderView->objectLayer() );
393 createAxis( Qt::Axis::XAxis );
394 createAxis( Qt::Axis::YAxis );
395 createAxis( Qt::Axis::ZAxis );
397 mCubeRoot =
new Qt3DCore::QEntity;
398 mCubeRoot->setParent( mAxisSceneEntity );
399 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
400 mCubeRoot->addComponent( mRenderView->objectLayer() );
409 mAxisSceneEntity->setEnabled(
false );
410 setEnableAxis(
false );
411 setEnableCube(
false );
412 mRenderView->setEnabled(
false );
416 mRenderView->setEnabled(
true );
417 mAxisSceneEntity->setEnabled(
true );
420 setEnableCube(
false );
421 setEnableAxis(
true );
423 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
425 if ( axisDirections.length() > 0 )
428 mTextX->setText(
"X?" );
430 if ( axisDirections.length() > 1 )
433 mTextY->setText(
"Y?" );
435 if ( axisDirections.length() > 2 )
438 mTextZ->setText( u
"up"_s );
442 setEnableCube(
true );
443 setEnableAxis(
false );
447 setEnableCube(
false );
448 setEnableAxis(
true );
449 mTextX->setText(
"X?" );
450 mTextY->setText(
"Y?" );
451 mTextZ->setText(
"Z?" );
454 updateAxisLabelPosition();
458void Qgs3DAxis::createMenu()
463 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
464 typeOffAct->setCheckable(
true );
465 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
468 typeOffAct->setChecked(
true );
471 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
472 typeCrsAct->setCheckable(
true );
473 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
476 typeCrsAct->setChecked(
true );
479 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
480 typeCubeAct->setCheckable(
true );
481 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
484 typeCubeAct->setChecked(
true );
487 QActionGroup *typeGroup =
new QActionGroup( mMenu );
488 typeGroup->addAction( typeOffAct );
489 typeGroup->addAction( typeCrsAct );
490 typeGroup->addAction( typeCubeAct );
496 QMenu *typeMenu =
new QMenu( u
"Axis Type"_s, mMenu );
497 Q_ASSERT( typeMenu );
498 typeMenu->addAction( typeOffAct );
499 typeMenu->addAction( typeCrsAct );
500 typeMenu->addAction( typeCubeAct );
501 mMenu->addMenu( typeMenu );
504 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
505 hPosLeftAct->setCheckable(
true );
507 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
508 hPosLeftAct->setChecked(
true );
511 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
512 hPosMiddleAct->setCheckable(
true );
514 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
515 hPosMiddleAct->setChecked(
true );
518 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
519 hPosRightAct->setCheckable(
true );
521 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
522 hPosRightAct->setChecked(
true );
525 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
526 hPosGroup->addAction( hPosLeftAct );
527 hPosGroup->addAction( hPosMiddleAct );
528 hPosGroup->addAction( hPosRightAct );
530 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
531 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
532 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
534 QMenu *horizPosMenu =
new QMenu( u
"Horizontal Position"_s, mMenu );
535 horizPosMenu->addAction( hPosLeftAct );
536 horizPosMenu->addAction( hPosMiddleAct );
537 horizPosMenu->addAction( hPosRightAct );
538 mMenu->addMenu( horizPosMenu );
541 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
542 vPosTopAct->setCheckable(
true );
544 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
545 vPosTopAct->setChecked(
true );
548 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
549 vPosMiddleAct->setCheckable(
true );
551 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
552 vPosMiddleAct->setChecked(
true );
555 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
556 vPosBottomAct->setCheckable(
true );
558 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
559 vPosBottomAct->setChecked(
true );
562 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
563 vPosGroup->addAction( vPosTopAct );
564 vPosGroup->addAction( vPosMiddleAct );
565 vPosGroup->addAction( vPosBottomAct );
567 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
568 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
569 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
571 QMenu *vertPosMenu =
new QMenu( u
"Vertical Position"_s, mMenu );
572 vertPosMenu->addAction( vPosTopAct );
573 vertPosMenu->addAction( vPosMiddleAct );
574 vertPosMenu->addAction( vPosBottomAct );
575 mMenu->addMenu( vertPosMenu );
579 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+5", mMenu );
580 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+9", mMenu );
581 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
582 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
583 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
584 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
585 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ) +
"\t Ctrl+3", mMenu );
595 QMenu *viewMenu =
new QMenu( u
"Camera View"_s, mMenu );
596 viewMenu->addAction( viewHomeAct );
597 viewMenu->addAction( viewTopAct );
598 viewMenu->addAction( viewNorthAct );
599 viewMenu->addAction( viewEastAct );
600 viewMenu->addAction( viewSouthAct );
601 viewMenu->addAction( viewWestAct );
602 viewMenu->addAction( viewBottomAct );
603 mMenu->addMenu( viewMenu );
606 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(),
true );
609void Qgs3DAxis::hideMenu()
611 if ( mMenu && mMenu->isVisible() )
615void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
622 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
627 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
629 mMapSettings->set3DAxisSettings( s );
632void Qgs3DAxis::createCube()
634 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
637 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
638 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
639 Qgs3DWiredMesh *cubeLine =
new Qgs3DWiredMesh;
640 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
642 cubeLineEntity->addComponent( cubeLine );
644 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
645 cubeLineMaterial->setAmbient( Qt::white );
646 cubeLineEntity->addComponent( cubeLineMaterial );
649 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
650 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
651 cubeMesh->setXExtent( mCylinderLength );
652 cubeMesh->setYExtent( mCylinderLength );
653 cubeMesh->setZExtent( mCylinderLength );
654 mCubeRoot->addComponent( cubeMesh );
656 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
657 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
658 cubeMaterial->setShininess( 100 );
659 mCubeRoot->addComponent( cubeMaterial );
661 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
662 QMatrix4x4 transformMatrixcube;
664 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
665 cubeTransform->setMatrix( transformMatrixcube );
666 mCubeRoot->addComponent( cubeTransform );
670 const int fontSize =
static_cast<int>( std::round( 0.75f *
static_cast<float>( mFontSize ) ) );
671 const float textHeight =
static_cast<float>( fontSize ) * 1.5f;
673 const QFont font = createFont( fontSize );
677 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
678 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
680 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
685 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
686 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
688 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
689 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
694 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
695 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
697 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
698 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
699 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
704 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
705 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
707 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
708 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
709 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
714 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
715 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
717 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
718 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
723 textWidth =
static_cast<float>( text.length() * fontSize ) * 0.75f;
724 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
726 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
727 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
728 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
731 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
733 l->setParent( mCubeRoot );
737Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &font,
const QMatrix4x4 &rotation,
const QVector3D &translation )
739 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
740 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
741 textEntity->setFont( font );
742 textEntity->setHeight( textHeight );
743 textEntity->setWidth( textWidth );
744 textEntity->setColor( QColor( 192, 192, 192 ) );
745 textEntity->setText( text );
747 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
748 textFrontTransform->setMatrix( rotation );
749 textFrontTransform->setTranslation( translation );
750 textEntity->addComponent( textFrontTransform );
755void Qgs3DAxis::createAxis( Qt::Axis axisType )
757 float cylinderRadius = 0.05f * mCylinderLength;
758 float coneLength = 0.3f * mCylinderLength;
759 float coneBottomRadius = 0.1f * mCylinderLength;
761 QQuaternion rotation;
764 Qt3DExtras::QText2DEntity *text =
nullptr;
765 Qt3DCore::QTransform *textTransform =
nullptr;
770 case Qt::Axis::XAxis:
771 mTextX =
new Qt3DExtras::QText2DEntity();
772 mTextX->setParent( mTwoDLabelSceneEntity );
773 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
774 updateAxisLabelText( mTextX, text );
776 mTextTransformX =
new Qt3DCore::QTransform();
777 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
779 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
782 textTransform = mTextTransformX;
783 name =
"3DAxis_axisX";
786 case Qt::Axis::YAxis:
787 mTextY =
new Qt3DExtras::QText2DEntity();
788 mTextY->setParent( mTwoDLabelSceneEntity );
789 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
790 updateAxisLabelText( mTextY, text );
792 mTextTransformY =
new Qt3DCore::QTransform();
793 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
799 textTransform = mTextTransformY;
800 name =
"3DAxis_axisY";
803 case Qt::Axis::ZAxis:
804 mTextZ =
new Qt3DExtras::QText2DEntity();
805 mTextZ->setParent( mTwoDLabelSceneEntity );
806 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, [
this](
const QString &text ) {
807 updateAxisLabelText( mTextZ, text );
809 mTextTransformZ =
new Qt3DCore::QTransform();
810 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
812 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
815 textTransform = mTextTransformZ;
816 name =
"3DAxis_axisZ";
824 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
825 cylinder->setObjectName( name );
827 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
828 cylinderMesh->setRadius( cylinderRadius );
829 cylinderMesh->setLength( mCylinderLength );
830 cylinderMesh->setRings( 10 );
831 cylinderMesh->setSlices( 4 );
832 cylinder->addComponent( cylinderMesh );
834 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
835 cylinderMaterial->setAmbient( color );
836 cylinderMaterial->setShininess( 0 );
837 cylinder->addComponent( cylinderMaterial );
839 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
840 QMatrix4x4 transformMatrixCylinder;
841 transformMatrixCylinder.rotate( rotation );
842 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
843 cylinderTransform->setMatrix( transformMatrixCylinder );
844 cylinder->addComponent( cylinderTransform );
847 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
848 coneEntity->setObjectName( name );
849 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
850 coneMesh->setLength( coneLength );
851 coneMesh->setBottomRadius( coneBottomRadius );
852 coneMesh->setTopRadius( 0.0f );
853 coneMesh->setRings( 10 );
854 coneMesh->setSlices( 4 );
855 coneEntity->addComponent( coneMesh );
857 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
858 coneMaterial->setAmbient( color );
859 coneMaterial->setShininess( 0 );
860 coneEntity->addComponent( coneMaterial );
862 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
863 QMatrix4x4 transformMatrixCone;
864 transformMatrixCone.rotate( rotation );
865 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
866 coneTransform->setMatrix( transformMatrixCone );
867 coneEntity->addComponent( coneTransform );
870 text->setColor( QColor( 192, 192, 192, 192 ) );
871 text->addComponent( textTransform );
877 onAxisViewportSizeUpdate();
880void Qgs3DAxis::onAxisViewportSizeUpdate()
889 updateAxisLabelPosition();
896 if ( !mAxisRoot || !mCubeRoot )
901 if ( scaleFactor > 0.0 )
905 setEnableAxis(
true );
907 setEnableCube(
true );
909 mAxisScaleFactor = scaleFactor;
910 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
914 setEnableCube(
false );
915 setEnableAxis(
false );
919void Qgs3DAxis::onCameraUpdate()
921 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
923 if ( parentCamera->viewVector() != mPreviousVector
924 && !std::isnan( parentCamera->viewVector().x() )
925 && !std::isnan( parentCamera->viewVector().y() )
926 && !std::isnan( parentCamera->viewVector().z() ) )
928 mPreviousVector = parentCamera->viewVector();
930 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
931 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
932 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
934 if ( mAxisRoot->isEnabled() )
936 updateAxisLabelPosition();
941void Qgs3DAxis::updateAxisLabelPosition()
943 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
945 mTextTransformX->setTranslation(
from3DTo2DLabelPosition( mTextCoordX *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
946 updateAxisLabelText( mTextX, mTextX->text() );
948 mTextTransformY->setTranslation(
from3DTo2DLabelPosition( mTextCoordY *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
949 updateAxisLabelText( mTextY, mTextY->text() );
951 mTextTransformZ->setTranslation(
from3DTo2DLabelPosition( mTextCoordZ *
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
952 updateAxisLabelText( mTextZ, mTextZ->text() );
956void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity,
const QString &text )
958 const float scaledFontSize =
static_cast<float>( mAxisScaleFactor ) *
static_cast<float>( mFontSize );
959 const QFont font = createFont(
static_cast<int>( std::round( scaledFontSize ) ) );
960 textEntity->setFont( font );
961 textEntity->setWidth( scaledFontSize *
static_cast<float>( text.length() ) );
962 textEntity->setHeight( 1.5f * scaledFontSize );
965QFont Qgs3DAxis::createFont(
int pointSize )
967 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
968 font.setPointSize( pointSize );
969 font.setWeight( QFont::Weight::Black );
970 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)