QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
18 #include <Qt3DCore/QTransform>
19 #include <Qt3DRender/QAttribute>
20 #include <Qt3DExtras/QCylinderMesh>
21 #include <Qt3DExtras/QPhongMaterial>
22 #include <Qt3DExtras/QConeMesh>
23 #include <Qt3DRender/qcameralens.h>
24 #include <Qt3DRender/QCameraSelector>
25 #include <Qt3DRender/QClearBuffers>
26 #include <Qt3DRender/QLayer>
27 #include <Qt3DRender/QLayerFilter>
28 #include <Qt3DRender/QPointLight>
29 #include <QShortcut>
30 #include <QFontDatabase>
31 #include <ctime>
32 
33 #include "qgs3dmapscene.h"
34 #include "qgsterrainentity_p.h"
35 #include "qgs3dmapsettings.h"
38 #include "qgswindow3dengine.h"
39 
40 Qgs3DAxis::Qgs3DAxis( Qt3DExtras::Qt3DWindow *parentWindow,
41  Qt3DCore::QEntity *parent3DScene,
42  Qgs3DMapScene *mapScene,
43  QgsCameraController *cameraCtrl,
44  Qgs3DMapSettings *map )
45  : QObject( parentWindow )
46  , mMapSettings( map )
47  , mParentWindow( parentWindow )
48  , mMapScene( mapScene )
49  , mCameraController( cameraCtrl )
50  , mCrs( map->crs() )
51 {
52  mAxisViewport = constructAxisViewport( parent3DScene );
53  mAxisViewport->setParent( mParentWindow->activeFrameGraph() );
54 
55  mTwoDLabelViewport = constructLabelViewport( parent3DScene, QRectF( 0.0f, 0.0f, 1.0f, 1.0f ) );
56  mTwoDLabelViewport->setParent( mParentWindow->activeFrameGraph() );
57 
58  Qgs3DAxisSettings s = mMapSettings->get3dAxisSettings();
59  setAxisViewportPosition( mAxisViewportSize, s.verticalPosition(), s.horizontalPosition() );
60  mMode = s.mode();
61 
62  connect( cameraCtrl, &QgsCameraController::cameraChanged, this, &Qgs3DAxis::onCameraUpdate );
63  connect( mParentWindow, &Qt3DExtras::Qt3DWindow::widthChanged, this, &Qgs3DAxis::onAxisViewportSizeUpdate );
64  connect( mParentWindow, &Qt3DExtras::Qt3DWindow::heightChanged, this, &Qgs3DAxis::onAxisViewportSizeUpdate );
65 
66  onAxisViewportSizeUpdate();
67 
68  createAxisScene();
69 
70  mMenu = new QMenu();
71  connect( mMenu, &QMenu::aboutToShow, this, &Qgs3DAxis::populateMenu );
72 
73  init3DObjectPicking();
74 }
75 
77 {
78  delete mMenu;
79  mMenu = nullptr;
80 }
81 
82 void Qgs3DAxis::init3DObjectPicking( )
83 {
84  // Create screencaster to be used by EventFilter:
85  // 1- Perform ray casting tests by specifying "touch" coordinates in screen space
86  // 2- connect screencaster results to onTouchedByRay
87  // 3- screencaster will be triggered by EventFilter
88  mScreenRayCaster = new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
89  mScreenRayCaster->addLayer( mAxisSceneLayer ); // to only filter on axis objects
90  mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
91  mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
92  mAxisSceneEntity->addComponent( mScreenRayCaster );
93  QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged, this, &Qgs3DAxis::onTouchedByRay );
94 
95  // we need event filter (see Qgs3DAxis::eventFilter) to handle the mouse click event as this event is not catchable via the Qt3DRender::QObjectPicker
96  mParentWindow->installEventFilter( this );
97 }
98 
99 bool Qgs3DAxis::eventFilter( QObject *watched, QEvent *event )
100 {
101  if ( watched != mParentWindow )
102  return false;
103 
104  // handle QEvent::MouseButtonRelease as it represents the end of click and QEvent::MouseMove.
105  if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove )
106  {
107  QMouseEvent *lastClickEvent = static_cast<QMouseEvent *>( event );
108  // limit ray caster usage to the axis viewport
109  QPointF normalizedPos( ( float )lastClickEvent->pos().x() / mParentWindow->width(),
110  ( float )lastClickEvent->pos().y() / mParentWindow->height() );
111 #ifdef DEBUG
112  if ( event->type() == QEvent::MouseButtonRelease )
113  qDebug() << "normalized pos:" << normalizedPos << "/ viewport:" << mAxisViewport->normalizedRect();
114 #endif
115 
116  if ( mAxisViewport->normalizedRect().contains( normalizedPos ) )
117  {
118  mLastClickedButton = lastClickEvent->button();
119  mLastClickedPos = lastClickEvent->pos();
120 
121  // if casted ray from pos matches an entity, call onTouchedByRay
122  mScreenRayCaster->trigger( mLastClickedPos );
123  }
124 
125  // when we exit the viewport, reset the mouse cursor if needed
126  else if ( mPreviousCursor != Qt::ArrowCursor && mParentWindow->cursor() == Qt::ArrowCursor )
127  {
128  mParentWindow->setCursor( mPreviousCursor );
129  mPreviousCursor = Qt::ArrowCursor;
130  }
131  }
132 
133  return false;
134 }
135 
136 void Qgs3DAxis::onTouchedByRay( const Qt3DRender::QAbstractRayCaster::Hits &hits )
137 {
138  int mHitsFound = -1;
139  if ( !hits.empty() )
140  {
141 #ifdef DEBUG
142  qDebug() << hits.length() << "hit(s) at " << mLastClickedPos << "with" << mLastClickedButton;
143  for ( int i = 0; i < hits.length(); ++i )
144  {
145  qDebug() << "\tHit Type: " << hits.at( i ).type();
146  qDebug() << "\tHit triangle id: " << hits.at( i ).primitiveIndex();
147  qDebug() << "\tHit distance: " << hits.at( i ).distance();
148  qDebug() << "\tHit entity name: " << hits.at( i ).entity()->objectName();
149  }
150 #endif
151 
152  for ( int i = 0; i < hits.length() && mHitsFound == -1; ++i )
153  {
154  if ( hits.at( i ).distance() < 500.0 && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
155  {
156  mHitsFound = i;
157  }
158  }
159  }
160 
161  if ( mLastClickedButton == Qt::NoButton ) // hover
162  {
163  if ( mHitsFound != -1 )
164  {
165  if ( mParentWindow->cursor() != Qt::ArrowCursor )
166  {
167  mPreviousCursor = mParentWindow->cursor();
168  mParentWindow->setCursor( Qt::ArrowCursor );
169  }
170  }
171  }
172  else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 ) // show menu
173  {
174  displayMenuAt( mLastClickedPos );
175  }
176  else if ( mLastClickedButton == Qt::MouseButton::LeftButton ) // handle cube face clicks
177  {
178  if ( mMenu->isVisible() )
179  hideMenu();
180 
181  if ( mHitsFound != -1 )
182  {
183  if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
184  {
185 #ifdef DEBUG
186  switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
187  {
188  case 0:
189  qDebug() << "East face";
190  break;
191 
192  case 1:
193  qDebug() << "West face ";
194  break;
195 
196  case 2:
197  qDebug() << "North face ";
198  break;
199 
200  case 3:
201  qDebug() << "South face";
202  break;
203 
204  case 4:
205  qDebug() << "Top face ";
206  break;
207 
208  case 5:
209  qDebug() << "Bottom face ";
210  break;
211  }
212 #endif
213  switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
214  {
215  case 0: // "East face";
216  onCameraViewChangeEast();
217  break;
218 
219  case 1: // "West face ";
220  onCameraViewChangeWest();
221  break;
222 
223  case 2: // "North face ";
224  onCameraViewChangeNorth();
225  break;
226 
227  case 3: // "South face";
228  onCameraViewChangeSouth();
229  break;
230 
231  case 4: // "Top face ";
232  onCameraViewChangeTop();
233  break;
234 
235  case 5: // "Bottom face ";
236  onCameraViewChangeBottom();
237  break;
238 
239  default:
240  break;
241  }
242 
243  mParentWindow->requestUpdate();
244  }
245  }
246  }
247 }
248 
249 Qt3DRender::QViewport *Qgs3DAxis::constructAxisViewport( Qt3DCore::QEntity *parent3DScene )
250 {
251  Qt3DRender::QViewport *axisViewport = new Qt3DRender::QViewport;
252  // parent will be set later
253  // size will be set later
254 
255  mAxisSceneLayer = new Qt3DRender::QLayer;
256 
257  mAxisSceneEntity = new Qt3DCore::QEntity;
258  mAxisSceneEntity->setParent( parent3DScene );
259  mAxisSceneEntity->setObjectName( "3DAxis_SceneEntity" );
260 
261  mAxisCamera = new Qt3DRender::QCamera;
262  mAxisCamera->setParent( mAxisSceneEntity );
263  mAxisCamera->setProjectionType( mCameraController->camera()->projectionType() );
264  mAxisCamera->lens()->setFieldOfView( mCameraController->camera()->lens()->fieldOfView() * 0.5 );
265 
266  mAxisCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
267  mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
268  // position will be set later
269 
270  Qt3DRender::QLayer *axisLayer = new Qt3DRender::QLayer;
271  axisLayer->setRecursive( true );
272  mAxisSceneEntity->addComponent( axisLayer );
273 
274  Qt3DRender::QLayerFilter *axisLayerFilter = new Qt3DRender::QLayerFilter( axisViewport );
275  axisLayerFilter->addLayer( axisLayer );
276 
277  Qt3DRender::QCameraSelector *axisCameraSelector = new Qt3DRender::QCameraSelector;
278  axisCameraSelector->setParent( axisLayerFilter );
279  axisCameraSelector->setCamera( mAxisCamera );
280 
281  Qt3DRender::QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers( axisCameraSelector );
282  clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
283 
284  // cppcheck-suppress memleak
285  return axisViewport;
286 }
287 
288 Qt3DRender::QViewport *Qgs3DAxis::constructLabelViewport( Qt3DCore::QEntity *parent3DScene, const QRectF &parentViewportSize )
289 {
290  Qt3DRender::QViewport *twoDViewport = new Qt3DRender::QViewport;
291  // parent will be set later
292  twoDViewport->setNormalizedRect( parentViewportSize );
293 
294  mTwoDLabelSceneEntity = new Qt3DCore::QEntity;
295  mTwoDLabelSceneEntity->setParent( parent3DScene );
296  mTwoDLabelSceneEntity->setEnabled( true );
297 
298  mTwoDLabelCamera = new Qt3DRender::QCamera;
299  mTwoDLabelCamera->setParent( mTwoDLabelSceneEntity );
300  mTwoDLabelCamera->setProjectionType( Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection );
301  mTwoDLabelCamera->lens()->setOrthographicProjection(
302  -mParentWindow->width() / 2.0f, mParentWindow->width() / 2.0f,
303  -mParentWindow->height() / 2.0f, mParentWindow->height() / 2.0f,
304  -10.0f, 100.0f );
305 
306  mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
307  mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
308 
309  mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
310 
311  Qt3DRender::QLayer *twoDLayer = new Qt3DRender::QLayer;
312  twoDLayer->setRecursive( true );
313  mTwoDLabelSceneEntity->addComponent( twoDLayer );
314 
315  Qt3DRender::QLayerFilter *twoDLayerFilter = new Qt3DRender::QLayerFilter( twoDViewport );
316  twoDLayerFilter->addLayer( twoDLayer );
317 
318  Qt3DRender::QCameraSelector *twoDCameraSelector = new Qt3DRender::QCameraSelector;
319  twoDCameraSelector->setParent( twoDLayerFilter );
320  twoDCameraSelector->setCamera( mTwoDLabelCamera );
321 
322  Qt3DRender::QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers( twoDCameraSelector );
323  clearBuffers->setBuffers( Qt3DRender::QClearBuffers::DepthBuffer );
324 
325  // cppcheck-suppress memleak
326  return twoDViewport;
327 }
328 
329 QVector3D Qgs3DAxis::from3dTo2dLabelPosition( const QVector3D &sourcePos,
330  Qt3DRender::QCamera *sourceCamera, Qt3DRender::QViewport *sourceViewport,
331  Qt3DRender::QCamera *destCamera, Qt3DRender::QViewport *destViewport,
332  const QSize &destSize )
333 {
334  QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(),
335  destCamera->projectionMatrix(),
336  QRect( 0.0f, 0.0f,
337  destViewport->normalizedRect().width() * destSize.width(),
338  destViewport->normalizedRect().height() * destSize.height() ) );
339  QPointF axisCenter = sourceViewport->normalizedRect().center();
340  QPointF labelCenter = destViewport->normalizedRect().center();
341  QVector3D viewTranslation = QVector3D( ( axisCenter - labelCenter ).x() * destSize.width(),
342  ( axisCenter - labelCenter ).y() * destSize.height(),
343  0.0 );
344  destPos -= QVector3D( labelCenter.x() * destSize.width(),
345  labelCenter.y() * destSize.height(),
346  0.0f );
347  destPos.setX( destPos.x() + viewTranslation.x() );
348  destPos.setY( destPos.y() - viewTranslation.y() );
349  destPos.setZ( 0.0f );
350 
351 #ifdef DEBUG
352  qDebug() << "from3dTo2dLabelPosition: sourcePos" << sourcePos << " with" << viewTranslation << "corrected destPos" << destPos;
353 #endif
354  return destPos;
355 }
356 
357 void Qgs3DAxis::setEnableCube( bool show )
358 {
359  mCubeRoot->setEnabled( show );
360 }
361 
362 void Qgs3DAxis::setEnableAxis( bool show )
363 {
364  mAxisRoot->setEnabled( show );
365  mTextX->setEnabled( show );
366  mTextY->setEnabled( show );
367  mTextZ->setEnabled( show );
368 }
369 
370 void Qgs3DAxis::createAxisScene()
371 {
372  if ( mAxisRoot == nullptr || mCubeRoot == nullptr )
373  {
374 #ifdef DEBUG
375  qDebug() << "Should recreate mAxisRoot" << mMode;
376 #endif
377  mAxisRoot = new Qt3DCore::QEntity;
378  mAxisRoot->setParent( mAxisSceneEntity );
379  mAxisRoot->setObjectName( "3DAxis_AxisRoot" );
380  mAxisRoot->addComponent( mAxisSceneLayer );
381 
382  createAxis( Qt::Axis::XAxis );
383  createAxis( Qt::Axis::YAxis );
384  createAxis( Qt::Axis::ZAxis );
385 
386  mCubeRoot = new Qt3DCore::QEntity;
387  mCubeRoot->setParent( mAxisSceneEntity );
388  mCubeRoot->setObjectName( "3DAxis_CubeRoot" );
389  mCubeRoot->addComponent( mAxisSceneLayer );
390 
391  createCube( );
392  }
393 
394  if ( mMode == Mode::Off )
395  {
396  mAxisSceneEntity->setEnabled( false );
397  setEnableAxis( false );
398  setEnableCube( false );
399  }
400  else
401  {
402  mAxisSceneEntity->setEnabled( true );
403  if ( mMode == Mode::Crs )
404  {
405  setEnableCube( false );
406  setEnableAxis( true );
407 
408  const QList< Qgis::CrsAxisDirection > axisDirections = mCrs.axisOrdering();
409 
410  if ( axisDirections.length() > 0 )
411  mTextX->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 0 ) ) );
412  else
413  mTextY->setText( "X?" );
414 
415  if ( axisDirections.length() > 1 )
416  mTextY->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 1 ) ) );
417  else
418  mTextY->setText( "Y?" );
419 
420  if ( axisDirections.length() > 2 )
421  mTextZ->setText( QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString( axisDirections.at( 2 ) ) );
422  else
423  mTextZ->setText( QStringLiteral( "up" ) );
424  }
425  else if ( mMode == Mode::Cube )
426  {
427  setEnableCube( true );
428  setEnableAxis( false );
429  }
430  else
431  {
432  setEnableCube( false );
433  setEnableAxis( true );
434  mTextX->setText( "X?" );
435  mTextY->setText( "Y?" );
436  mTextZ->setText( "Z?" );
437  }
438 
439  updateAxisLabelPosition();
440  }
441 }
442 
443 void Qgs3DAxis::populateMenu()
444 {
445  mMenu->clear();
446 
447  // axis type menu
448  QAction *typeOffAct = new QAction( tr( "&Off" ), mMenu );
449  typeOffAct->setCheckable( true );
450  typeOffAct->setStatusTip( tr( "Disable 3D axis" ) );
451  if ( mMode == Mode::Off )
452  typeOffAct->setChecked( true );
453 
454  QAction *typeCrsAct = new QAction( tr( "Coordinate Reference &System" ), mMenu );
455  typeCrsAct->setCheckable( true );
456  typeCrsAct->setStatusTip( tr( "Coordinate Reference System 3D axis" ) );
457  if ( mMode == Mode::Crs )
458  typeCrsAct->setChecked( true );
459 
460  QAction *typeCubeAct = new QAction( tr( "&Cube" ), mMenu );
461  typeCubeAct->setCheckable( true );
462  typeCubeAct->setStatusTip( tr( "Cube 3D axis" ) );
463  if ( mMode == Mode::Cube )
464  typeCubeAct->setChecked( true );
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( Mode::Off );} );
472  connect( typeCrsAct, &QAction::triggered, this, [this]( bool ) {onAxisModeChanged( Mode::Crs );} );
473  connect( typeCubeAct, &QAction::triggered, this, [this]( bool ) {onAxisModeChanged( Mode::Cube );} );
474 
475  QMenu *typeMenu = new QMenu( QStringLiteral( "Axis Type" ), mMenu );
476  typeMenu->addAction( typeOffAct );
477  typeMenu->addAction( typeCrsAct );
478  typeMenu->addAction( typeCubeAct );
479  mMenu->addMenu( typeMenu );
480 
481  // horizontal position menu
482  QAction *hPosLeftAct = new QAction( tr( "&Left" ), mMenu );
483  hPosLeftAct->setCheckable( true );
484  if ( mAxisViewportHorizPos == Qt::AnchorPoint::AnchorLeft )
485  hPosLeftAct->setChecked( true );
486 
487  QAction *hPosMiddleAct = new QAction( tr( "&Center" ), mMenu );
488  hPosMiddleAct->setCheckable( true );
489  if ( mAxisViewportHorizPos == Qt::AnchorPoint::AnchorHorizontalCenter )
490  hPosMiddleAct->setChecked( true );
491 
492  QAction *hPosRightAct = new QAction( tr( "&Right" ), mMenu );
493  hPosRightAct->setCheckable( true );
494  if ( mAxisViewportHorizPos == Qt::AnchorPoint::AnchorRight )
495  hPosRightAct->setChecked( true );
496 
497  QActionGroup *hPosGroup = new QActionGroup( mMenu );
498  hPosGroup->addAction( hPosLeftAct );
499  hPosGroup->addAction( hPosMiddleAct );
500  hPosGroup->addAction( hPosRightAct );
501 
502  connect( hPosLeftAct, &QAction::triggered, this, [this]( bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorLeft );} );
503  connect( hPosMiddleAct, &QAction::triggered, this, [this]( bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter );} );
504  connect( hPosRightAct, &QAction::triggered, this, [this]( bool ) {onAxisHorizPositionChanged( Qt::AnchorPoint::AnchorRight );} );
505 
506  QMenu *horizPosMenu = new QMenu( QStringLiteral( "Horizontal Position" ), mMenu );
507  horizPosMenu->addAction( hPosLeftAct );
508  horizPosMenu->addAction( hPosMiddleAct );
509  horizPosMenu->addAction( hPosRightAct );
510  mMenu->addMenu( horizPosMenu );
511 
512  // vertical position menu
513  QAction *vPosTopAct = new QAction( tr( "&Top" ), mMenu );
514  vPosTopAct->setCheckable( true );
515  if ( mAxisViewportVertPos == Qt::AnchorPoint::AnchorTop )
516  vPosTopAct->setChecked( true );
517 
518  QAction *vPosMiddleAct = new QAction( tr( "&Middle" ), mMenu );
519  vPosMiddleAct->setCheckable( true );
520  if ( mAxisViewportVertPos == Qt::AnchorPoint::AnchorVerticalCenter )
521  vPosMiddleAct->setChecked( true );
522 
523  QAction *vPosBottomAct = new QAction( tr( "&Bottom" ), mMenu );
524  vPosBottomAct->setCheckable( true );
525  if ( mAxisViewportVertPos == Qt::AnchorPoint::AnchorBottom )
526  vPosBottomAct->setChecked( true );
527 
528  QActionGroup *vPosGroup = new QActionGroup( mMenu );
529  vPosGroup->addAction( vPosTopAct );
530  vPosGroup->addAction( vPosMiddleAct );
531  vPosGroup->addAction( vPosBottomAct );
532 
533  connect( vPosTopAct, &QAction::triggered, this, [this]( bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorTop );} );
534  connect( vPosMiddleAct, &QAction::triggered, this, [this]( bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter );} );
535  connect( vPosBottomAct, &QAction::triggered, this, [this]( bool ) {onAxisVertPositionChanged( Qt::AnchorPoint::AnchorBottom );} );
536 
537  QMenu *vertPosMenu = new QMenu( QStringLiteral( "Vertical Position" ), mMenu );
538  vertPosMenu->addAction( vPosTopAct );
539  vertPosMenu->addAction( vPosMiddleAct );
540  vertPosMenu->addAction( vPosBottomAct );
541  mMenu->addMenu( vertPosMenu );
542 
543  // axis view menu
544  QAction *viewHomeAct = new QAction( tr( "&Home" ) + "\t Ctrl+1", mMenu );
545  QAction *viewTopAct = new QAction( tr( "&Top" ) + "\t Ctrl+5", mMenu );
546  QAction *viewNorthAct = new QAction( tr( "&North" ) + "\t Ctrl+8", mMenu );
547  QAction *viewEastAct = new QAction( tr( "&East" ) + "\t Ctrl+6", mMenu );
548  QAction *viewSouthAct = new QAction( tr( "&South" ) + "\t Ctrl+2", mMenu );
549  QAction *viewWestAct = new QAction( tr( "&West" ) + "\t Ctrl+4", mMenu );
550  QAction *viewBottomAct = new QAction( tr( "&Bottom" ), mMenu );
551 
552  connect( viewHomeAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeHome );
553  connect( viewTopAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeTop );
554  connect( viewNorthAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeNorth );
555  connect( viewEastAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeEast );
556  connect( viewSouthAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeSouth );
557  connect( viewWestAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeWest );
558  connect( viewBottomAct, &QAction::triggered, this, &Qgs3DAxis::onCameraViewChangeBottom );
559 
560  QgsWindow3DEngine *eng = dynamic_cast<QgsWindow3DEngine *>( mMapScene->engine() );
561  if ( eng )
562  {
563  QWidget *mapCanvas = dynamic_cast<QWidget *>( eng->parent() );
564  if ( mapCanvas == nullptr )
565  qDebug() << "NO CANVAS!";
566  else
567  {
568  QShortcut *shortcutHome = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_1 ), mapCanvas );
569  connect( shortcutHome, &QShortcut::activated, this, [this]( ) {onCameraViewChangeHome();} );
570 
571  QShortcut *shortcutTop = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_5 ), mapCanvas );
572  connect( shortcutTop, &QShortcut::activated, this, [this]( ) {onCameraViewChangeTop();} );
573 
574  QShortcut *shortcutNorth = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_8 ), mapCanvas );
575  connect( shortcutNorth, &QShortcut::activated, this, [this]( ) {onCameraViewChangeNorth();} );
576 
577  QShortcut *shortcutEast = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_6 ), mapCanvas );
578  connect( shortcutEast, &QShortcut::activated, this, [this]( ) {onCameraViewChangeEast();} );
579 
580  QShortcut *shortcutSouth = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_2 ), mapCanvas );
581  connect( shortcutSouth, &QShortcut::activated, this, [this]( ) {onCameraViewChangeSouth();} );
582 
583  QShortcut *shortcutWest = new QShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ), mapCanvas );
584  connect( shortcutWest, &QShortcut::activated, this, [this]( ) {onCameraViewChangeWest();} );
585  }
586  }
587 
588  QMenu *viewMenu = new QMenu( QStringLiteral( "Camera View" ), mMenu );
589  viewMenu->addAction( viewHomeAct );
590  viewMenu->addAction( viewTopAct );
591  viewMenu->addAction( viewNorthAct );
592  viewMenu->addAction( viewEastAct );
593  viewMenu->addAction( viewSouthAct );
594  viewMenu->addAction( viewWestAct );
595  viewMenu->addAction( viewBottomAct );
596  mMenu->addMenu( viewMenu );
597 }
598 
599 void Qgs3DAxis::hideMenu()
600 {
601  mMenu->hide();
602 }
603 
604 void Qgs3DAxis::displayMenuAt( const QPoint &sourcePos )
605 {
606  mMenu->popup( mParentWindow->parent()->mapToGlobal( sourcePos ) );
607 }
608 
609 void Qgs3DAxis::onAxisModeChanged( Qgs3DAxis::Mode mode )
610 {
611  setMode( mode );
612  Qgs3DAxisSettings s = mMapSettings->get3dAxisSettings();
613  s.setMode( mode );
614  mMapSettings->set3dAxisSettings( s );
615 }
616 
617 void Qgs3DAxis::onAxisHorizPositionChanged( Qt::AnchorPoint pos )
618 {
619  setAxisViewportPosition( mAxisViewportSize, mAxisViewportVertPos, pos );
620  Qgs3DAxisSettings s = mMapSettings->get3dAxisSettings();
621  s.setHorizontalPosition( pos );
622  mMapSettings->set3dAxisSettings( s );
623 }
624 
625 void Qgs3DAxis::onAxisVertPositionChanged( Qt::AnchorPoint pos )
626 {
627  setAxisViewportPosition( mAxisViewportSize, pos, mAxisViewportHorizPos );
628  Qgs3DAxisSettings s = mMapSettings->get3dAxisSettings();
629  s.setVerticalPosition( pos );
630  mMapSettings->set3dAxisSettings( s );
631 }
632 
633 void Qgs3DAxis::onCameraViewChange( float pitch, float yaw )
634 {
635  QgsVector3D pos = mCameraController->lookingAtPoint();
636  if ( mMapSettings->terrainRenderingEnabled() )
637  pos.set( pos.x(), mMapScene->terrainEntity()->terrainElevationOffset(), pos.z() );
638  else
639  pos.set( pos.x(), 0.0, pos.z() );
640 
641  mCameraController->setLookingAtPoint( pos, ( mCameraController->camera()->position() - pos.toVector3D() ).length(),
642  pitch, yaw );
643 }
644 
645 
646 void Qgs3DAxis::createCube( )
647 {
648  QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
649 
650  // cube outlines
651  Qt3DCore::QEntity *cubeLineEntity = new Qt3DCore::QEntity( mCubeRoot );
652  cubeLineEntity->setObjectName( "3DAxis_cubeline" );
653  Qgs3DWiredMesh *cubeLine = new Qgs3DWiredMesh;
654  QgsAABB box = QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f,
655  mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
656  cubeLine->setVertices( box.verticesForLines() );
657  cubeLineEntity->addComponent( cubeLine );
658 
659  Qt3DExtras::QPhongMaterial *cubeLineMaterial = new Qt3DExtras::QPhongMaterial;
660  cubeLineMaterial->setAmbient( Qt::white );
661  cubeLineEntity->addComponent( cubeLineMaterial );
662 
663  // cube mesh
664  Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
665  cubeMesh->setObjectName( "3DAxis_cubemesh" );
666  cubeMesh->setXExtent( mCylinderLength );
667  cubeMesh->setYExtent( mCylinderLength );
668  cubeMesh->setZExtent( mCylinderLength );
669  mCubeRoot->addComponent( cubeMesh );
670 
671  Qt3DExtras::QPhongMaterial *cubeMaterial = new Qt3DExtras::QPhongMaterial( mCubeRoot );
672  cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
673  cubeMaterial->setShininess( 100 );
674  mCubeRoot->addComponent( cubeMaterial );
675 
676  Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform;
677  QMatrix4x4 transformMatrixcube;
678  //transformMatrixcube.rotate( rotation );
679  transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
680  cubeTransform->setMatrix( transformMatrixcube );
681  mCubeRoot->addComponent( cubeTransform );
682 
683  // text
684  QString text;
685  int fontSize = 0.75 * mFontSize;
686  float textHeight = fontSize * 1.5;
687  float textWidth;
688  QFont f = QFontDatabase::systemFont( QFontDatabase::FixedFont );
689  f.setPointSize( fontSize );
690  f.setWeight( QFont::Weight::Black );
691 
692  {
693  text = QStringLiteral( "top" );
694  textWidth = text.length() * fontSize * 0.75;
695  QVector3D translation = minPos + QVector3D(
696  mCylinderLength * 0.5f - textWidth / 2.0f,
697  mCylinderLength * 0.5f - textHeight / 2.0f,
698  mCylinderLength * 1.01f );
699  QMatrix4x4 rotation;
700  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
701  }
702 
703  {
704  text = QStringLiteral( "btm" );
705  textWidth = text.length() * fontSize * 0.75;
706  QVector3D translation = minPos + QVector3D(
707  mCylinderLength * 0.5f - textWidth / 2.0f,
708  mCylinderLength * 0.5f + textHeight / 2.0f,
709  -mCylinderLength * 0.01f );
710  QMatrix4x4 rotation;
711  rotation.rotate( 180.0f, QVector3D( 1.0, 0.0, 0.0 ).normalized() );
712  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
713  }
714 
715  {
716  text = QStringLiteral( "west" );
717  textWidth = text.length() * fontSize * 0.75;
718  QVector3D translation = minPos + QVector3D(
719  - mCylinderLength * 0.01f,
720  mCylinderLength * 0.5f + textWidth / 2.0f,
721  mCylinderLength * 0.5f - textHeight / 2.0f );
722  QMatrix4x4 rotation;
723  rotation.rotate( 90.0f, QVector3D( 0.0, -1.0, 0.0 ).normalized() );
724  rotation.rotate( 90.0f, QVector3D( 0.0, 0.0, -1.0 ).normalized() );
725  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
726  }
727 
728  {
729  text = QStringLiteral( "east" );
730  textWidth = text.length() * fontSize * 0.75;
731  QVector3D translation = minPos + QVector3D(
732  mCylinderLength * 1.01f,
733  mCylinderLength * 0.5f - textWidth / 2.0f,
734  mCylinderLength * 0.5f - textHeight / 2.0f );
735  QMatrix4x4 rotation;
736  rotation.rotate( 90.0f, QVector3D( 0.0, 1.0, 0.0 ).normalized() );
737  rotation.rotate( 90.0f, QVector3D( 0.0, 0.0, 1.0 ).normalized() );
738  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
739  }
740 
741  {
742  text = QStringLiteral( "south" );
743  textWidth = text.length() * fontSize * 0.75;
744  QVector3D translation = minPos + QVector3D(
745  mCylinderLength * 0.5f - textWidth / 2.0f,
746  - mCylinderLength * 0.01f,
747  mCylinderLength * 0.5f - textHeight / 2.0f );
748  QMatrix4x4 rotation;
749  rotation.rotate( 90.0f, QVector3D( 1.0, 0.0, 0.0 ).normalized() );
750  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
751  }
752 
753  {
754  text = QStringLiteral( "north" );
755  textWidth = text.length() * fontSize * 0.75;
756  QVector3D translation = minPos + QVector3D(
757  mCylinderLength * 0.5f + textWidth / 2.0f,
758  mCylinderLength * 1.01f,
759  mCylinderLength * 0.5f - textHeight / 2.0f );
760  QMatrix4x4 rotation;
761  rotation.rotate( 90.0f, QVector3D( -1.0, 0.0, 0.0 ).normalized() );
762  rotation.rotate( 180.0f, QVector3D( 0.0, 0.0, 1.0 ).normalized() );
763  mCubeLabels << addCubeText( text, textHeight, textWidth, f, rotation, translation );
764  }
765 
766  for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
767  {
768  l->setParent( mCubeRoot );
769  }
770 }
771 
772 Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText( const QString &text, float textHeight, float textWidth, const QFont &f, const QMatrix4x4 &rotation, const QVector3D &translation )
773 {
774  Qt3DExtras::QText2DEntity *textEntity = new Qt3DExtras::QText2DEntity;
775  textEntity->setObjectName( "3DAxis_cube_label_" + text );
776  textEntity->setFont( f );
777  textEntity->setHeight( textHeight );
778  textEntity->setWidth( textWidth );
779  textEntity->setColor( QColor( 192, 192, 192 ) );
780  textEntity->setText( text );
781 
782  Qt3DCore::QTransform *textFrontTransform = new Qt3DCore::QTransform();
783  textFrontTransform->setMatrix( rotation );
784  textFrontTransform->setTranslation( translation );
785  textEntity->addComponent( textFrontTransform );
786 
787  return textEntity;
788 }
789 
790 void Qgs3DAxis::createAxis( Qt::Axis axisType )
791 {
792  float cylinderRadius = 0.05 * mCylinderLength;
793  float coneLength = 0.3 * mCylinderLength;
794  float coneBottomRadius = 0.1 * mCylinderLength;
795 
796  QQuaternion rotation;
797  QColor color;
798 
799  Qt3DExtras::QText2DEntity *text = nullptr;
800  Qt3DCore::QTransform *textTransform = nullptr;
801  QString name;
802 
803  switch ( axisType )
804  {
805  case Qt::Axis::XAxis:
806  mTextX = new Qt3DExtras::QText2DEntity( ); // object initialization in two step:
807  mTextX->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
808  connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString & text )
809  {
810  mTextX->setWidth( mFontSize * text.length() );
811  } );
812  mTextTransformX = new Qt3DCore::QTransform();
813  mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0, 0.0f, 0.0f );
814 
815  rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
816  color = Qt::red;
817  text = mTextX;
818  textTransform = mTextTransformX;
819  name = "3DAxis_axisX";
820  break;
821 
822  case Qt::Axis::YAxis:
823  mTextY = new Qt3DExtras::QText2DEntity( ); // object initialization in two step:
824  mTextY->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
825  connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString & text )
826  {
827  mTextY->setWidth( mFontSize * text.length() );
828  } );
829  mTextTransformY = new Qt3DCore::QTransform();
830  mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0, 0.0f );
831 
832  rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 0.0f ), 0.0f );
833  color = Qt::green;
834  text = mTextY;
835  textTransform = mTextTransformY;
836  name = "3DAxis_axisY";
837  break;
838 
839  case Qt::Axis::ZAxis:
840  mTextZ = new Qt3DExtras::QText2DEntity( ); // object initialization in two step:
841  mTextZ->setParent( mTwoDLabelSceneEntity ); // see https://bugreports.qt.io/browse/QTBUG-77139
842  connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged, this, [this]( const QString & text )
843  {
844  mTextZ->setWidth( mFontSize * text.length() );
845  } );
846  mTextTransformZ = new Qt3DCore::QTransform();
847  mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0 );
848 
849  rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
850  color = Qt::blue;
851  text = mTextZ;
852  textTransform = mTextTransformZ;
853  name = "3DAxis_axisZ";
854  break;
855 
856  default:
857  return;
858  }
859 
860  // cylinder
861  Qt3DCore::QEntity *cylinder = new Qt3DCore::QEntity( mAxisRoot );
862  cylinder->setObjectName( name );
863 
864  Qt3DExtras::QCylinderMesh *cylinderMesh = new Qt3DExtras::QCylinderMesh;
865  cylinderMesh->setRadius( cylinderRadius );
866  cylinderMesh->setLength( mCylinderLength );
867  cylinderMesh->setRings( 10 );
868  cylinderMesh->setSlices( 4 );
869  cylinder->addComponent( cylinderMesh );
870 
871  Qt3DExtras::QPhongMaterial *cylinderMaterial = new Qt3DExtras::QPhongMaterial( cylinder );
872  cylinderMaterial->setAmbient( color );
873  cylinderMaterial->setShininess( 0 );
874  cylinder->addComponent( cylinderMaterial );
875 
876  Qt3DCore::QTransform *cylinderTransform = new Qt3DCore::QTransform;
877  QMatrix4x4 transformMatrixCylinder;
878  transformMatrixCylinder.rotate( rotation );
879  transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
880  cylinderTransform->setMatrix( transformMatrixCylinder );
881  cylinder->addComponent( cylinderTransform );
882 
883  // cone
884  Qt3DCore::QEntity *coneEntity = new Qt3DCore::QEntity( mAxisRoot );
885  coneEntity->setObjectName( name );
886  Qt3DExtras::QConeMesh *coneMesh = new Qt3DExtras::QConeMesh;
887  coneMesh->setLength( coneLength );
888  coneMesh->setBottomRadius( coneBottomRadius );
889  coneMesh->setTopRadius( 0.0f );
890  coneMesh->setRings( 10 );
891  coneMesh->setSlices( 4 );
892  coneEntity->addComponent( coneMesh );
893 
894  Qt3DExtras::QPhongMaterial *coneMaterial = new Qt3DExtras::QPhongMaterial( coneEntity );
895  coneMaterial->setAmbient( color );
896  coneMaterial->setShininess( 0 );
897  coneEntity->addComponent( coneMaterial );
898 
899  Qt3DCore::QTransform *coneTransform = new Qt3DCore::QTransform;
900  QMatrix4x4 transformMatrixCone;
901  transformMatrixCone.rotate( rotation );
902  transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
903  coneTransform->setMatrix( transformMatrixCone );
904  coneEntity->addComponent( coneTransform );
905 
906  // text
907  QFont f = QFont( "monospace", mFontSize ); // TODO: should use outlined font
908  f.setWeight( QFont::Weight::Black );
909  f.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
910  text->setFont( f );
911  text->setHeight( mFontSize * 1.5 );
912  text->setWidth( mFontSize );
913  text->setColor( QColor( 192, 192, 192, 192 ) );
914  text->addComponent( textTransform );
915 }
916 
917 
918 void Qgs3DAxis::setAxisViewportPosition( int axisViewportSize, Qt::AnchorPoint axisViewportVertPos, Qt::AnchorPoint axisViewportHorizPos )
919 {
920  mAxisViewportSize = axisViewportSize;
921  mAxisViewportVertPos = axisViewportVertPos;
922  mAxisViewportHorizPos = axisViewportHorizPos;
923  onAxisViewportSizeUpdate();
924  mParentWindow->requestUpdate();
925 }
926 
927 void Qgs3DAxis::onAxisViewportSizeUpdate( int )
928 {
929  float widthRatio = ( float )mAxisViewportSize / mParentWindow->width();
930  float heightRatio = ( float )mAxisViewportSize / mParentWindow->height();
931 
932  float xRatio = 1.0;
933  float yRatio = 1.0;
934  if ( mAxisViewportHorizPos == Qt::AnchorPoint::AnchorLeft )
935  xRatio = 0.0f;
936  else if ( mAxisViewportHorizPos == Qt::AnchorPoint::AnchorHorizontalCenter )
937  xRatio = 0.5 - widthRatio / 2.0;
938  else
939  xRatio = 1.0 - widthRatio;
940 
941  if ( mAxisViewportVertPos == Qt::AnchorPoint::AnchorTop )
942  yRatio = 0.0f;
943  else if ( mAxisViewportVertPos == Qt::AnchorPoint::AnchorVerticalCenter )
944  yRatio = 0.5 - heightRatio / 2.0;
945  else
946  yRatio = 1.0 - heightRatio;
947 
948 #ifdef DEBUG
949  qDebug() << "Axis, update viewport" << xRatio << yRatio << widthRatio << heightRatio;
950 #endif
951  mAxisViewport->setNormalizedRect( QRectF( xRatio, yRatio, widthRatio, heightRatio ) );
952 
953  mTwoDLabelCamera->lens()->setOrthographicProjection(
954  -mParentWindow->width() / 2.0f, mParentWindow->width() / 2.0f,
955  -mParentWindow->height() / 2.0f, mParentWindow->height() / 2.0f,
956  mTwoDLabelCamera->lens()->nearPlane(), mTwoDLabelCamera->lens()->farPlane() );
957 
958  updateAxisLabelPosition();
959 }
960 
961 void Qgs3DAxis::onCameraUpdate( )
962 {
963  Qt3DRender::QCamera *parentCamera = mCameraController->camera();
964 
965  if ( parentCamera->viewVector() != mPreviousVector
966  && !std::isnan( parentCamera->viewVector().x() )
967  && !std::isnan( parentCamera->viewVector().y() )
968  && !std::isnan( parentCamera->viewVector().z() ) )
969  {
970  mPreviousVector = parentCamera->viewVector();
971  QVector3D mainCameraShift = parentCamera->viewVector().normalized();
972  float zy_swap = mainCameraShift.y();
973  mainCameraShift.setY( mainCameraShift.z() );
974  mainCameraShift.setZ( -zy_swap );
975  mainCameraShift.setX( -mainCameraShift.x() );
976 
977  if ( mAxisCamera->projectionType() == Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection )
978  {
979  mAxisCamera->setPosition( mainCameraShift );
980  }
981  else
982  {
983  mAxisCamera->setPosition( mainCameraShift * mCylinderLength * 10.0 );
984  }
985 
986  if ( mAxisRoot->isEnabled() )
987  {
988  updateAxisLabelPosition();
989  }
990  }
991 }
992 
993 void Qgs3DAxis::updateAxisLabelPosition()
994 {
995  if ( mTextTransformX && mTextTransformY && mTextTransformZ )
996  {
997  mTextTransformX->setTranslation( from3dTo2dLabelPosition( mTextCoordX, mAxisCamera, mAxisViewport,
998  mTwoDLabelCamera, mTwoDLabelViewport,
999  mParentWindow->size() ) );
1000  mTextTransformY->setTranslation( from3dTo2dLabelPosition( mTextCoordY, mAxisCamera, mAxisViewport,
1001  mTwoDLabelCamera, mTwoDLabelViewport,
1002  mParentWindow->size() ) );
1003  mTextTransformZ->setTranslation( from3dTo2dLabelPosition( mTextCoordZ, mAxisCamera, mAxisViewport,
1004  mTwoDLabelCamera, mTwoDLabelViewport,
1005  mParentWindow->size() ) );
1006  }
1007 }
1008 
1009 void Qgs3DAxis::setMode( Mode axisMode )
1010 {
1011  if ( mMode != axisMode )
1012  {
1013  mMode = axisMode;
1014  createAxisScene();
1015  mParentWindow->requestUpdate();
1016  }
1017 }
1018 
1019 
1020 //
1021 // Qgs3DWiredMesh
1022 //
1023 
1024 Qgs3DWiredMesh::Qgs3DWiredMesh( Qt3DCore::QNode *parent )
1025  : Qt3DRender::QGeometryRenderer( parent ),
1026  mPositionAttribute( new Qt3DRender::QAttribute( this ) ),
1027  mVertexBuffer( new Qt3DRender::QBuffer( this ) )
1028 {
1029  mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
1030  mPositionAttribute->setBuffer( mVertexBuffer );
1031  mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
1032  mPositionAttribute->setVertexSize( 3 );
1033  mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
1034 
1035  mGeom = new Qt3DRender::QGeometry( this );
1036  mGeom->addAttribute( mPositionAttribute );
1037 
1038  setInstanceCount( 1 );
1039  setIndexOffset( 0 );
1040  setFirstInstance( 0 );
1041  setPrimitiveType( Qt3DRender::QGeometryRenderer::Lines );
1042  setGeometry( mGeom );
1043 }
1044 
1046 
1047 void Qgs3DWiredMesh::setVertices( const QList<QVector3D> &vertices )
1048 {
1049  QByteArray vertexBufferData;
1050  vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
1051  float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
1052  int idx = 0;
1053  for ( const QVector3D &v : std::as_const( vertices ) )
1054  {
1055  rawVertexArray[idx++] = v.x();
1056  rawVertexArray[idx++] = v.y();
1057  rawVertexArray[idx++] = v.z();
1058  }
1059 
1060  mVertexBuffer->setData( vertexBufferData );
1061  setVertexCount( vertices.count() );
1062 }
Qgs3DWiredMesh::setVertices
void setVertices(const QList< QVector3D > &vertices)
add or replace mesh vertices coordinates
Definition: qgs3daxis.cpp:1047
QgsCameraController::lookingAtPoint
QgsVector3D lookingAtPoint() const
Returns the point in the world coordinates towards which the camera is looking.
Definition: qgscameracontroller.cpp:186
Qgs3DMapScene::terrainEntity
QgsTerrainEntity * terrainEntity()
Returns terrain entity (may be temporarily nullptr)
Definition: qgs3dmapscene.h:80
Qgs3DAxis::mode
Qgs3DAxis::Mode mode()
Returns axis mode.
Definition: qgs3daxis.h:87
Qgs3DMapSettings::terrainRenderingEnabled
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
Definition: qgs3dmapsettings.h:578
QgsVector3D
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
Qgs3DWiredMesh
Geometry renderer for lines, draws a wired mesh.
Definition: qgs3daxis.h:228
Qgs3DAxisSettings::setVerticalPosition
void setVerticalPosition(Qt::AnchorPoint position)
Sets the vertical position for the 3d axis.
Definition: qgs3daxissettings.h:69
QgsCoordinateReferenceSystem::axisOrdering
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
Definition: qgscoordinatereferencesystem.cpp:804
QgsVector3D::toVector3D
QVector3D toVector3D() const
Converts the current object to QVector3D.
Definition: qgsvector3d.h:182
Qgs3DWiredMesh::Qgs3DWiredMesh
Qgs3DWiredMesh(Qt3DCore::QNode *parent=nullptr)
Defaul Qgs3DWiredMesh constructor.
Definition: qgs3daxis.cpp:1024
Qgs3DAxis::setMode
void setMode(Qgs3DAxis::Mode axisMode)
set axis representation mode
Definition: qgs3daxis.cpp:1009
QgsVector3D::set
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:69
Qgs3DWiredMesh::~Qgs3DWiredMesh
~Qgs3DWiredMesh() override
Qgs3DMapSettings::get3dAxisSettings
Qgs3DAxisSettings get3dAxisSettings() const
Returns the current configuration of 3d axis.
Definition: qgs3dmapsettings.h:635
Qgs3DAxisSettings::verticalPosition
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
Definition: qgs3daxissettings.h:67
Qgs3DAxisSettings::mode
Qgs3DAxis::Mode mode() const
Returns the type of the 3daxis.
Definition: qgs3daxissettings.h:57
QgsVector3D::z
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:66
QgsCameraController::camera
Qt3DRender::QCamera camera
Definition: qgscameracontroller.h:64
Qgs3DAxis::Mode::Cube
@ Cube
Abstract cube mode.
QgsAABB::verticesForLines
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
Definition: qgsaabb.cpp:63
Qt3DRender
Definition: qgs3dmapscene.h:28
QgsAABB
Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:33
Qgs3DMapSettings
Definition of the world.
Definition: qgs3dmapsettings.h:57
qgswindow3dengine.h
Qgs3DAxisSettings
Contains the configuration of a 3d axis.
Definition: qgs3daxissettings.h:35
qgscoordinatereferencesystemutils.h
Qgs3DAxisSettings::setHorizontalPosition
void setHorizontalPosition(Qt::AnchorPoint position)
Sets the horizontal position for the 3d axis.
Definition: qgs3daxissettings.h:64
QgsCameraController::setLookingAtPoint
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),...
Definition: qgscameracontroller.cpp:191
Qgs3DAxis::axisViewportSize
int axisViewportSize() const
Returns axis viewport size.
Definition: qgs3daxis.h:106
Qgs3DAxisSettings::setMode
void setMode(Qgs3DAxis::Mode type)
Sets the type of the 3daxis.
Definition: qgs3daxissettings.h:59
Qgs3DMapScene
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
Definition: qgs3dmapscene.h:70
Qgs3DAxis::Qgs3DAxis
Qgs3DAxis(Qt3DExtras::Qt3DWindow *parentWindow, Qt3DCore::QEntity *parent3DScene, Qgs3DMapScene *mapScene, QgsCameraController *camera, Qgs3DMapSettings *map)
Defaul Qgs3DAxis constructor.
Definition: qgs3daxis.cpp:40
qgsterrainentity_p.h
qgs3dmapsettings.h
Qgs3DAxis::Mode::Crs
@ Crs
Respect CRS directions.
Qgs3DAxis::from3dTo2dLabelPosition
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...
Definition: qgs3daxis.cpp:329
Qgs3DMapScene::engine
QgsAbstract3DEngine * engine()
Returns the abstract 3D engine.
Definition: qgs3dmapscene.h:158
Qgs3DAxis::~Qgs3DAxis
~Qgs3DAxis() override
Definition: qgs3daxis.cpp:76
QgsCoordinateReferenceSystemUtils::axisDirectionToAbbreviatedString
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
Definition: qgscoordinatereferencesystemutils.cpp:82
Qgs3DAxis::Mode
Mode
The Mode enum.
Definition: qgs3daxis.h:76
Qgs3DAxisSettings::horizontalPosition
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
Definition: qgs3daxissettings.h:62
QgsCameraController
Object that controls camera movement based on user input.
Definition: qgscameracontroller.h:61
Qgs3DAxis::setAxisViewportPosition
void setAxisViewportPosition(int axisViewportSize, Qt::AnchorPoint axisViewportVertPos, Qt::AnchorPoint axisViewportHorizPos)
set axis viewport position parameters
Definition: qgs3daxis.cpp:918
qgs3daxis.h
QgsCameraController::cameraChanged
void cameraChanged()
Emitted when camera has been updated.
QgsVector3D::x
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:62
qgscoordinatereferencesystem.h
QgsWindow3DEngine
On-screen 3D engine: it creates OpenGL window (QWindow) and displays rendered 3D scene there....
Definition: qgswindow3dengine.h:49
Qgs3DAxis::Mode::Off
@ Off
Hide 3d axis.
qgs3dmapscene.h
Qgs3DMapSettings::set3dAxisSettings
void set3dAxisSettings(const Qgs3DAxisSettings &axisSettings)
Sets the current configuration of 3d axis.
Definition: qgs3dmapsettings.cpp:965