18#include <Qt3DCore/QTransform>
19#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
20#include <Qt3DRender/QAttribute>
21#include <Qt3DRender/QGeometry>
26#include <Qt3DCore/QAttribute>
27#include <Qt3DCore/QGeometry>
32#include <Qt3DExtras/QCylinderMesh>
33#include <Qt3DExtras/QPhongMaterial>
34#include <Qt3DExtras/QConeMesh>
35#include <Qt3DRender/qcameralens.h>
36#include <Qt3DRender/QCameraSelector>
37#include <Qt3DRender/QClearBuffers>
38#include <Qt3DRender/QLayer>
39#include <Qt3DRender/QLayerFilter>
40#include <Qt3DRender/QPointLight>
44#include <QFontDatabase>
46#include <QApplication>
47#include <QActionGroup>
58 Qt3DCore::QEntity *parent3DScene,
62 : QObject( parentWindow )
64 , mParentWindow( parentWindow )
65 , mMapScene( mapScene )
66 , mCameraController( cameraCtrl )
69 mAxisViewport = constructAxisViewport( parent3DScene );
70 mAxisViewport->setParent( mParentWindow->activeFrameGraph() );
72 mTwoDLabelViewport = constructLabelViewport( parent3DScene, QRectF( 0.0f, 0.0f, 1.0f, 1.0f ) );
73 mTwoDLabelViewport->setParent( mParentWindow->activeFrameGraph() );
76 connect( mParentWindow, &Qt3DExtras::Qt3DWindow::widthChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
77 connect( mParentWindow, &Qt3DExtras::Qt3DWindow::heightChanged,
this, &Qgs3DAxis::onAxisViewportSizeUpdate );
80 onAxisViewportSizeUpdate();
82 init3DObjectPicking();
84 createKeyboardShortCut();
93void Qgs3DAxis::init3DObjectPicking( )
99 mScreenRayCaster =
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
100 mScreenRayCaster->addLayer( mAxisSceneLayer );
101 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
102 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
104 mAxisSceneEntity->addComponent( mScreenRayCaster );
106 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged,
this, &Qgs3DAxis::onTouchedByRay );
109 mParentWindow->installEventFilter(
this );
112bool Qgs3DAxis::eventFilter( QObject *watched, QEvent *event )
114 if ( watched != mParentWindow )
117 if ( event->type() == QEvent::MouseButtonPress )
121 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
122 mLastClickedPos = mouseEvent->pos();
126 else if ( event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseMove )
128 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
131 if ( event->type() == QEvent::MouseMove &&
132 ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
138 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
145 else if ( ! mIsDragging )
148 QPointF normalizedPos(
static_cast<float>( mouseEvent->pos().x() ) / mParentWindow->width(),
149 (
float )mouseEvent->pos().y() / mParentWindow->height() );
153 std::ostringstream os;
154 os <<
"QGS3DAxis: normalized pos: " << normalizedPos <<
" / viewport: " << mAxisViewport->normalizedRect();
158 if ( mAxisViewport->normalizedRect().contains( normalizedPos ) )
160 mLastClickedButton = mouseEvent->button();
161 mLastClickedPos = mouseEvent->pos();
164 mScreenRayCaster->trigger( mLastClickedPos );
168 else if ( mPreviousCursor != Qt::ArrowCursor && mParentWindow->cursor() == Qt::ArrowCursor )
170 mParentWindow->setCursor( mPreviousCursor );
171 mPreviousCursor = Qt::ArrowCursor;
182void Qgs3DAxis::onTouchedByRay(
const Qt3DRender::QAbstractRayCaster::Hits &hits )
189 std::ostringstream os;
190 os <<
"Qgs3DAxis::onTouchedByRay " << hits.length() <<
" hits at pos " << mLastClickedPos <<
" with QButton: " << mLastClickedButton;
191 for (
int i = 0; i < hits.length(); ++i )
194 os <<
"\tHit Type: " << hits.at( i ).type() <<
"\n";
195 os <<
"\tHit triangle id: " << hits.at( i ).primitiveIndex() <<
"\n";
196 os <<
"\tHit distance: " << hits.at( i ).distance() <<
"\n";
197 os <<
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
202 for (
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
204 if ( hits.at( i ).distance() < 500.0f && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
211 if ( mLastClickedButton == Qt::NoButton )
213 if ( mHitsFound != -1 )
215 if ( mParentWindow->cursor() != Qt::ArrowCursor )
217 mPreviousCursor = mParentWindow->cursor();
218 mParentWindow->setCursor( Qt::ArrowCursor );
223 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 )
225 displayMenuAt( mLastClickedPos );
227 else if ( mLastClickedButton == Qt::MouseButton::LeftButton )
231 if ( mHitsFound != -1 )
233 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
235 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
239 onCameraViewChangeEast();
244 onCameraViewChangeWest();
249 onCameraViewChangeNorth();
254 onCameraViewChangeSouth();
259 onCameraViewChangeTop();
264 onCameraViewChangeBottom();
275Qt3DRender::QViewport *Qgs3DAxis::constructAxisViewport( Qt3DCore::QEntity *parent3DScene )
277 Qt3DRender::QViewport *axisViewport =
new Qt3DRender::QViewport;
281 mAxisSceneEntity =
new Qt3DCore::QEntity;
282 mAxisSceneEntity->setParent( parent3DScene );
283 mAxisSceneEntity->setObjectName(
"3DAxis_SceneEntity" );
285 mAxisSceneLayer =
new Qt3DRender::QLayer;
286 mAxisSceneLayer->setObjectName(
"3DAxis_SceneLayer" );
287 mAxisSceneLayer->setParent( mAxisSceneEntity );
288 mAxisSceneLayer->setRecursive(
true );
290 mAxisCamera =
new Qt3DRender::QCamera;
291 mAxisCamera->setParent( mAxisSceneEntity );
292 mAxisCamera->setProjectionType( mCameraController->
camera()->projectionType() );
293 mAxisCamera->lens()->setFieldOfView( mCameraController->
camera()->lens()->fieldOfView() * 0.5f );
295 mAxisCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
296 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
299 Qt3DRender::QLayer *axisLayer =
new Qt3DRender::QLayer;
300 axisLayer->setRecursive(
true );
301 mAxisSceneEntity->addComponent( axisLayer );
303 Qt3DRender::QLayerFilter *axisLayerFilter =
new Qt3DRender::QLayerFilter( axisViewport );
304 axisLayerFilter->addLayer( axisLayer );
306 Qt3DRender::QCameraSelector *axisCameraSelector =
new Qt3DRender::QCameraSelector;
307 axisCameraSelector->setParent( axisLayerFilter );
308 axisCameraSelector->setCamera( mAxisCamera );
310 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( axisCameraSelector );
311 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
317Qt3DRender::QViewport *Qgs3DAxis::constructLabelViewport( Qt3DCore::QEntity *parent3DScene,
const QRectF &parentViewportSize )
319 Qt3DRender::QViewport *twoDViewport =
new Qt3DRender::QViewport;
321 twoDViewport->setNormalizedRect( parentViewportSize );
323 mTwoDLabelSceneEntity =
new Qt3DCore::QEntity;
324 mTwoDLabelSceneEntity->setParent( parent3DScene );
325 mTwoDLabelSceneEntity->setEnabled(
true );
327 mTwoDLabelCamera =
new Qt3DRender::QCamera;
328 mTwoDLabelCamera->setParent( mTwoDLabelSceneEntity );
329 mTwoDLabelCamera->setProjectionType( Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection );
330 mTwoDLabelCamera->lens()->setOrthographicProjection(
331 -mParentWindow->width() / 2.0f, mParentWindow->width() / 2.0f,
332 -mParentWindow->height() / 2.0f, mParentWindow->height() / 2.0f,
335 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
336 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
338 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
340 Qt3DRender::QLayer *twoDLayer =
new Qt3DRender::QLayer;
341 twoDLayer->setRecursive(
true );
342 mTwoDLabelSceneEntity->addComponent( twoDLayer );
344 Qt3DRender::QLayerFilter *twoDLayerFilter =
new Qt3DRender::QLayerFilter( twoDViewport );
345 twoDLayerFilter->addLayer( twoDLayer );
347 Qt3DRender::QCameraSelector *twoDCameraSelector =
new Qt3DRender::QCameraSelector;
348 twoDCameraSelector->setParent( twoDLayerFilter );
349 twoDCameraSelector->setCamera( mTwoDLabelCamera );
351 Qt3DRender::QClearBuffers *clearBuffers =
new Qt3DRender::QClearBuffers( twoDCameraSelector );
352 clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
359 Qt3DRender::QCamera *sourceCamera, Qt3DRender::QViewport *sourceViewport,
360 Qt3DRender::QCamera *destCamera, Qt3DRender::QViewport *destViewport,
361 const QSize &destSize )
363 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(),
364 destCamera->projectionMatrix(),
366 destViewport->normalizedRect().width() * destSize.width(),
367 destViewport->normalizedRect().height() * destSize.height() ) );
368 QPointF axisCenter = sourceViewport->normalizedRect().center();
369 QPointF labelCenter = destViewport->normalizedRect().center();
370 QVector3D viewTranslation = QVector3D( ( axisCenter - labelCenter ).x() * destSize.width(),
371 ( axisCenter - labelCenter ).y() * destSize.height(),
373 destPos -= QVector3D( labelCenter.x() * destSize.width(),
374 labelCenter.y() * destSize.height(),
376 destPos.setX( destPos.x() + viewTranslation.x() );
377 destPos.setY( destPos.y() - viewTranslation.y() );
378 destPos.setZ( 0.0f );
382 std::ostringstream os;
383 os <<
"Qgs3DAxis::from3DTo2DLabelPosition: sourcePos: " << sourcePos.toPoint()
384 <<
" with translation: " << viewTranslation.toPoint()
385 <<
" corrected to pos: " << destPos.toPoint();
391void Qgs3DAxis::setEnableCube(
bool show )
393 mCubeRoot->setEnabled( show );
396 mCubeRoot->setParent( mAxisSceneEntity );
400 mCubeRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
404void Qgs3DAxis::setEnableAxis(
bool show )
406 mAxisRoot->setEnabled( show );
409 mAxisRoot->setParent( mAxisSceneEntity );
413 mAxisRoot->setParent(
static_cast<Qt3DCore::QEntity *
>(
nullptr ) );
416 mTextX->setEnabled( show );
417 mTextY->setEnabled( show );
418 mTextZ->setEnabled( show );
421void Qgs3DAxis::createAxisScene()
423 if ( mAxisRoot ==
nullptr || mCubeRoot ==
nullptr )
425 mAxisRoot =
new Qt3DCore::QEntity;
426 mAxisRoot->setParent( mAxisSceneEntity );
427 mAxisRoot->setObjectName(
"3DAxis_AxisRoot" );
428 mAxisRoot->addComponent( mAxisSceneLayer );
430 createAxis( Qt::Axis::XAxis );
431 createAxis( Qt::Axis::YAxis );
432 createAxis( Qt::Axis::ZAxis );
434 mCubeRoot =
new Qt3DCore::QEntity;
435 mCubeRoot->setParent( mAxisSceneEntity );
436 mCubeRoot->setObjectName(
"3DAxis_CubeRoot" );
437 mCubeRoot->addComponent( mAxisSceneLayer );
446 mAxisSceneEntity->setEnabled(
false );
447 setEnableAxis(
false );
448 setEnableCube(
false );
452 mAxisSceneEntity->setEnabled(
true );
455 setEnableCube(
false );
456 setEnableAxis(
true );
458 const QList< Qgis::CrsAxisDirection > axisDirections = mCrs.
axisOrdering();
460 if ( axisDirections.length() > 0 )
463 mTextY->setText(
"X?" );
465 if ( axisDirections.length() > 1 )
468 mTextY->setText(
"Y?" );
470 if ( axisDirections.length() > 2 )
473 mTextZ->setText( QStringLiteral(
"up" ) );
477 setEnableCube(
true );
478 setEnableAxis(
false );
482 setEnableCube(
false );
483 setEnableAxis(
true );
484 mTextX->setText(
"X?" );
485 mTextY->setText(
"Y?" );
486 mTextZ->setText(
"Z?" );
489 updateAxisLabelPosition();
493void Qgs3DAxis::createKeyboardShortCut()
498 QWidget *mapCanvas =
dynamic_cast<QWidget *
>( eng->parent() );
499 if ( mapCanvas ==
nullptr )
505 QShortcut *shortcutHome =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_1 ), mapCanvas );
506 connect( shortcutHome, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeHome();} );
508 QShortcut *shortcutTop =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_5 ), mapCanvas );
509 connect( shortcutTop, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeTop();} );
511 QShortcut *shortcutNorth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_8 ), mapCanvas );
512 connect( shortcutNorth, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeNorth();} );
514 QShortcut *shortcutEast =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_6 ), mapCanvas );
515 connect( shortcutEast, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeEast();} );
517 QShortcut *shortcutSouth =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_2 ), mapCanvas );
518 connect( shortcutSouth, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeSouth();} );
520 QShortcut *shortcutWest =
new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ), mapCanvas );
521 connect( shortcutWest, &QShortcut::activated,
this, [
this]( ) {onCameraViewChangeWest();} );
526void Qgs3DAxis::createMenu()
531 QAction *typeOffAct =
new QAction( tr(
"&Off" ), mMenu );
532 typeOffAct->setCheckable(
true );
533 typeOffAct->setStatusTip( tr(
"Disable 3D axis" ) );
537 typeOffAct->setChecked(
true );
540 QAction *typeCrsAct =
new QAction( tr(
"Coordinate Reference &System" ), mMenu );
541 typeCrsAct->setCheckable(
true );
542 typeCrsAct->setStatusTip( tr(
"Coordinate Reference System 3D axis" ) );
546 typeCrsAct->setChecked(
true );
549 QAction *typeCubeAct =
new QAction( tr(
"&Cube" ), mMenu );
550 typeCubeAct->setCheckable(
true );
551 typeCubeAct->setStatusTip( tr(
"Cube 3D axis" ) );
555 typeCubeAct->setChecked(
true );
558 QActionGroup *typeGroup =
new QActionGroup( mMenu );
559 typeGroup->addAction( typeOffAct );
560 typeGroup->addAction( typeCrsAct );
561 typeGroup->addAction( typeCubeAct );
567 QMenu *typeMenu =
new QMenu( QStringLiteral(
"Axis Type" ), mMenu );
568 Q_ASSERT( typeMenu );
569 typeMenu->addAction( typeOffAct );
570 typeMenu->addAction( typeCrsAct );
571 typeMenu->addAction( typeCubeAct );
572 mMenu->addMenu( typeMenu );
575 QAction *hPosLeftAct =
new QAction( tr(
"&Left" ), mMenu );
576 hPosLeftAct->setCheckable(
true );
580 hPosLeftAct->setChecked(
true );
583 QAction *hPosMiddleAct =
new QAction( tr(
"&Center" ), mMenu );
584 hPosMiddleAct->setCheckable(
true );
588 hPosMiddleAct->setChecked(
true );
591 QAction *hPosRightAct =
new QAction( tr(
"&Right" ), mMenu );
592 hPosRightAct->setCheckable(
true );
596 hPosRightAct->setChecked(
true );
599 QActionGroup *hPosGroup =
new QActionGroup( mMenu );
600 hPosGroup->addAction( hPosLeftAct );
601 hPosGroup->addAction( hPosMiddleAct );
602 hPosGroup->addAction( hPosRightAct );
604 connect( hPosLeftAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorLeft );} );
605 connect( hPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter );} );
606 connect( hPosRightAct, &QAction::triggered,
this, [
this](
bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorRight );} );
608 QMenu *horizPosMenu =
new QMenu( QStringLiteral(
"Horizontal Position" ), mMenu );
609 horizPosMenu->addAction( hPosLeftAct );
610 horizPosMenu->addAction( hPosMiddleAct );
611 horizPosMenu->addAction( hPosRightAct );
612 mMenu->addMenu( horizPosMenu );
615 QAction *vPosTopAct =
new QAction( tr(
"&Top" ), mMenu );
616 vPosTopAct->setCheckable(
true );
620 vPosTopAct->setChecked(
true );
623 QAction *vPosMiddleAct =
new QAction( tr(
"&Middle" ), mMenu );
624 vPosMiddleAct->setCheckable(
true );
628 vPosMiddleAct->setChecked(
true );
631 QAction *vPosBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
632 vPosBottomAct->setCheckable(
true );
636 vPosBottomAct->setChecked(
true );
639 QActionGroup *vPosGroup =
new QActionGroup( mMenu );
640 vPosGroup->addAction( vPosTopAct );
641 vPosGroup->addAction( vPosMiddleAct );
642 vPosGroup->addAction( vPosBottomAct );
644 connect( vPosTopAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorTop );} );
645 connect( vPosMiddleAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter );} );
646 connect( vPosBottomAct, &QAction::triggered,
this, [
this](
bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorBottom );} );
648 QMenu *vertPosMenu =
new QMenu( QStringLiteral(
"Vertical Position" ), mMenu );
649 vertPosMenu->addAction( vPosTopAct );
650 vertPosMenu->addAction( vPosMiddleAct );
651 vertPosMenu->addAction( vPosBottomAct );
652 mMenu->addMenu( vertPosMenu );
655 QAction *viewHomeAct =
new QAction( tr(
"&Home" ) +
"\t Ctrl+1", mMenu );
656 QAction *viewTopAct =
new QAction( tr(
"&Top" ) +
"\t Ctrl+5", mMenu );
657 QAction *viewNorthAct =
new QAction( tr(
"&North" ) +
"\t Ctrl+8", mMenu );
658 QAction *viewEastAct =
new QAction( tr(
"&East" ) +
"\t Ctrl+6", mMenu );
659 QAction *viewSouthAct =
new QAction( tr(
"&South" ) +
"\t Ctrl+2", mMenu );
660 QAction *viewWestAct =
new QAction( tr(
"&West" ) +
"\t Ctrl+4", mMenu );
661 QAction *viewBottomAct =
new QAction( tr(
"&Bottom" ), mMenu );
663 connect( viewHomeAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeHome );
664 connect( viewTopAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeTop );
665 connect( viewNorthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeNorth );
666 connect( viewEastAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeEast );
667 connect( viewSouthAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeSouth );
668 connect( viewWestAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeWest );
669 connect( viewBottomAct, &QAction::triggered,
this, &Qgs3DAxis::onCameraViewChangeBottom );
671 QMenu *viewMenu =
new QMenu( QStringLiteral(
"Camera View" ), mMenu );
672 viewMenu->addAction( viewHomeAct );
673 viewMenu->addAction( viewTopAct );
674 viewMenu->addAction( viewNorthAct );
675 viewMenu->addAction( viewEastAct );
676 viewMenu->addAction( viewSouthAct );
677 viewMenu->addAction( viewWestAct );
678 viewMenu->addAction( viewBottomAct );
679 mMenu->addMenu( viewMenu );
685void Qgs3DAxis::hideMenu()
687 if ( mMenu && mMenu->isVisible() )
691void Qgs3DAxis::displayMenuAt(
const QPoint &sourcePos )
693 if ( mMenu ==
nullptr )
697 QObject *threeDMapCanvasWidget = mMapScene->
engine()
701 QWidget *container =
dynamic_cast<QWidget *
>( threeDMapCanvasWidget->parent() );
703 mMenu->popup( container->mapToGlobal( sourcePos ) );
705 mMenu->popup( mParentWindow->parent()->mapToGlobal( sourcePos ) );
715void Qgs3DAxis::onAxisHorizPositionChanged( Qt::AnchorPoint pos )
722void Qgs3DAxis::onAxisVertPositionChanged( Qt::AnchorPoint pos )
729void Qgs3DAxis::onCameraViewChange(
float pitch,
float yaw )
732 double elevation = 0.0;
736 QVector3D camPos = mCameraController->
camera()->position();
737 QgsRayCastingUtils::Ray3D ray( camPos, pos.
toVector3D() - camPos, mCameraController->
camera()->farPlane() );
738 const QVector<QgsRayCastingUtils::RayHit> hits = mMapScene->
terrainEntity()->rayIntersection( ray, QgsRayCastingUtils::RayCastContext() );
739 if ( !hits.isEmpty() )
741 elevation = hits.at( 0 ).pos.y();
742 QgsDebugMsgLevel( QString(
"Computed elevation from terrain: %1" ).arg( elevation ), 2 );
756void Qgs3DAxis::createCube( )
758 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
761 Qt3DCore::QEntity *cubeLineEntity =
new Qt3DCore::QEntity( mCubeRoot );
762 cubeLineEntity->setObjectName(
"3DAxis_cubeline" );
764 QgsAABB box =
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f,
765 mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
767 cubeLineEntity->addComponent( cubeLine );
769 Qt3DExtras::QPhongMaterial *cubeLineMaterial =
new Qt3DExtras::QPhongMaterial;
770 cubeLineMaterial->setAmbient( Qt::white );
771 cubeLineEntity->addComponent( cubeLineMaterial );
774 Qt3DExtras::QCuboidMesh *cubeMesh =
new Qt3DExtras::QCuboidMesh;
775 cubeMesh->setObjectName(
"3DAxis_cubemesh" );
776 cubeMesh->setXExtent( mCylinderLength );
777 cubeMesh->setYExtent( mCylinderLength );
778 cubeMesh->setZExtent( mCylinderLength );
779 mCubeRoot->addComponent( cubeMesh );
781 Qt3DExtras::QPhongMaterial *cubeMaterial =
new Qt3DExtras::QPhongMaterial( mCubeRoot );
782 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
783 cubeMaterial->setShininess( 100 );
784 mCubeRoot->addComponent( cubeMaterial );
786 Qt3DCore::QTransform *cubeTransform =
new Qt3DCore::QTransform;
787 QMatrix4x4 transformMatrixcube;
789 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
790 cubeTransform->setMatrix( transformMatrixcube );
791 mCubeRoot->addComponent( cubeTransform );
795 int fontSize = 0.75 * mFontSize;
796 float textHeight = fontSize * 1.5f;
798 QFont f = QFontDatabase::systemFont( QFontDatabase::FixedFont );
799 f.setPointSize( fontSize );
800 f.setWeight( QFont::Weight::Black );
803 text = QStringLiteral(
"top" );
804 textWidth = text.length() * fontSize * 0.75f;
805 QVector3D translation = minPos + QVector3D(
806 mCylinderLength * 0.5f - textWidth / 2.0f,
807 mCylinderLength * 0.5f - textHeight / 2.0f,
808 mCylinderLength * 1.01f );
810 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
814 text = QStringLiteral(
"btm" );
815 textWidth = text.length() * fontSize * 0.75f;
816 QVector3D translation = minPos + QVector3D(
817 mCylinderLength * 0.5f - textWidth / 2.0f,
818 mCylinderLength * 0.5f + textHeight / 2.0f,
819 -mCylinderLength * 0.01f );
821 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
822 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
826 text = QStringLiteral(
"west" );
827 textWidth = text.length() * fontSize * 0.75f;
828 QVector3D translation = minPos + QVector3D(
829 - mCylinderLength * 0.01f,
830 mCylinderLength * 0.5f + textWidth / 2.0f,
831 mCylinderLength * 0.5f - textHeight / 2.0f );
833 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
834 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
835 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
839 text = QStringLiteral(
"east" );
840 textWidth = text.length() * fontSize * 0.75f;
841 QVector3D translation = minPos + QVector3D(
842 mCylinderLength * 1.01f,
843 mCylinderLength * 0.5f - textWidth / 2.0f,
844 mCylinderLength * 0.5f - textHeight / 2.0f );
846 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
847 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
848 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
852 text = QStringLiteral(
"south" );
853 textWidth = text.length() * fontSize * 0.75f;
854 QVector3D translation = minPos + QVector3D(
855 mCylinderLength * 0.5f - textWidth / 2.0f,
856 - mCylinderLength * 0.01f,
857 mCylinderLength * 0.5f - textHeight / 2.0f );
859 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
860 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
864 text = QStringLiteral(
"north" );
865 textWidth = text.length() * fontSize * 0.75f;
866 QVector3D translation = minPos + QVector3D(
867 mCylinderLength * 0.5f + textWidth / 2.0f,
868 mCylinderLength * 1.01f,
869 mCylinderLength * 0.5f - textHeight / 2.0f );
871 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
872 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
873 mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
876 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
878 l->setParent( mCubeRoot );
882Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText(
const QString &text,
float textHeight,
float textWidth,
const QFont &f,
const QMatrix4x4 &rotation,
const QVector3D &translation )
884 Qt3DExtras::QText2DEntity *textEntity =
new Qt3DExtras::QText2DEntity;
885 textEntity->setObjectName(
"3DAxis_cube_label_" + text );
886 textEntity->setFont( f );
887 textEntity->setHeight( textHeight );
888 textEntity->setWidth( textWidth );
889 textEntity->setColor( QColor( 192, 192, 192 ) );
890 textEntity->setText( text );
892 Qt3DCore::QTransform *textFrontTransform =
new Qt3DCore::QTransform();
893 textFrontTransform->setMatrix( rotation );
894 textFrontTransform->setTranslation( translation );
895 textEntity->addComponent( textFrontTransform );
900void Qgs3DAxis::createAxis( Qt::Axis axisType )
902 float cylinderRadius = 0.05f * mCylinderLength;
903 float coneLength = 0.3f * mCylinderLength;
904 float coneBottomRadius = 0.1f * mCylinderLength;
906 QQuaternion rotation;
909 Qt3DExtras::QText2DEntity *text =
nullptr;
910 Qt3DCore::QTransform *textTransform =
nullptr;
915 case Qt::Axis::XAxis:
916 mTextX =
new Qt3DExtras::QText2DEntity( );
917 mTextX->setParent( mTwoDLabelSceneEntity );
918 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged,
this, &Qgs3DAxis::onTextXChanged );
919 mTextTransformX =
new Qt3DCore::QTransform();
920 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
922 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
925 textTransform = mTextTransformX;
926 name =
"3DAxis_axisX";
929 case Qt::Axis::YAxis:
930 mTextY =
new Qt3DExtras::QText2DEntity( );
931 mTextY->setParent( mTwoDLabelSceneEntity );
932 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged,
this, &Qgs3DAxis::onTextYChanged );
933 mTextTransformY =
new Qt3DCore::QTransform();
934 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
936 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 0.0f ), 0.0f );
939 textTransform = mTextTransformY;
940 name =
"3DAxis_axisY";
943 case Qt::Axis::ZAxis:
944 mTextZ =
new Qt3DExtras::QText2DEntity( );
945 mTextZ->setParent( mTwoDLabelSceneEntity );
946 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged,
this, &Qgs3DAxis::onTextZChanged );
947 mTextTransformZ =
new Qt3DCore::QTransform();
948 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
950 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
953 textTransform = mTextTransformZ;
954 name =
"3DAxis_axisZ";
962 Qt3DCore::QEntity *cylinder =
new Qt3DCore::QEntity( mAxisRoot );
963 cylinder->setObjectName( name );
965 Qt3DExtras::QCylinderMesh *cylinderMesh =
new Qt3DExtras::QCylinderMesh;
966 cylinderMesh->setRadius( cylinderRadius );
967 cylinderMesh->setLength( mCylinderLength );
968 cylinderMesh->setRings( 10 );
969 cylinderMesh->setSlices( 4 );
970 cylinder->addComponent( cylinderMesh );
972 Qt3DExtras::QPhongMaterial *cylinderMaterial =
new Qt3DExtras::QPhongMaterial( cylinder );
973 cylinderMaterial->setAmbient( color );
974 cylinderMaterial->setShininess( 0 );
975 cylinder->addComponent( cylinderMaterial );
977 Qt3DCore::QTransform *cylinderTransform =
new Qt3DCore::QTransform;
978 QMatrix4x4 transformMatrixCylinder;
979 transformMatrixCylinder.rotate( rotation );
980 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
981 cylinderTransform->setMatrix( transformMatrixCylinder );
982 cylinder->addComponent( cylinderTransform );
985 Qt3DCore::QEntity *coneEntity =
new Qt3DCore::QEntity( mAxisRoot );
986 coneEntity->setObjectName( name );
987 Qt3DExtras::QConeMesh *coneMesh =
new Qt3DExtras::QConeMesh;
988 coneMesh->setLength( coneLength );
989 coneMesh->setBottomRadius( coneBottomRadius );
990 coneMesh->setTopRadius( 0.0f );
991 coneMesh->setRings( 10 );
992 coneMesh->setSlices( 4 );
993 coneEntity->addComponent( coneMesh );
995 Qt3DExtras::QPhongMaterial *coneMaterial =
new Qt3DExtras::QPhongMaterial( coneEntity );
996 coneMaterial->setAmbient( color );
997 coneMaterial->setShininess( 0 );
998 coneEntity->addComponent( coneMaterial );
1000 Qt3DCore::QTransform *coneTransform =
new Qt3DCore::QTransform;
1001 QMatrix4x4 transformMatrixCone;
1002 transformMatrixCone.rotate( rotation );
1003 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
1004 coneTransform->setMatrix( transformMatrixCone );
1005 coneEntity->addComponent( coneTransform );
1008 text->setColor( QColor( 192, 192, 192, 192 ) );
1009 text->addComponent( textTransform );
1015 onAxisViewportSizeUpdate();
1018void Qgs3DAxis::onAxisViewportSizeUpdate(
int )
1022 double windowWidth = ( double )mParentWindow->width();
1023 double windowHeight = ( double )mParentWindow->height();
1028 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window w/h: %1px / %2px" )
1029 .arg( windowWidth ).arg( windowHeight ), 2 );
1030 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window physicalDpi %1 (%2, %3)" )
1031 .arg( mParentWindow->screen()->physicalDotsPerInch() )
1032 .arg( mParentWindow->screen()->physicalDotsPerInchX() )
1033 .arg( mParentWindow->screen()->physicalDotsPerInchY() ), 2 );
1034 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window logicalDotsPerInch %1 (%2, %3)" )
1035 .arg( mParentWindow->screen()->logicalDotsPerInch() )
1036 .arg( mParentWindow->screen()->logicalDotsPerInchX() )
1037 .arg( mParentWindow->screen()->logicalDotsPerInchY() ), 2 );
1039 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate window pixel ratio %1" )
1040 .arg( mParentWindow->screen()->devicePixelRatio() ), 2 );
1051 double defaultViewportPixelSize = ( ( double )settings.
defaultViewportSize() / 25.4 ) * 92.0;
1055 double viewportPixelSize = defaultViewportPixelSize + ( ( double )settings.
defaultViewportSize() / 25.4 )
1056 * ( mParentWindow->screen()->physicalDotsPerInch() - 92.0 ) * 0.7;
1057 QgsDebugMsgLevel( QString(
"onAxisViewportSizeUpdate viewportPixelSize %1" ).arg( viewportPixelSize ), 2 );
1058 double widthRatio = viewportPixelSize / windowWidth;
1059 double heightRatio = widthRatio * windowWidth / windowHeight;
1061 QgsDebugMsgLevel( QString(
"3DAxis viewport ratios width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1063 if ( heightRatio * windowHeight < viewportPixelSize )
1065 heightRatio = viewportPixelSize / windowHeight;
1066 widthRatio = heightRatio * windowHeight / windowWidth;
1067 QgsDebugMsgLevel( QString(
"3DAxis viewport, height too small, ratios adjusted to width: %1% / height: %2%" ).arg( widthRatio ).arg( heightRatio ), 2 );
1072 QgsDebugMsgLevel(
"viewport takes too much place into the 3d view, disabling it", 2 );
1074 mAxisViewport->setEnabled(
false );
1075 setEnableCube(
false );
1076 setEnableAxis(
false );
1081 mAxisScaleFactor = viewportPixelSize / defaultViewportPixelSize;
1082 QgsDebugMsgLevel( QString(
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
1084 if ( ! mAxisViewport->isEnabled() )
1087 setEnableAxis(
true );
1089 setEnableCube(
true );
1091 mAxisViewport->setEnabled(
true );
1093 float xRatio = 1.0f;
1094 float yRatio = 1.0f;
1098 xRatio = 0.5f - widthRatio / 2.0f;
1100 xRatio = 1.0f - widthRatio;
1104 else if ( settings.
verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
1105 yRatio = 0.5f - heightRatio / 2.0f;
1107 yRatio = 1.0f - heightRatio;
1109 QgsDebugMsgLevel( QString(
"Qgs3DAxis: update viewport: %1 x %2 x %3 x %4" ).arg( xRatio ).arg( yRatio ).arg( widthRatio ).arg( heightRatio ), 2 );
1110 mAxisViewport->setNormalizedRect( QRectF( xRatio, yRatio, widthRatio, heightRatio ) );
1114 mTwoDLabelCamera->lens()->setOrthographicProjection(
1115 -windowWidth / 2.0f, windowWidth / 2.0f,
1116 -windowHeight / 2.0f, windowHeight / 2.0f,
1117 mTwoDLabelCamera->lens()->nearPlane(), mTwoDLabelCamera->lens()->farPlane() );
1119 updateAxisLabelPosition();
1124void Qgs3DAxis::onCameraUpdate( )
1126 Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
1128 if ( parentCamera->viewVector() != mPreviousVector
1129 && !std::isnan( parentCamera->viewVector().x() )
1130 && !std::isnan( parentCamera->viewVector().y() )
1131 && !std::isnan( parentCamera->viewVector().z() ) )
1133 mPreviousVector = parentCamera->viewVector();
1134 QVector3D mainCameraShift = parentCamera->viewVector().normalized();
1135 float zy_swap = mainCameraShift.y();
1136 mainCameraShift.setY( mainCameraShift.z() );
1137 mainCameraShift.setZ( -zy_swap );
1138 mainCameraShift.setX( -mainCameraShift.x() );
1140 if ( mAxisCamera->projectionType() == Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection )
1142 mAxisCamera->setPosition( mainCameraShift );
1146 mAxisCamera->setPosition( mainCameraShift * mCylinderLength * 10.0 );
1149 if ( mAxisRoot->isEnabled() )
1151 updateAxisLabelPosition();
1156void Qgs3DAxis::updateAxisLabelPosition()
1158 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
1161 mAxisViewport, mTwoDLabelCamera, mTwoDLabelViewport,
1162 mParentWindow->size() ) );
1163 onTextXChanged( mTextX->text() );
1166 mAxisViewport, mTwoDLabelCamera, mTwoDLabelViewport,
1167 mParentWindow->size() ) );
1168 onTextYChanged( mTextY->text() );
1171 mAxisViewport, mTwoDLabelCamera, mTwoDLabelViewport,
1172 mParentWindow->size() ) );
1173 onTextZChanged( mTextZ->text() );
1177void Qgs3DAxis::onTextXChanged(
const QString &text )
1179 QFont f = QFont(
"monospace", mAxisScaleFactor * mFontSize );
1180 f.setWeight( QFont::Weight::Black );
1181 f.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
1182 mTextX->setFont( f );
1183 mTextX->setWidth( mAxisScaleFactor * mFontSize * text.length() );
1184 mTextX->setHeight( mAxisScaleFactor * mFontSize * 1.5f );
1187void Qgs3DAxis::onTextYChanged(
const QString &text )
1189 QFont f = QFont(
"monospace", mAxisScaleFactor * mFontSize );
1190 f.setWeight( QFont::Weight::Black );
1191 f.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
1192 mTextY->setFont( f );
1193 mTextY->setWidth( mAxisScaleFactor * mFontSize * text.length() );
1194 mTextY->setHeight( mAxisScaleFactor * mFontSize * 1.5f );
1197void Qgs3DAxis::onTextZChanged(
const QString &text )
1199 QFont f = QFont(
"monospace", mAxisScaleFactor * mFontSize );
1200 f.setWeight( QFont::Weight::Black );
1201 f.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
1202 mTextZ->setFont( f );
1203 mTextZ->setWidth( mAxisScaleFactor * mFontSize * text.length() );
1204 mTextZ->setHeight( mAxisScaleFactor * mFontSize * 1.5f );
1216 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
1217 mPositionAttribute->setBuffer( mVertexBuffer );
1218 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
1219 mPositionAttribute->setVertexSize( 3 );
1220 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
1223 mGeom->addAttribute( mPositionAttribute );
1225 setInstanceCount( 1 );
1226 setIndexOffset( 0 );
1227 setFirstInstance( 0 );
1228 setPrimitiveType( Qt3DRender::QGeometryRenderer::Lines );
1229 setGeometry( mGeom );
1236 QByteArray vertexBufferData;
1237 vertexBufferData.resize( vertices.size() * 3 *
sizeof(
float ) );
1238 float *rawVertexArray =
reinterpret_cast<float *
>( vertexBufferData.data() );
1240 for (
const QVector3D &v : std::as_const( vertices ) )
1242 rawVertexArray[idx++] = v.x();
1243 rawVertexArray[idx++] = v.y();
1244 rawVertexArray[idx++] = v.z();
1247 mVertexBuffer->setData( vertexBufferData );
1248 setVertexCount( vertices.count() );
Contains the configuration of a 3d axis.
void setMode(Qgs3DAxisSettings::Mode type)
Sets the type of the 3daxis.
double maxViewportRatio() const
Returns the maximal axis viewport ratio (see Qt3DRender::QViewport::normalizedRect())
Mode
Axis representation enum.
@ Crs
Respect CRS directions.
@ Cube
Abstract cube mode.
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
void setHorizontalPosition(Qt::AnchorPoint position)
Sets the horizontal position for the 3d axis.
int defaultViewportSize() const
Returns the default axis viewport size in millimeters.
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
void setVerticalPosition(Qt::AnchorPoint position)
Sets the vertical position for the 3d axis.
Qgs3DAxis(Qt3DExtras::Qt3DWindow *parentWindow, Qt3DCore::QEntity *parent3DScene, Qgs3DMapScene *mapScene, QgsCameraController *camera, Qgs3DMapSettings *map)
Default Qgs3DAxis constructor.
QVector3D from3DTo2DLabelPosition(const QVector3D &sourcePos, Qt3DRender::QCamera *sourceCamera, Qt3DRender::QViewport *sourceViewport, Qt3DRender::QCamera *destCamera, Qt3DRender::QViewport *destViewport, const QSize &destSize)
project a 3D position from sourceCamera (in sourceViewport) to a 2D position for destCamera (in destV...
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
QgsAbstract3DEngine * engine()
Returns the abstract 3D engine.
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be temporarily nullptr)
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
float terrainElevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down)
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.
~Qgs3DWiredMesh() override
Qgs3DWiredMesh(Qt3DCore::QNode *parent=nullptr)
Defaul Qgs3DWiredMesh constructor.
void setVertices(const QList< QVector3D > &vertices)
add or replace mesh vertices coordinates
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
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.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
static void warning(const QString &msg)
Goes to qWarning.
The QgsMapSettings class contains configuration for rendering of the map.
double dpiTarget() const
Returns the target DPI (dots per inch) to be taken into consideration when rendering.
float devicePixelRatio() const
Returns the device pixel ratio.
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
double z() const
Returns Z 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.
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs