QGIS API Documentation 3.43.0-Master (80be09f213e)
qgs3daxis.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3daxis.cpp
3 --------------------------------------
4 Date : March 2022
5 Copyright : (C) 2022 by Jean Felder
6 Email : jean dot felder at oslandia dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgs3daxis.h"
17#include "moc_qgs3daxis.cpp"
18
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>
26#include <QWidget>
27#include <QScreen>
28#include <QFontDatabase>
29#include <ctime>
30#include <QApplication>
31#include <QActionGroup>
32
33#include "qgs3dmapsettings.h"
34#include "qgs3dmapscene.h"
35#include "qgsterrainentity.h"
37#include "qgswindow3dengine.h"
39#include "qgs3dwiredmesh_p.h"
40#include "qgsframegraph.h"
42
43Qgs3DAxis::Qgs3DAxis( Qgs3DMapCanvas *canvas, Qt3DCore::QEntity *parent3DScene, Qgs3DMapScene *mapScene, //
44 QgsCameraController *cameraCtrl, Qgs3DMapSettings *map )
45 : QObject( canvas )
46 , mMapSettings( map )
47 , mCanvas( canvas )
48 , mMapScene( mapScene )
49 , mCameraController( cameraCtrl )
50 , mCrs( map->crs() )
51{
52 mMapScene->engine()->frameGraph()->registerRenderView( std::make_unique<Qgs3DAxisRenderView>( //
54 mCanvas, mCameraController, mMapSettings, //
55 this
56 ),
58
59 mRenderView = dynamic_cast<Qgs3DAxisRenderView *>( mMapScene->engine()->frameGraph()->renderView( QgsFrameGraph::AXIS3D_RENDERVIEW ) );
60 Q_ASSERT( mRenderView );
61 constructAxisScene( parent3DScene );
62 constructLabelsScene( parent3DScene );
63
64 mTwoDLabelSceneEntity->addComponent( mRenderView->labelLayer() );
65
66 connect( cameraCtrl, &QgsCameraController::cameraChanged, this, &Qgs3DAxis::onCameraUpdate );
67
68 createAxisScene();
69 onAxisViewportSizeUpdate();
70
71 init3DObjectPicking();
72}
73
75{
76 delete mMenu;
77 mMenu = nullptr;
78
79 // When an object (axis or cube) is not enabled. It is still present but it does not have a parent.
80 // In that case, it will never be automatically deleted. Therefore, it needs to be manually deleted.
81 // See setEnableCube() and setEnableAxis().
82 switch ( mMapSettings->get3DAxisSettings().mode() )
83 {
85 delete mCubeRoot;
86 mCubeRoot = nullptr;
87 break;
89 delete mAxisRoot;
90 mAxisRoot = nullptr;
91 break;
93 delete mAxisRoot;
94 mAxisRoot = nullptr;
95 delete mCubeRoot;
96 mCubeRoot = nullptr;
97 break;
98 }
99
100 // render view unregistration will be done by framegraph destructor!
101}
102
103void Qgs3DAxis::init3DObjectPicking()
104{
105 mDefaultPickingMethod = mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod();
106
107 // Create screencaster to be used by EventFilter:
108 // 1- Perform ray casting tests by specifying "touch" coordinates in screen space
109 // 2- connect screencaster results to onTouchedByRay
110 // 3- screencaster will be triggered by EventFilter
111 mScreenRayCaster = new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
112 mScreenRayCaster->addLayer( mRenderView->objectLayer() ); // to only filter on axis objects
113 mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
114 mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
115
116 mAxisSceneEntity->addComponent( mScreenRayCaster );
117
118 QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged, this, &Qgs3DAxis::onTouchedByRay );
119}
120
121// will be called by Qgs3DMapCanvas::eventFilter
122bool Qgs3DAxis::handleEvent( QEvent *event )
123{
124 if ( event->type() == QEvent::MouseButtonPress )
125 {
126 // register mouse click to detect dragging
127 mHasClicked = true;
128 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
129 mLastClickedPos = mouseEvent->pos();
130 }
131
132 // handle QEvent::MouseButtonRelease as it represents the end of click and QEvent::MouseMove.
133 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
134 {
135 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
136
137 // user has clicked and move ==> dragging start
138 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
139 {
140 mIsDragging = true;
141 }
142
143 // user has released ==> dragging ends
144 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
145 {
146 mIsDragging = false;
147 mHasClicked = false;
148 }
149
150 // user is moving or has released but not dragging
151 else if ( !mIsDragging )
152 {
153 // limit ray caster usage to the axis viewport
154 QPointF normalizedPos( static_cast<float>( mouseEvent->pos().x() ) / static_cast<float>( mCanvas->width() ), static_cast<float>( mouseEvent->pos().y() ) / static_cast<float>( mCanvas->height() ) );
155
156 if ( 2 <= QgsLogger::debugLevel() && event->type() == QEvent::MouseButtonRelease )
157 {
158 std::ostringstream os;
159 os << "QGS3DAxis: normalized pos: " << normalizedPos << " / viewport: " << mRenderView->viewport()->normalizedRect();
160 QgsDebugMsgLevel( os.str().c_str(), 2 );
161 }
162
163 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
164 {
165 mLastClickedButton = mouseEvent->button();
166 mLastClickedPos = mouseEvent->pos();
167
168 // if casted ray from pos matches an entity, call onTouchedByRay
169 mScreenRayCaster->trigger( mLastClickedPos );
170 }
171 // exit the viewport
172 else
173 {
174 // reset the mouse cursor if needed
175 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
176 {
177 mCanvas->setCursor( mPreviousCursor );
178 mPreviousCursor = Qt::ArrowCursor;
179 }
180
181 // reset the picking settings if needed
182 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
183 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
184 {
185 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
186 QgsDebugMsgLevel( "Disabling triangle picking", 2 );
187 }
188 }
189
190 mIsDragging = false; // drag ends
191 mHasClicked = false;
192 }
193 }
194
195 return false;
196}
197
198void Qgs3DAxis::onTouchedByRay( const Qt3DRender::QAbstractRayCaster::Hits &hits )
199{
200 int mHitsFound = -1;
201 if ( !hits.empty() )
202 {
203 if ( 2 <= QgsLogger::debugLevel() )
204 {
205 std::ostringstream os;
206 os << "Qgs3DAxis::onTouchedByRay " << hits.length() << " hits at pos " << mLastClickedPos << " with QButton: " << mLastClickedButton;
207 for ( int i = 0; i < hits.length(); ++i )
208 {
209 os << "\n";
210 os << "\tHit Type: " << hits.at( i ).type() << "\n";
211 os << "\tHit triangle id: " << hits.at( i ).primitiveIndex() << "\n";
212 os << "\tHit distance: " << hits.at( i ).distance() << "\n";
213 os << "\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
214 }
215 QgsDebugMsgLevel( os.str().c_str(), 2 );
216 }
217
218 for ( int i = 0; i < hits.length() && mHitsFound == -1; ++i )
219 {
220 if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
221 {
222 mHitsFound = i;
223 }
224 }
225 }
226
227 if ( mLastClickedButton == Qt::NoButton ) // hover
228 {
229 if ( mHitsFound != -1 )
230 {
231 if ( mCanvas->cursor() != Qt::ArrowCursor )
232 {
233 mPreviousCursor = mCanvas->cursor();
234 mCanvas->setCursor( Qt::ArrowCursor );
235 QgsDebugMsgLevel( "Enabling arrow cursor", 2 );
236
237 // The cube needs triangle picking to handle click on faces.
238 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
239 {
240 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
241 QgsDebugMsgLevel( "Enabling triangle picking", 2 );
242 }
243 }
244 }
245 }
246 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 ) // show menu
247 {
248 displayMenuAt( mLastClickedPos );
249 }
250 else if ( mLastClickedButton == Qt::MouseButton::LeftButton ) // handle cube face clicks
251 {
252 hideMenu();
253
254 if ( mHitsFound != -1 )
255 {
256 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
257 {
258 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
259 {
260 case 0: // "East face";
261 QgsDebugMsgLevel( "Qgs3DAxis: East face clicked", 2 );
262 mCameraController->rotateCameraToEast();
263 break;
264
265 case 1: // "West face ";
266 QgsDebugMsgLevel( "Qgs3DAxis: West face clicked", 2 );
267 mCameraController->rotateCameraToWest();
268 break;
269
270 case 2: // "North face ";
271 QgsDebugMsgLevel( "Qgs3DAxis: North face clicked", 2 );
272 mCameraController->rotateCameraToNorth();
273 break;
274
275 case 3: // "South face";
276 QgsDebugMsgLevel( "Qgs3DAxis: South face clicked", 2 );
277 mCameraController->rotateCameraToSouth();
278 break;
279
280 case 4: // "Top face ";
281 QgsDebugMsgLevel( "Qgs3DAxis: Top face clicked", 2 );
282 mCameraController->rotateCameraToTop();
283 break;
284
285 case 5: // "Bottom face ";
286 QgsDebugMsgLevel( "Qgs3DAxis: Bottom face clicked", 2 );
287 mCameraController->rotateCameraToBottom();
288 break;
289
290 default:
291 break;
292 }
293 }
294 }
295 }
296}
297
298void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
299{
300 mAxisSceneEntity = new Qt3DCore::QEntity;
301 mAxisSceneEntity->setParent( parent3DScene );
302 mAxisSceneEntity->setObjectName( "3DAxis_SceneEntity" );
303
304 mAxisCamera = mRenderView->objectCamera();
305 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
306 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
307 // position will be set later
308}
309
310void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
311{
312 mTwoDLabelSceneEntity = new Qt3DCore::QEntity;
313 mTwoDLabelSceneEntity->setParent( parent3DScene );
314 mTwoDLabelSceneEntity->setEnabled( true );
315
316 mTwoDLabelCamera = mRenderView->labelCamera();
317 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
318 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
319 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
320}
321
322QVector3D Qgs3DAxis::from3DTo2DLabelPosition( const QVector3D &sourcePos, Qt3DRender::QCamera *sourceCamera, Qt3DRender::QCamera *destCamera )
323{
324 const int viewportWidth = static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
325 const int viewportHeight = static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
326 QRect viewportRect( static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ), static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
327
328 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
329 destPos.setZ( 0.0f );
330 return destPos;
331}
332
333void Qgs3DAxis::setEnableCube( bool show )
334{
335 mCubeRoot->setEnabled( show );
336 if ( show )
337 {
338 mCubeRoot->setParent( mAxisSceneEntity );
339 }
340 else
341 {
342 mCubeRoot->setParent( static_cast<Qt3DCore::QEntity *>( nullptr ) );
343 }
344}
345
346void Qgs3DAxis::setEnableAxis( bool show )
347{
348 mAxisRoot->setEnabled( show );
349 if ( show )
350 {
351 mAxisRoot->setParent( mAxisSceneEntity );
352 }
353 else
354 {
355 mAxisRoot->setParent( static_cast<Qt3DCore::QEntity *>( nullptr ) );
356 }
357
358 mTextX->setEnabled( show );
359 mTextY->setEnabled( show );
360 mTextZ->setEnabled( show );
361}
362
363void Qgs3DAxis::createAxisScene()
364{
365 if ( !mAxisRoot || !mCubeRoot )
366 {
367 mAxisRoot = new Qt3DCore::QEntity;
368 mAxisRoot->setParent( mAxisSceneEntity );
369 mAxisRoot->setObjectName( "3DAxis_AxisRoot" );
370 mAxisRoot->addComponent( mRenderView->objectLayer() ); // raycaster will filter object containing this layer
371
372 createAxis( Qt::Axis::XAxis );
373 createAxis( Qt::Axis::YAxis );
374 createAxis( Qt::Axis::ZAxis );
375
376 mCubeRoot = new Qt3DCore::QEntity;
377 mCubeRoot->setParent( mAxisSceneEntity );
378 mCubeRoot->setObjectName( "3DAxis_CubeRoot" );
379 mCubeRoot->addComponent( mRenderView->objectLayer() ); // raycaster will filter object containing this layer
380
381 createCube();
382 }
383
384 Qgs3DAxisSettings::Mode mode = mMapSettings->get3DAxisSettings().mode();
385
386 if ( mode == Qgs3DAxisSettings::Mode::Off )
387 {
388 mAxisSceneEntity->setEnabled( false );
389 setEnableAxis( false );
390 setEnableCube( false );
391 mRenderView->setEnabled( false );
392 }
393 else
394 {
395 mRenderView->setEnabled( true );
396 mAxisSceneEntity->setEnabled( true );
397 if ( mode == Qgs3DAxisSettings::Mode::Crs )
398 {
399 setEnableCube( false );
400 setEnableAxis( true );
401
402 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
403
404 if ( axisDirections.length() > 0 )
405 mTextX->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 0 ) ) );
406 else
407 mTextX->setText( "X?" );
408
409 if ( axisDirections.length() > 1 )
410 mTextY->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 1 ) ) );
411 else
412 mTextY->setText( "Y?" );
413
414 if ( axisDirections.length() > 2 )
415 mTextZ->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 2 ) ) );
416 else
417 mTextZ->setText( QStringLiteral( "up" ) );
418 }
419 else if ( mode == Qgs3DAxisSettings::Mode::Cube )
420 {
421 setEnableCube( true );
422 setEnableAxis( false );
423 }
424 else
425 {
426 setEnableCube( false );
427 setEnableAxis( true );
428 mTextX->setText( "X?" );
429 mTextY->setText( "Y?" );
430 mTextZ->setText( "Z?" );
431 }
432
433 updateAxisLabelPosition();
434 }
435}
436
437void Qgs3DAxis::createMenu()
438{
439 mMenu = new QMenu();
440
441 // axis type menu
442 QAction *typeOffAct = new QAction( tr( "&Off" ), mMenu );
443 typeOffAct->setCheckable( true );
444 typeOffAct->setStatusTip( tr( "Disable 3D axis" ) );
445 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeOffAct, this]() {
446 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Off )
447 typeOffAct->setChecked( true );
448 } );
449
450 QAction *typeCrsAct = new QAction( tr( "Coordinate Reference &System" ), mMenu );
451 typeCrsAct->setCheckable( true );
452 typeCrsAct->setStatusTip( tr( "Coordinate Reference System 3D axis" ) );
453 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeCrsAct, this]() {
454 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Crs )
455 typeCrsAct->setChecked( true );
456 } );
457
458 QAction *typeCubeAct = new QAction( tr( "&Cube" ), mMenu );
459 typeCubeAct->setCheckable( true );
460 typeCubeAct->setStatusTip( tr( "Cube 3D axis" ) );
461 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeCubeAct, this]() {
462 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Cube )
463 typeCubeAct->setChecked( true );
464 } );
465
466 QActionGroup *typeGroup = new QActionGroup( mMenu );
467 typeGroup->addAction( typeOffAct );
468 typeGroup->addAction( typeCrsAct );
469 typeGroup->addAction( typeCubeAct );
470
471 connect( typeOffAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Off ); } );
472 connect( typeCrsAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Crs ); } );
473 connect( typeCubeAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Cube ); } );
474
475 QMenu *typeMenu = new QMenu( QStringLiteral( "Axis Type" ), mMenu );
476 Q_ASSERT( typeMenu );
477 typeMenu->addAction( typeOffAct );
478 typeMenu->addAction( typeCrsAct );
479 typeMenu->addAction( typeCubeAct );
480 mMenu->addMenu( typeMenu );
481
482 // horizontal position menu
483 QAction *hPosLeftAct = new QAction( tr( "&Left" ), mMenu );
484 hPosLeftAct->setCheckable( true );
485 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosLeftAct, this]() {
486 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
487 hPosLeftAct->setChecked( true );
488 } );
489
490 QAction *hPosMiddleAct = new QAction( tr( "&Center" ), mMenu );
491 hPosMiddleAct->setCheckable( true );
492 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosMiddleAct, this]() {
493 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
494 hPosMiddleAct->setChecked( true );
495 } );
496
497 QAction *hPosRightAct = new QAction( tr( "&Right" ), mMenu );
498 hPosRightAct->setCheckable( true );
499 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosRightAct, this]() {
500 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
501 hPosRightAct->setChecked( true );
502 } );
503
504 QActionGroup *hPosGroup = new QActionGroup( mMenu );
505 hPosGroup->addAction( hPosLeftAct );
506 hPosGroup->addAction( hPosMiddleAct );
507 hPosGroup->addAction( hPosRightAct );
508
509 connect( hPosLeftAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
510 connect( hPosMiddleAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
511 connect( hPosRightAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
512
513 QMenu *horizPosMenu = new QMenu( QStringLiteral( "Horizontal Position" ), mMenu );
514 horizPosMenu->addAction( hPosLeftAct );
515 horizPosMenu->addAction( hPosMiddleAct );
516 horizPosMenu->addAction( hPosRightAct );
517 mMenu->addMenu( horizPosMenu );
518
519 // vertical position menu
520 QAction *vPosTopAct = new QAction( tr( "&Top" ), mMenu );
521 vPosTopAct->setCheckable( true );
522 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosTopAct, this]() {
523 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
524 vPosTopAct->setChecked( true );
525 } );
526
527 QAction *vPosMiddleAct = new QAction( tr( "&Middle" ), mMenu );
528 vPosMiddleAct->setCheckable( true );
529 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosMiddleAct, this]() {
530 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
531 vPosMiddleAct->setChecked( true );
532 } );
533
534 QAction *vPosBottomAct = new QAction( tr( "&Bottom" ), mMenu );
535 vPosBottomAct->setCheckable( true );
536 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosBottomAct, this]() {
537 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
538 vPosBottomAct->setChecked( true );
539 } );
540
541 QActionGroup *vPosGroup = new QActionGroup( mMenu );
542 vPosGroup->addAction( vPosTopAct );
543 vPosGroup->addAction( vPosMiddleAct );
544 vPosGroup->addAction( vPosBottomAct );
545
546 connect( vPosTopAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
547 connect( vPosMiddleAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
548 connect( vPosBottomAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
549
550 QMenu *vertPosMenu = new QMenu( QStringLiteral( "Vertical Position" ), mMenu );
551 vertPosMenu->addAction( vPosTopAct );
552 vertPosMenu->addAction( vPosMiddleAct );
553 vertPosMenu->addAction( vPosBottomAct );
554 mMenu->addMenu( vertPosMenu );
555
556 // axis view menu
557 // Make sure to sync the key combinations with QgsCameraController::keyboardEventFilter()!
558 QAction *viewHomeAct = new QAction( tr( "&Home" ) + "\t Ctrl+5", mMenu );
559 QAction *viewTopAct = new QAction( tr( "&Top" ) + "\t Ctrl+9", mMenu );
560 QAction *viewNorthAct = new QAction( tr( "&North" ) + "\t Ctrl+8", mMenu );
561 QAction *viewEastAct = new QAction( tr( "&East" ) + "\t Ctrl+6", mMenu );
562 QAction *viewSouthAct = new QAction( tr( "&South" ) + "\t Ctrl+2", mMenu );
563 QAction *viewWestAct = new QAction( tr( "&West" ) + "\t Ctrl+4", mMenu );
564 QAction *viewBottomAct = new QAction( tr( "&Bottom" ) + "\t Ctrl+3", mMenu );
565
566 connect( viewHomeAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToHome );
567 connect( viewTopAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToTop );
568 connect( viewNorthAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToNorth );
569 connect( viewEastAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToEast );
570 connect( viewSouthAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToSouth );
571 connect( viewWestAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToWest );
572 connect( viewBottomAct, &QAction::triggered, mCameraController, &QgsCameraController::rotateCameraToBottom );
573
574 QMenu *viewMenu = new QMenu( QStringLiteral( "Camera View" ), mMenu );
575 viewMenu->addAction( viewHomeAct );
576 viewMenu->addAction( viewTopAct );
577 viewMenu->addAction( viewNorthAct );
578 viewMenu->addAction( viewEastAct );
579 viewMenu->addAction( viewSouthAct );
580 viewMenu->addAction( viewWestAct );
581 viewMenu->addAction( viewBottomAct );
582 mMenu->addMenu( viewMenu );
583
584 // update checkable items
585 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(), true );
586}
587
588void Qgs3DAxis::hideMenu()
589{
590 if ( mMenu && mMenu->isVisible() )
591 mMenu->hide();
592}
593
594void Qgs3DAxis::displayMenuAt( const QPoint &sourcePos )
595{
596 if ( !mMenu )
597 {
598 createMenu();
599 }
600
601 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
602}
603
604void Qgs3DAxis::onAxisModeChanged( Qgs3DAxisSettings::Mode mode )
605{
606 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
607 s.setMode( mode );
608 mMapSettings->set3DAxisSettings( s );
609}
610
611void Qgs3DAxis::createCube()
612{
613 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
614
615 // cube outlines
616 Qt3DCore::QEntity *cubeLineEntity = new Qt3DCore::QEntity( mCubeRoot );
617 cubeLineEntity->setObjectName( "3DAxis_cubeline" );
618 Qgs3DWiredMesh *cubeLine = new Qgs3DWiredMesh;
619 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
620 cubeLine->setVertices( box.verticesForLines() );
621 cubeLineEntity->addComponent( cubeLine );
622
623 Qt3DExtras::QPhongMaterial *cubeLineMaterial = new Qt3DExtras::QPhongMaterial;
624 cubeLineMaterial->setAmbient( Qt::white );
625 cubeLineEntity->addComponent( cubeLineMaterial );
626
627 // cube mesh
628 Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
629 cubeMesh->setObjectName( "3DAxis_cubemesh" );
630 cubeMesh->setXExtent( mCylinderLength );
631 cubeMesh->setYExtent( mCylinderLength );
632 cubeMesh->setZExtent( mCylinderLength );
633 mCubeRoot->addComponent( cubeMesh );
634
635 Qt3DExtras::QPhongMaterial *cubeMaterial = new Qt3DExtras::QPhongMaterial( mCubeRoot );
636 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
637 cubeMaterial->setShininess( 100 );
638 mCubeRoot->addComponent( cubeMaterial );
639
640 Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform;
641 QMatrix4x4 transformMatrixcube;
642 //transformMatrixcube.rotate( rotation );
643 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
644 cubeTransform->setMatrix( transformMatrixcube );
645 mCubeRoot->addComponent( cubeTransform );
646
647 // text
648 QString text;
649 const int fontSize = static_cast<int>( std::round( 0.75f * static_cast<float>( mFontSize ) ) );
650 const float textHeight = static_cast<float>( fontSize ) * 1.5f;
651 float textWidth;
652 const QFont font = createFont( fontSize );
653
654 {
655 text = QStringLiteral( "top" );
656 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
657 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
658 QMatrix4x4 rotation;
659 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
660 }
661
662 {
663 text = QStringLiteral( "btm" );
664 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
665 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
666 QMatrix4x4 rotation;
667 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
668 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
669 }
670
671 {
672 text = QStringLiteral( "west" );
673 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
674 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
675 QMatrix4x4 rotation;
676 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
677 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
678 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
679 }
680
681 {
682 text = QStringLiteral( "east" );
683 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
684 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
685 QMatrix4x4 rotation;
686 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
687 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
688 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
689 }
690
691 {
692 text = QStringLiteral( "south" );
693 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
694 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
695 QMatrix4x4 rotation;
696 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
697 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
698 }
699
700 {
701 text = QStringLiteral( "north" );
702 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
703 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
704 QMatrix4x4 rotation;
705 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
706 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
707 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
708 }
709
710 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
711 {
712 l->setParent( mCubeRoot );
713 }
714}
715
716Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText( const QString &text, float textHeight, float textWidth, const QFont &font, const QMatrix4x4 &rotation, const QVector3D &translation )
717{
718 Qt3DExtras::QText2DEntity *textEntity = new Qt3DExtras::QText2DEntity;
719 textEntity->setObjectName( "3DAxis_cube_label_" + text );
720 textEntity->setFont( font );
721 textEntity->setHeight( textHeight );
722 textEntity->setWidth( textWidth );
723 textEntity->setColor( QColor( 192, 192, 192 ) );
724 textEntity->setText( text );
725
726 Qt3DCore::QTransform *textFrontTransform = new Qt3DCore::QTransform();
727 textFrontTransform->setMatrix( rotation );
728 textFrontTransform->setTranslation( translation );
729 textEntity->addComponent( textFrontTransform );
730
731 return textEntity;
732}
733
734void Qgs3DAxis::createAxis( Qt::Axis axisType )
735{
736 float cylinderRadius = 0.05f * mCylinderLength;
737 float coneLength = 0.3f * mCylinderLength;
738 float coneBottomRadius = 0.1f * mCylinderLength;
739
740 QQuaternion rotation;
741 QColor color;
742
743 Qt3DExtras::QText2DEntity *text = nullptr;
744 Qt3DCore::QTransform *textTransform = nullptr;
745 QString name;
746
747 switch ( axisType )
748 {
749 case Qt::Axis::XAxis:
750 mTextX = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
751 mTextX->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
752 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
753 updateAxisLabelText( mTextX, text );
754 } );
755 mTextTransformX = new Qt3DCore::QTransform();
756 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
757
758 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
759 color = Qt::red;
760 text = mTextX;
761 textTransform = mTextTransformX;
762 name = "3DAxis_axisX";
763 break;
764
765 case Qt::Axis::YAxis:
766 mTextY = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
767 mTextY->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
768 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
769 updateAxisLabelText( mTextY, text );
770 } );
771 mTextTransformY = new Qt3DCore::QTransform();
772 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
773
774 // no rotation
775
776 color = Qt::green;
777 text = mTextY;
778 textTransform = mTextTransformY;
779 name = "3DAxis_axisY";
780 break;
781
782 case Qt::Axis::ZAxis:
783 mTextZ = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
784 mTextZ->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
785 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
786 updateAxisLabelText( mTextZ, text );
787 } );
788 mTextTransformZ = new Qt3DCore::QTransform();
789 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
790
791 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
792 color = Qt::blue;
793 text = mTextZ;
794 textTransform = mTextTransformZ;
795 name = "3DAxis_axisZ";
796 break;
797
798 default:
799 return;
800 }
801
802 // cylinder
803 Qt3DCore::QEntity *cylinder = new Qt3DCore::QEntity( mAxisRoot );
804 cylinder->setObjectName( name );
805
806 Qt3DExtras::QCylinderMesh *cylinderMesh = new Qt3DExtras::QCylinderMesh;
807 cylinderMesh->setRadius( cylinderRadius );
808 cylinderMesh->setLength( mCylinderLength );
809 cylinderMesh->setRings( 10 );
810 cylinderMesh->setSlices( 4 );
811 cylinder->addComponent( cylinderMesh );
812
813 Qt3DExtras::QPhongMaterial *cylinderMaterial = new Qt3DExtras::QPhongMaterial( cylinder );
814 cylinderMaterial->setAmbient( color );
815 cylinderMaterial->setShininess( 0 );
816 cylinder->addComponent( cylinderMaterial );
817
818 Qt3DCore::QTransform *cylinderTransform = new Qt3DCore::QTransform;
819 QMatrix4x4 transformMatrixCylinder;
820 transformMatrixCylinder.rotate( rotation );
821 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
822 cylinderTransform->setMatrix( transformMatrixCylinder );
823 cylinder->addComponent( cylinderTransform );
824
825 // cone
826 Qt3DCore::QEntity *coneEntity = new Qt3DCore::QEntity( mAxisRoot );
827 coneEntity->setObjectName( name );
828 Qt3DExtras::QConeMesh *coneMesh = new Qt3DExtras::QConeMesh;
829 coneMesh->setLength( coneLength );
830 coneMesh->setBottomRadius( coneBottomRadius );
831 coneMesh->setTopRadius( 0.0f );
832 coneMesh->setRings( 10 );
833 coneMesh->setSlices( 4 );
834 coneEntity->addComponent( coneMesh );
835
836 Qt3DExtras::QPhongMaterial *coneMaterial = new Qt3DExtras::QPhongMaterial( coneEntity );
837 coneMaterial->setAmbient( color );
838 coneMaterial->setShininess( 0 );
839 coneEntity->addComponent( coneMaterial );
840
841 Qt3DCore::QTransform *coneTransform = new Qt3DCore::QTransform;
842 QMatrix4x4 transformMatrixCone;
843 transformMatrixCone.rotate( rotation );
844 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
845 coneTransform->setMatrix( transformMatrixCone );
846 coneEntity->addComponent( coneTransform );
847
848 // text font, height and width will be set later in onText?Changed
849 text->setColor( QColor( 192, 192, 192, 192 ) );
850 text->addComponent( textTransform );
851}
852
854{
855 createAxisScene();
856 onAxisViewportSizeUpdate();
857}
858
859void Qgs3DAxis::onAxisViewportSizeUpdate()
860{
861 mRenderView->onViewportSizeUpdate(); // will call onViewportScaleFactorChanged as callback
862
863 // mRenderView->onViewportSizeUpdate() has updated `mTwoDLabelCamera` lens parameters.
864 // The position of the labels needs to be updated.
865 const Qgs3DAxisSettings axisSettings = mMapSettings->get3DAxisSettings();
866 if ( axisSettings.mode() == Qgs3DAxisSettings::Mode::Crs && mAxisRoot->isEnabled() )
867 {
868 updateAxisLabelPosition();
869 }
870}
871
873{
874 // if the axis scene has not been created, don't do anything
875 if ( !mAxisRoot || !mCubeRoot )
876 {
877 return;
878 }
879
880 if ( scaleFactor > 0.0 )
881 {
882 Qgs3DAxisSettings settings = mMapSettings->get3DAxisSettings();
883 if ( settings.mode() == Qgs3DAxisSettings::Mode::Crs )
884 setEnableAxis( true );
885 else if ( settings.mode() == Qgs3DAxisSettings::Mode::Cube )
886 setEnableCube( true );
887
888 mAxisScaleFactor = scaleFactor;
889 QgsDebugMsgLevel( QString( "3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
890 }
891 else
892 {
893 setEnableCube( false );
894 setEnableAxis( false );
895 }
896}
897
898void Qgs3DAxis::onCameraUpdate()
899{
900 Qt3DRender::QCamera *parentCamera = mCameraController->camera();
901
902 if ( parentCamera->viewVector() != mPreviousVector
903 && !std::isnan( parentCamera->viewVector().x() )
904 && !std::isnan( parentCamera->viewVector().y() )
905 && !std::isnan( parentCamera->viewVector().z() ) )
906 {
907 mPreviousVector = parentCamera->viewVector();
908
909 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
910 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
911 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
912
913 if ( mAxisRoot->isEnabled() )
914 {
915 updateAxisLabelPosition();
916 }
917 }
918}
919
920void Qgs3DAxis::updateAxisLabelPosition()
921{
922 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
923 {
924 mTextTransformX->setTranslation( from3DTo2DLabelPosition( mTextCoordX * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
925 updateAxisLabelText( mTextX, mTextX->text() );
926
927 mTextTransformY->setTranslation( from3DTo2DLabelPosition( mTextCoordY * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
928 updateAxisLabelText( mTextY, mTextY->text() );
929
930 mTextTransformZ->setTranslation( from3DTo2DLabelPosition( mTextCoordZ * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
931 updateAxisLabelText( mTextZ, mTextZ->text() );
932 }
933}
934
935void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity, const QString &text )
936{
937 const float scaledFontSize = static_cast<float>( mAxisScaleFactor ) * static_cast<float>( mFontSize );
938 const QFont font = createFont( static_cast<int>( std::round( scaledFontSize ) ) );
939 textEntity->setFont( font );
940 textEntity->setWidth( scaledFontSize * static_cast<float>( text.length() ) );
941 textEntity->setHeight( 1.5f * scaledFontSize );
942}
943
944QFont Qgs3DAxis::createFont( int pointSize )
945{
946 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
947 font.setPointSize( pointSize );
948 font.setWeight( QFont::Weight::Black );
949 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
950 return font;
951}
3D axis render view.
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.
~Qgs3DAxis() override
Definition qgs3daxis.cpp:74
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.
Definition qgs3daxis.cpp:43
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.
Definition of the world.
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
void set3DAxisSettings(const Qgs3DAxisSettings &axisSettings, bool force=false)
Sets the current configuration of 3d axis.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
Axis-aligned bounding box - in world coords.
Definition qgsaabb.h:35
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
Definition qgsaabb.cpp:59
QgsFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
virtual void setEnabled(bool enable)
Enable or disable via enable the render view sub tree.
Object that controls camera movement based on user input.
void rotateCameraToBottom()
Rotate to bottom-up view.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void rotateCameraToTop()
Rotate to top-down view.
void rotateCameraToEast()
Rotate to view from the east.
void rotateCameraToHome()
Rotate to diagonal view.
void rotateCameraToNorth()
Rotate to view from the north.
void rotateCameraToWest()
Rotate to view from the west.
void cameraChanged()
Emitted when camera has been updated.
void rotateCameraToSouth()
Rotate to view from the south.
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
bool registerRenderView(std::unique_ptr< QgsAbstractRenderView > renderView, const QString &name, Qt3DRender::QFrameGraphNode *topNode=nullptr)
Registers a new the render view renderView with name name.
QgsAbstractRenderView * renderView(const QString &name)
Returns the render view named name, if any.
static const QString AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
Definition qgslogger.h:112
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
const QgsCoordinateReferenceSystem & crs