QGIS API Documentation 3.43.0-Master (58029bba303)
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::ShortcutOverride )
125 {
126 return handleKeyEvent( static_cast<QKeyEvent *>( event ) );
127 }
128
129 if ( event->type() == QEvent::MouseButtonPress )
130 {
131 // register mouse click to detect dragging
132 mHasClicked = true;
133 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
134 mLastClickedPos = mouseEvent->pos();
135 }
136
137 // handle QEvent::MouseButtonRelease as it represents the end of click and QEvent::MouseMove.
138 else if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
139 {
140 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>( event );
141
142 // user has clicked and move ==> dragging start
143 if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
144 {
145 mIsDragging = true;
146 }
147
148 // user has released ==> dragging ends
149 else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
150 {
151 mIsDragging = false;
152 mHasClicked = false;
153 }
154
155 // user is moving or has released but not dragging
156 else if ( !mIsDragging )
157 {
158 // limit ray caster usage to the axis viewport
159 QPointF normalizedPos( static_cast<float>( mouseEvent->pos().x() ) / static_cast<float>( mCanvas->width() ), static_cast<float>( mouseEvent->pos().y() ) / static_cast<float>( mCanvas->height() ) );
160
161 if ( 2 <= QgsLogger::debugLevel() && event->type() == QEvent::MouseButtonRelease )
162 {
163 std::ostringstream os;
164 os << "QGS3DAxis: normalized pos: " << normalizedPos << " / viewport: " << mRenderView->viewport()->normalizedRect();
165 QgsDebugMsgLevel( os.str().c_str(), 2 );
166 }
167
168 if ( mRenderView->viewport()->normalizedRect().contains( normalizedPos ) )
169 {
170 mLastClickedButton = mouseEvent->button();
171 mLastClickedPos = mouseEvent->pos();
172
173 // if casted ray from pos matches an entity, call onTouchedByRay
174 mScreenRayCaster->trigger( mLastClickedPos );
175 }
176 // exit the viewport
177 else
178 {
179 // reset the mouse cursor if needed
180 if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
181 {
182 mCanvas->setCursor( mPreviousCursor );
183 mPreviousCursor = Qt::ArrowCursor;
184 }
185
186 // reset the picking settings if needed
187 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
188 && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
189 {
190 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
191 QgsDebugMsgLevel( "Disabling triangle picking", 2 );
192 }
193 }
194
195 mIsDragging = false; // drag ends
196 mHasClicked = false;
197 }
198 }
199
200 return false;
201}
202
203void Qgs3DAxis::onTouchedByRay( const Qt3DRender::QAbstractRayCaster::Hits &hits )
204{
205 int mHitsFound = -1;
206 if ( !hits.empty() )
207 {
208 if ( 2 <= QgsLogger::debugLevel() )
209 {
210 std::ostringstream os;
211 os << "Qgs3DAxis::onTouchedByRay " << hits.length() << " hits at pos " << mLastClickedPos << " with QButton: " << mLastClickedButton;
212 for ( int i = 0; i < hits.length(); ++i )
213 {
214 os << "\n";
215 os << "\tHit Type: " << hits.at( i ).type() << "\n";
216 os << "\tHit triangle id: " << hits.at( i ).primitiveIndex() << "\n";
217 os << "\tHit distance: " << hits.at( i ).distance() << "\n";
218 os << "\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
219 }
220 QgsDebugMsgLevel( os.str().c_str(), 2 );
221 }
222
223 for ( int i = 0; i < hits.length() && mHitsFound == -1; ++i )
224 {
225 if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
226 {
227 mHitsFound = i;
228 }
229 }
230 }
231
232 if ( mLastClickedButton == Qt::NoButton ) // hover
233 {
234 if ( mHitsFound != -1 )
235 {
236 if ( mCanvas->cursor() != Qt::ArrowCursor )
237 {
238 mPreviousCursor = mCanvas->cursor();
239 mCanvas->setCursor( Qt::ArrowCursor );
240 QgsDebugMsgLevel( "Enabling arrow cursor", 2 );
241
242 // The cube needs triangle picking to handle click on faces.
243 if ( mMapScene->engine()->renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
244 {
245 mMapScene->engine()->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
246 QgsDebugMsgLevel( "Enabling triangle picking", 2 );
247 }
248 }
249 }
250 }
251 else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 ) // show menu
252 {
253 displayMenuAt( mLastClickedPos );
254 }
255 else if ( mLastClickedButton == Qt::MouseButton::LeftButton ) // handle cube face clicks
256 {
257 hideMenu();
258
259 if ( mHitsFound != -1 )
260 {
261 if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
262 {
263 switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
264 {
265 case 0: // "East face";
266 QgsDebugMsgLevel( "Qgs3DAxis: East face clicked", 2 );
267 onCameraViewChangeEast();
268 break;
269
270 case 1: // "West face ";
271 QgsDebugMsgLevel( "Qgs3DAxis: West face clicked", 2 );
272 onCameraViewChangeWest();
273 break;
274
275 case 2: // "North face ";
276 QgsDebugMsgLevel( "Qgs3DAxis: North face clicked", 2 );
277 onCameraViewChangeNorth();
278 break;
279
280 case 3: // "South face";
281 QgsDebugMsgLevel( "Qgs3DAxis: South face clicked", 2 );
282 onCameraViewChangeSouth();
283 break;
284
285 case 4: // "Top face ";
286 QgsDebugMsgLevel( "Qgs3DAxis: Top face clicked", 2 );
287 onCameraViewChangeTop();
288 break;
289
290 case 5: // "Bottom face ";
291 QgsDebugMsgLevel( "Qgs3DAxis: Bottom face clicked", 2 );
292 onCameraViewChangeBottom();
293 break;
294
295 default:
296 break;
297 }
298 }
299 }
300 }
301}
302
303void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
304{
305 mAxisSceneEntity = new Qt3DCore::QEntity;
306 mAxisSceneEntity->setParent( parent3DScene );
307 mAxisSceneEntity->setObjectName( "3DAxis_SceneEntity" );
308
309 mAxisCamera = mRenderView->objectCamera();
310 mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
311 mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
312 // position will be set later
313}
314
315void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
316{
317 mTwoDLabelSceneEntity = new Qt3DCore::QEntity;
318 mTwoDLabelSceneEntity->setParent( parent3DScene );
319 mTwoDLabelSceneEntity->setEnabled( true );
320
321 mTwoDLabelCamera = mRenderView->labelCamera();
322 mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
323 mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
324 mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
325}
326
327QVector3D Qgs3DAxis::from3DTo2DLabelPosition( const QVector3D &sourcePos, Qt3DRender::QCamera *sourceCamera, Qt3DRender::QCamera *destCamera )
328{
329 const int viewportWidth = static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
330 const int viewportHeight = static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
331 QRect viewportRect( static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ), static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
332
333 QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
334 destPos.setZ( 0.0f );
335 return destPos;
336}
337
338void Qgs3DAxis::setEnableCube( bool show )
339{
340 mCubeRoot->setEnabled( show );
341 if ( show )
342 {
343 mCubeRoot->setParent( mAxisSceneEntity );
344 }
345 else
346 {
347 mCubeRoot->setParent( static_cast<Qt3DCore::QEntity *>( nullptr ) );
348 }
349}
350
351void Qgs3DAxis::setEnableAxis( bool show )
352{
353 mAxisRoot->setEnabled( show );
354 if ( show )
355 {
356 mAxisRoot->setParent( mAxisSceneEntity );
357 }
358 else
359 {
360 mAxisRoot->setParent( static_cast<Qt3DCore::QEntity *>( nullptr ) );
361 }
362
363 mTextX->setEnabled( show );
364 mTextY->setEnabled( show );
365 mTextZ->setEnabled( show );
366}
367
368void Qgs3DAxis::createAxisScene()
369{
370 if ( !mAxisRoot || !mCubeRoot )
371 {
372 mAxisRoot = new Qt3DCore::QEntity;
373 mAxisRoot->setParent( mAxisSceneEntity );
374 mAxisRoot->setObjectName( "3DAxis_AxisRoot" );
375 mAxisRoot->addComponent( mRenderView->objectLayer() ); // raycaster will filter object containing this layer
376
377 createAxis( Qt::Axis::XAxis );
378 createAxis( Qt::Axis::YAxis );
379 createAxis( Qt::Axis::ZAxis );
380
381 mCubeRoot = new Qt3DCore::QEntity;
382 mCubeRoot->setParent( mAxisSceneEntity );
383 mCubeRoot->setObjectName( "3DAxis_CubeRoot" );
384 mCubeRoot->addComponent( mRenderView->objectLayer() ); // raycaster will filter object containing this layer
385
386 createCube();
387 }
388
389 Qgs3DAxisSettings::Mode mode = mMapSettings->get3DAxisSettings().mode();
390
391 if ( mode == Qgs3DAxisSettings::Mode::Off )
392 {
393 mAxisSceneEntity->setEnabled( false );
394 setEnableAxis( false );
395 setEnableCube( false );
396 mRenderView->setEnabled( false );
397 }
398 else
399 {
400 mRenderView->setEnabled( true );
401 mAxisSceneEntity->setEnabled( true );
402 if ( mode == Qgs3DAxisSettings::Mode::Crs )
403 {
404 setEnableCube( false );
405 setEnableAxis( true );
406
407 const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.axisOrdering();
408
409 if ( axisDirections.length() > 0 )
410 mTextX->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 0 ) ) );
411 else
412 mTextX->setText( "X?" );
413
414 if ( axisDirections.length() > 1 )
415 mTextY->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 1 ) ) );
416 else
417 mTextY->setText( "Y?" );
418
419 if ( axisDirections.length() > 2 )
420 mTextZ->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 2 ) ) );
421 else
422 mTextZ->setText( QStringLiteral( "up" ) );
423 }
424 else if ( mode == Qgs3DAxisSettings::Mode::Cube )
425 {
426 setEnableCube( true );
427 setEnableAxis( false );
428 }
429 else
430 {
431 setEnableCube( false );
432 setEnableAxis( true );
433 mTextX->setText( "X?" );
434 mTextY->setText( "Y?" );
435 mTextZ->setText( "Z?" );
436 }
437
438 updateAxisLabelPosition();
439 }
440}
441
442bool Qgs3DAxis::handleKeyEvent( QKeyEvent *keyEvent )
443{
444 bool ret = false;
445 if ( keyEvent->modifiers() | Qt::ControlModifier )
446 {
447 ret = true;
448 switch ( keyEvent->key() )
449 {
450 case Qt::Key_8:
451 onCameraViewChangeNorth();
452 break;
453
454 case Qt::Key_6:
455 onCameraViewChangeEast();
456 break;
457
458 case Qt::Key_2:
459 onCameraViewChangeSouth();
460 break;
461
462 case Qt::Key_4:
463 onCameraViewChangeWest();
464 break;
465
466 case Qt::Key_9:
467 onCameraViewChangeTop();
468 break;
469
470 case Qt::Key_3:
471 onCameraViewChangeBottom();
472 break;
473
474 case Qt::Key_5:
475 onCameraViewChangeHome();
476 break;
477
478 default:
479 ret = false;
480 break;
481 }
482 }
483 return ret;
484}
485
486void Qgs3DAxis::createMenu()
487{
488 mMenu = new QMenu();
489
490 // axis type menu
491 QAction *typeOffAct = new QAction( tr( "&Off" ), mMenu );
492 typeOffAct->setCheckable( true );
493 typeOffAct->setStatusTip( tr( "Disable 3D axis" ) );
494 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeOffAct, this]() {
495 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Off )
496 typeOffAct->setChecked( true );
497 } );
498
499 QAction *typeCrsAct = new QAction( tr( "Coordinate Reference &System" ), mMenu );
500 typeCrsAct->setCheckable( true );
501 typeCrsAct->setStatusTip( tr( "Coordinate Reference System 3D axis" ) );
502 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeCrsAct, this]() {
503 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Crs )
504 typeCrsAct->setChecked( true );
505 } );
506
507 QAction *typeCubeAct = new QAction( tr( "&Cube" ), mMenu );
508 typeCubeAct->setCheckable( true );
509 typeCubeAct->setStatusTip( tr( "Cube 3D axis" ) );
510 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [typeCubeAct, this]() {
511 if ( mMapSettings->get3DAxisSettings().mode() == Qgs3DAxisSettings::Mode::Cube )
512 typeCubeAct->setChecked( true );
513 } );
514
515 QActionGroup *typeGroup = new QActionGroup( mMenu );
516 typeGroup->addAction( typeOffAct );
517 typeGroup->addAction( typeCrsAct );
518 typeGroup->addAction( typeCubeAct );
519
520 connect( typeOffAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Off ); } );
521 connect( typeCrsAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Crs ); } );
522 connect( typeCubeAct, &QAction::triggered, this, [this]( bool ) { onAxisModeChanged( Qgs3DAxisSettings::Mode::Cube ); } );
523
524 QMenu *typeMenu = new QMenu( QStringLiteral( "Axis Type" ), mMenu );
525 Q_ASSERT( typeMenu );
526 typeMenu->addAction( typeOffAct );
527 typeMenu->addAction( typeCrsAct );
528 typeMenu->addAction( typeCubeAct );
529 mMenu->addMenu( typeMenu );
530
531 // horizontal position menu
532 QAction *hPosLeftAct = new QAction( tr( "&Left" ), mMenu );
533 hPosLeftAct->setCheckable( true );
534 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosLeftAct, this]() {
535 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorLeft )
536 hPosLeftAct->setChecked( true );
537 } );
538
539 QAction *hPosMiddleAct = new QAction( tr( "&Center" ), mMenu );
540 hPosMiddleAct->setCheckable( true );
541 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosMiddleAct, this]() {
542 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorHorizontalCenter )
543 hPosMiddleAct->setChecked( true );
544 } );
545
546 QAction *hPosRightAct = new QAction( tr( "&Right" ), mMenu );
547 hPosRightAct->setCheckable( true );
548 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [hPosRightAct, this]() {
549 if ( mMapSettings->get3DAxisSettings().horizontalPosition() == Qt::AnchorPoint::AnchorRight )
550 hPosRightAct->setChecked( true );
551 } );
552
553 QActionGroup *hPosGroup = new QActionGroup( mMenu );
554 hPosGroup->addAction( hPosLeftAct );
555 hPosGroup->addAction( hPosMiddleAct );
556 hPosGroup->addAction( hPosRightAct );
557
558 connect( hPosLeftAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
559 connect( hPosMiddleAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
560 connect( hPosRightAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
561
562 QMenu *horizPosMenu = new QMenu( QStringLiteral( "Horizontal Position" ), mMenu );
563 horizPosMenu->addAction( hPosLeftAct );
564 horizPosMenu->addAction( hPosMiddleAct );
565 horizPosMenu->addAction( hPosRightAct );
566 mMenu->addMenu( horizPosMenu );
567
568 // vertical position menu
569 QAction *vPosTopAct = new QAction( tr( "&Top" ), mMenu );
570 vPosTopAct->setCheckable( true );
571 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosTopAct, this]() {
572 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorTop )
573 vPosTopAct->setChecked( true );
574 } );
575
576 QAction *vPosMiddleAct = new QAction( tr( "&Middle" ), mMenu );
577 vPosMiddleAct->setCheckable( true );
578 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosMiddleAct, this]() {
579 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorVerticalCenter )
580 vPosMiddleAct->setChecked( true );
581 } );
582
583 QAction *vPosBottomAct = new QAction( tr( "&Bottom" ), mMenu );
584 vPosBottomAct->setCheckable( true );
585 connect( mMapSettings, &Qgs3DMapSettings::axisSettingsChanged, this, [vPosBottomAct, this]() {
586 if ( mMapSettings->get3DAxisSettings().verticalPosition() == Qt::AnchorPoint::AnchorBottom )
587 vPosBottomAct->setChecked( true );
588 } );
589
590 QActionGroup *vPosGroup = new QActionGroup( mMenu );
591 vPosGroup->addAction( vPosTopAct );
592 vPosGroup->addAction( vPosMiddleAct );
593 vPosGroup->addAction( vPosBottomAct );
594
595 connect( vPosTopAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
596 connect( vPosMiddleAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
597 connect( vPosBottomAct, &QAction::triggered, this, [this]( bool ) { mRenderView->onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
598
599 QMenu *vertPosMenu = new QMenu( QStringLiteral( "Vertical Position" ), mMenu );
600 vertPosMenu->addAction( vPosTopAct );
601 vertPosMenu->addAction( vPosMiddleAct );
602 vertPosMenu->addAction( vPosBottomAct );
603 mMenu->addMenu( vertPosMenu );
604
605 // axis view menu
606 QAction *viewHomeAct = new QAction( tr( "&Home" ) + "\t Ctrl+1", mMenu );
607 QAction *viewTopAct = new QAction( tr( "&Top" ) + "\t Ctrl+5", mMenu );
608 QAction *viewNorthAct = new QAction( tr( "&North" ) + "\t Ctrl+8", mMenu );
609 QAction *viewEastAct = new QAction( tr( "&East" ) + "\t Ctrl+6", mMenu );
610 QAction *viewSouthAct = new QAction( tr( "&South" ) + "\t Ctrl+2", mMenu );
611 QAction *viewWestAct = new QAction( tr( "&West" ) + "\t Ctrl+4", mMenu );
612 QAction *viewBottomAct = new QAction( tr( "&Bottom" ), mMenu );
613
614 connect( viewHomeAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeHome );
615 connect( viewTopAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeTop );
616 connect( viewNorthAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeNorth );
617 connect( viewEastAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeEast );
618 connect( viewSouthAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeSouth );
619 connect( viewWestAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeWest );
620 connect( viewBottomAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeBottom );
621
622 QMenu *viewMenu = new QMenu( QStringLiteral( "Camera View" ), mMenu );
623 viewMenu->addAction( viewHomeAct );
624 viewMenu->addAction( viewTopAct );
625 viewMenu->addAction( viewNorthAct );
626 viewMenu->addAction( viewEastAct );
627 viewMenu->addAction( viewSouthAct );
628 viewMenu->addAction( viewWestAct );
629 viewMenu->addAction( viewBottomAct );
630 mMenu->addMenu( viewMenu );
631
632 // update checkable items
633 mMapSettings->set3DAxisSettings( mMapSettings->get3DAxisSettings(), true );
634}
635
636void Qgs3DAxis::hideMenu()
637{
638 if ( mMenu && mMenu->isVisible() )
639 mMenu->hide();
640}
641
642void Qgs3DAxis::displayMenuAt( const QPoint &sourcePos )
643{
644 if ( !mMenu )
645 {
646 createMenu();
647 }
648
649 mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
650}
651
652void Qgs3DAxis::onAxisModeChanged( Qgs3DAxisSettings::Mode mode )
653{
654 Qgs3DAxisSettings s = mMapSettings->get3DAxisSettings();
655 s.setMode( mode );
656 mMapSettings->set3DAxisSettings( s );
657}
658
659void Qgs3DAxis::onCameraViewChange( float pitch, float yaw )
660{
661 QgsVector3D pos = mCameraController->lookingAtPoint();
662 double elevation = 0.0;
663 if ( mMapSettings->terrainRenderingEnabled() )
664 {
665 QgsDebugMsgLevel( "Checking elevation from terrain...", 2 );
666 QVector3D camPos = mCameraController->camera()->position();
667 QgsRayCastingUtils::Ray3D ray( camPos, pos.toVector3D() - camPos, mCameraController->camera()->farPlane() );
668 const QVector<QgsRayCastingUtils::RayHit> hits = mMapScene->terrainEntity()->rayIntersection( ray, QgsRayCastingUtils::RayCastContext() );
669 if ( !hits.isEmpty() )
670 {
671 elevation = hits.at( 0 ).pos.z();
672 QgsDebugMsgLevel( QString( "Computed elevation from terrain: %1" ).arg( elevation ), 2 );
673 }
674 else
675 {
676 QgsDebugMsgLevel( "Unable to obtain elevation from terrain", 2 );
677 }
678 }
679 pos.set( pos.x(), pos.y(), elevation + mMapSettings->terrainSettings()->elevationOffset() );
680
681 mCameraController->setLookingAtPoint( pos, ( mCameraController->camera()->position() - pos.toVector3D() ).length(), pitch, yaw );
682}
683
684
685void Qgs3DAxis::createCube()
686{
687 QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
688
689 // cube outlines
690 Qt3DCore::QEntity *cubeLineEntity = new Qt3DCore::QEntity( mCubeRoot );
691 cubeLineEntity->setObjectName( "3DAxis_cubeline" );
692 Qgs3DWiredMesh *cubeLine = new Qgs3DWiredMesh;
693 QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
694 cubeLine->setVertices( box.verticesForLines() );
695 cubeLineEntity->addComponent( cubeLine );
696
697 Qt3DExtras::QPhongMaterial *cubeLineMaterial = new Qt3DExtras::QPhongMaterial;
698 cubeLineMaterial->setAmbient( Qt::white );
699 cubeLineEntity->addComponent( cubeLineMaterial );
700
701 // cube mesh
702 Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
703 cubeMesh->setObjectName( "3DAxis_cubemesh" );
704 cubeMesh->setXExtent( mCylinderLength );
705 cubeMesh->setYExtent( mCylinderLength );
706 cubeMesh->setZExtent( mCylinderLength );
707 mCubeRoot->addComponent( cubeMesh );
708
709 Qt3DExtras::QPhongMaterial *cubeMaterial = new Qt3DExtras::QPhongMaterial( mCubeRoot );
710 cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
711 cubeMaterial->setShininess( 100 );
712 mCubeRoot->addComponent( cubeMaterial );
713
714 Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform;
715 QMatrix4x4 transformMatrixcube;
716 //transformMatrixcube.rotate( rotation );
717 transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
718 cubeTransform->setMatrix( transformMatrixcube );
719 mCubeRoot->addComponent( cubeTransform );
720
721 // text
722 QString text;
723 const int fontSize = static_cast<int>( std::round( 0.75f * static_cast<float>( mFontSize ) ) );
724 const float textHeight = static_cast<float>( fontSize ) * 1.5f;
725 float textWidth;
726 const QFont font = createFont( fontSize );
727
728 {
729 text = QStringLiteral( "top" );
730 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
731 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
732 QMatrix4x4 rotation;
733 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
734 }
735
736 {
737 text = QStringLiteral( "btm" );
738 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
739 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
740 QMatrix4x4 rotation;
741 rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
742 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
743 }
744
745 {
746 text = QStringLiteral( "west" );
747 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
748 QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
749 QMatrix4x4 rotation;
750 rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
751 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
752 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
753 }
754
755 {
756 text = QStringLiteral( "east" );
757 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
758 QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
759 QMatrix4x4 rotation;
760 rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
761 rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
762 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
763 }
764
765 {
766 text = QStringLiteral( "south" );
767 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
768 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
769 QMatrix4x4 rotation;
770 rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
771 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
772 }
773
774 {
775 text = QStringLiteral( "north" );
776 textWidth = static_cast<float>( text.length() * fontSize ) * 0.75f;
777 QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
778 QMatrix4x4 rotation;
779 rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
780 rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
781 mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
782 }
783
784 for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
785 {
786 l->setParent( mCubeRoot );
787 }
788}
789
790Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText( const QString &text, float textHeight, float textWidth, const QFont &font, const QMatrix4x4 &rotation, const QVector3D &translation )
791{
792 Qt3DExtras::QText2DEntity *textEntity = new Qt3DExtras::QText2DEntity;
793 textEntity->setObjectName( "3DAxis_cube_label_" + text );
794 textEntity->setFont( font );
795 textEntity->setHeight( textHeight );
796 textEntity->setWidth( textWidth );
797 textEntity->setColor( QColor( 192, 192, 192 ) );
798 textEntity->setText( text );
799
800 Qt3DCore::QTransform *textFrontTransform = new Qt3DCore::QTransform();
801 textFrontTransform->setMatrix( rotation );
802 textFrontTransform->setTranslation( translation );
803 textEntity->addComponent( textFrontTransform );
804
805 return textEntity;
806}
807
808void Qgs3DAxis::createAxis( Qt::Axis axisType )
809{
810 float cylinderRadius = 0.05f * mCylinderLength;
811 float coneLength = 0.3f * mCylinderLength;
812 float coneBottomRadius = 0.1f * mCylinderLength;
813
814 QQuaternion rotation;
815 QColor color;
816
817 Qt3DExtras::QText2DEntity *text = nullptr;
818 Qt3DCore::QTransform *textTransform = nullptr;
819 QString name;
820
821 switch ( axisType )
822 {
823 case Qt::Axis::XAxis:
824 mTextX = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
825 mTextX->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
826 connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
827 updateAxisLabelText( mTextX, text );
828 } );
829 mTextTransformX = new Qt3DCore::QTransform();
830 mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
831
832 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
833 color = Qt::red;
834 text = mTextX;
835 textTransform = mTextTransformX;
836 name = "3DAxis_axisX";
837 break;
838
839 case Qt::Axis::YAxis:
840 mTextY = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
841 mTextY->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
842 connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
843 updateAxisLabelText( mTextY, text );
844 } );
845 mTextTransformY = new Qt3DCore::QTransform();
846 mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
847
848 // no rotation
849
850 color = Qt::green;
851 text = mTextY;
852 textTransform = mTextTransformY;
853 name = "3DAxis_axisY";
854 break;
855
856 case Qt::Axis::ZAxis:
857 mTextZ = new Qt3DExtras::QText2DEntity(); // object initialization in two step:
858 mTextZ->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
859 connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString &text ) {
860 updateAxisLabelText( mTextZ, text );
861 } );
862 mTextTransformZ = new Qt3DCore::QTransform();
863 mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
864
865 rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
866 color = Qt::blue;
867 text = mTextZ;
868 textTransform = mTextTransformZ;
869 name = "3DAxis_axisZ";
870 break;
871
872 default:
873 return;
874 }
875
876 // cylinder
877 Qt3DCore::QEntity *cylinder = new Qt3DCore::QEntity( mAxisRoot );
878 cylinder->setObjectName( name );
879
880 Qt3DExtras::QCylinderMesh *cylinderMesh = new Qt3DExtras::QCylinderMesh;
881 cylinderMesh->setRadius( cylinderRadius );
882 cylinderMesh->setLength( mCylinderLength );
883 cylinderMesh->setRings( 10 );
884 cylinderMesh->setSlices( 4 );
885 cylinder->addComponent( cylinderMesh );
886
887 Qt3DExtras::QPhongMaterial *cylinderMaterial = new Qt3DExtras::QPhongMaterial( cylinder );
888 cylinderMaterial->setAmbient( color );
889 cylinderMaterial->setShininess( 0 );
890 cylinder->addComponent( cylinderMaterial );
891
892 Qt3DCore::QTransform *cylinderTransform = new Qt3DCore::QTransform;
893 QMatrix4x4 transformMatrixCylinder;
894 transformMatrixCylinder.rotate( rotation );
895 transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
896 cylinderTransform->setMatrix( transformMatrixCylinder );
897 cylinder->addComponent( cylinderTransform );
898
899 // cone
900 Qt3DCore::QEntity *coneEntity = new Qt3DCore::QEntity( mAxisRoot );
901 coneEntity->setObjectName( name );
902 Qt3DExtras::QConeMesh *coneMesh = new Qt3DExtras::QConeMesh;
903 coneMesh->setLength( coneLength );
904 coneMesh->setBottomRadius( coneBottomRadius );
905 coneMesh->setTopRadius( 0.0f );
906 coneMesh->setRings( 10 );
907 coneMesh->setSlices( 4 );
908 coneEntity->addComponent( coneMesh );
909
910 Qt3DExtras::QPhongMaterial *coneMaterial = new Qt3DExtras::QPhongMaterial( coneEntity );
911 coneMaterial->setAmbient( color );
912 coneMaterial->setShininess( 0 );
913 coneEntity->addComponent( coneMaterial );
914
915 Qt3DCore::QTransform *coneTransform = new Qt3DCore::QTransform;
916 QMatrix4x4 transformMatrixCone;
917 transformMatrixCone.rotate( rotation );
918 transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
919 coneTransform->setMatrix( transformMatrixCone );
920 coneEntity->addComponent( coneTransform );
921
922 // text font, height and width will be set later in onText?Changed
923 text->setColor( QColor( 192, 192, 192, 192 ) );
924 text->addComponent( textTransform );
925}
926
928{
929 createAxisScene();
930 onAxisViewportSizeUpdate();
931}
932
933void Qgs3DAxis::onAxisViewportSizeUpdate()
934{
935 mRenderView->onViewportSizeUpdate(); // will call onViewportScaleFactorChanged as callback
936
937 // mRenderView->onViewportSizeUpdate() has updated `mTwoDLabelCamera` lens parameters.
938 // The position of the labels needs to be updated.
939 const Qgs3DAxisSettings axisSettings = mMapSettings->get3DAxisSettings();
940 if ( axisSettings.mode() == Qgs3DAxisSettings::Mode::Crs && mAxisRoot->isEnabled() )
941 {
942 updateAxisLabelPosition();
943 }
944}
945
947{
948 // if the axis scene has not been created, don't do anything
949 if ( !mAxisRoot || !mCubeRoot )
950 {
951 return;
952 }
953
954 if ( scaleFactor > 0.0 )
955 {
956 Qgs3DAxisSettings settings = mMapSettings->get3DAxisSettings();
957 if ( settings.mode() == Qgs3DAxisSettings::Mode::Crs )
958 setEnableAxis( true );
959 else if ( settings.mode() == Qgs3DAxisSettings::Mode::Cube )
960 setEnableCube( true );
961
962 mAxisScaleFactor = scaleFactor;
963 QgsDebugMsgLevel( QString( "3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
964 }
965 else
966 {
967 setEnableCube( false );
968 setEnableAxis( false );
969 }
970}
971
972void Qgs3DAxis::onCameraUpdate()
973{
974 Qt3DRender::QCamera *parentCamera = mCameraController->camera();
975
976 if ( parentCamera->viewVector() != mPreviousVector
977 && !std::isnan( parentCamera->viewVector().x() )
978 && !std::isnan( parentCamera->viewVector().y() )
979 && !std::isnan( parentCamera->viewVector().z() ) )
980 {
981 mPreviousVector = parentCamera->viewVector();
982
983 QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
984 mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
985 mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
986
987 if ( mAxisRoot->isEnabled() )
988 {
989 updateAxisLabelPosition();
990 }
991 }
992}
993
994void Qgs3DAxis::updateAxisLabelPosition()
995{
996 if ( mTextTransformX && mTextTransformY && mTextTransformZ )
997 {
998 mTextTransformX->setTranslation( from3DTo2DLabelPosition( mTextCoordX * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
999 updateAxisLabelText( mTextX, mTextX->text() );
1000
1001 mTextTransformY->setTranslation( from3DTo2DLabelPosition( mTextCoordY * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1002 updateAxisLabelText( mTextY, mTextY->text() );
1003
1004 mTextTransformZ->setTranslation( from3DTo2DLabelPosition( mTextCoordZ * static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
1005 updateAxisLabelText( mTextZ, mTextZ->text() );
1006 }
1007}
1008
1009void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity, const QString &text )
1010{
1011 const float scaledFontSize = static_cast<float>( mAxisScaleFactor ) * static_cast<float>( mFontSize );
1012 const QFont font = createFont( static_cast<int>( std::round( scaledFontSize ) ) );
1013 textEntity->setFont( font );
1014 textEntity->setWidth( scaledFontSize * static_cast<float>( text.length() ) );
1015 textEntity->setHeight( 1.5f * scaledFontSize );
1016}
1017
1018QFont Qgs3DAxis::createFont( int pointSize )
1019{
1020 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1021 font.setPointSize( pointSize );
1022 font.setWeight( QFont::Weight::Black );
1023 font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
1024 return font;
1025}
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.
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be nullptr if using globe scene, terrain rendering is disabled or when te...
Definition of the world.
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
void set3DAxisSettings(const Qgs3DAxisSettings &axisSettings, bool force=false)
Sets the current configuration of 3d axis.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
Axis-aligned bounding box - in world coords.
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.
double elevationOffset() const
Returns the elevation offset of the terrain (used to move the terrain up or down).
Object that controls camera movement based on user input.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void cameraChanged()
Emitted when camera has been updated.
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
bool registerRenderView(std::unique_ptr< QgsAbstractRenderView > renderView, const QString &name, Qt3DRender::QFrameGraphNode *topNode=nullptr)
Registers a new the render view renderView with name name.
QgsAbstractRenderView * renderView(const QString &name)
Returns the render view named name, if any.
static const QString AXIS3D_RENDERVIEW
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
Definition qgslogger.h:112
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:47
void set(double x, double y, double z)
Sets vector coordinates.
Definition qgsvector3d.h:72
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
const QgsCoordinateReferenceSystem & crs
Helper struct to store ray casting parameters.