QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgs3dmapscene.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgs3dmapscene.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail 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 "qgs3dmapscene.h"
17 
18 #include <Qt3DRender/QCamera>
19 #include <Qt3DRender/QMesh>
20 #include <Qt3DRender/QObjectPicker>
21 #include <Qt3DRender/QPickEvent>
22 #include <Qt3DRender/QPickingSettings>
23 #include <Qt3DRender/QPickTriangleEvent>
24 #include <Qt3DRender/QPointLight>
25 #include <Qt3DRender/QDirectionalLight>
26 #include <Qt3DRender/QRenderSettings>
27 #include <Qt3DRender/QSceneLoader>
28 #include <Qt3DExtras/QForwardRenderer>
29 #include <Qt3DExtras/QPhongMaterial>
30 #include <Qt3DExtras/QSphereMesh>
31 #include <Qt3DLogic/QFrameAction>
32 #include <Qt3DRender/QEffect>
33 #include <Qt3DRender/QTechnique>
34 #include <Qt3DRender/QRenderPass>
35 #include <Qt3DRender/QRenderState>
36 #include <Qt3DRender/QCullFace>
37 #include <Qt3DRender/QDepthTest>
38 #include <QSurface>
39 #include <QUrl>
40 
41 #include <QOpenGLContext>
42 #include <QOpenGLFunctions>
43 #include <QTimer>
44 
45 #include "qgslogger.h"
46 #include "qgsapplication.h"
47 #include "qgsaabb.h"
48 #include "qgsabstract3dengine.h"
50 #include "qgs3dmapsettings.h"
51 #include "qgs3dutils.h"
52 #include "qgsabstract3drenderer.h"
53 #include "qgscameracontroller.h"
54 #include "qgschunkedentity_p.h"
55 #include "qgschunknode_p.h"
56 #include "qgseventtracing.h"
57 #include "qgsmeshlayer.h"
58 #include "qgsmeshlayer3drenderer.h"
59 #include "qgspoint3dsymbol.h"
60 #include "qgsrulebased3drenderer.h"
61 #include "qgspointcloudlayer.h"
63 #include "qgssourcecache.h"
64 #include "qgsterrainentity_p.h"
65 #include "qgsterraingenerator.h"
67 #include "qgsvectorlayer.h"
71 
72 #include "qgslinematerial_p.h"
73 #include "qgs3dsceneexporter.h"
74 #include "qgsabstract3drenderer.h"
75 #include "qgs3dmapexportsettings.h"
76 #include "qgsmessageoutput.h"
77 
78 #include "qgsskyboxentity.h"
79 #include "qgsskyboxsettings.h"
80 
81 #include "qgswindow3dengine.h"
83 #include "qgspointcloudlayer.h"
85 
87  : mMap( map )
88  , mEngine( engine )
89 {
90 
91  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
92  onBackgroundColorChanged();
93 
94  // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
95  // even if there's no change. Switching to "on demand" should only re-render when something has changed
96  // and we save quite a lot of resources
97  mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
98 
99  // we want precise picking of terrain (also bounding volume picking does not seem to work - not sure why)
100  mEngine->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
101 
102  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
103 
104  // Camera
105  float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
106  mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
107 
108  mFrameAction = new Qt3DLogic::QFrameAction();
109  connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
110  this, &Qgs3DMapScene::onFrameTriggered );
111  addComponent( mFrameAction ); // takes ownership
112 
113  // Camera controlling
114  mCameraController = new QgsCameraController( this ); // attaches to the scene
115  mCameraController->setViewport( viewportRect );
116  mCameraController->setCamera( mEngine->camera() );
117  mCameraController->resetView( 1000 );
118 
119  addCameraViewCenterEntity( mEngine->camera() );
120  updateLights();
121 
122  // create terrain entity
123 
124  createTerrainDeferred();
125  connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
126  connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
127  connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
128  connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
129  connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
130  connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
131  connect( &map, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapScene::updateLights );
132  connect( &map, &Qgs3DMapSettings::directionalLightsChanged, this, &Qgs3DMapScene::updateLights );
133  connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
134  connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
135  connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
136  connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged );
137  connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
138  connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
139  connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
140  connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
141  connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
142  connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
143  connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
145  connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
146 
147  connect( QgsApplication::instance()->sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
148  {
149  const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
150  for ( QgsMapLayer *layer : modelVectorLayers )
151  {
152  QgsAbstract3DRenderer *renderer = layer->renderer3D();
153  if ( renderer )
154  {
155  if ( renderer->type() == QLatin1String( "vector" ) )
156  {
157  const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
158  if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
159  {
160  removeLayerEntity( layer );
161  addLayerEntity( layer );
162  }
163  }
164  else if ( renderer->type() == QLatin1String( "rulebased" ) )
165  {
166  const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
167  for ( auto rule : rules )
168  {
169  const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
170  if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
171  {
172  removeLayerEntity( layer );
173  addLayerEntity( layer );
174  break;
175  }
176  }
177  }
178  }
179  }
180  } );
181 
182  // create entities of renderers
183 
184  onRenderersChanged();
185 
186  // listen to changes of layers in order to add/remove 3D renderer entities
187  connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
188 
189 
190 #if 0
191  ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
192  testChunkEntity->setEnabled( false );
193  testChunkEntity->setParent( this );
194  chunkEntities << testChunkEntity;
195 #endif
196 
197  connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
198  connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged );
199 
200 #if 0
201  // experiments with loading of existing 3D models.
202 
203  // scene loader only gets loaded only when added to a scene...
204  // it loads everything: geometries, materials, transforms, lights, cameras (if any)
205  Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity;
206  Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader;
207  loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
208  loaderEntity->addComponent( loader );
209  loaderEntity->setParent( this );
210 
211  // mesh loads just geometry as one geometry...
212  // so if there are different materials (e.g. colors) used in the model, this information is lost
213  Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity;
214  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
215  mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
216  meshEntity->addComponent( mesh );
217  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
218  material->setAmbient( Qt::red );
219  meshEntity->addComponent( material );
220  Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform;
221  meshTransform->setScale( 1 );
222  meshEntity->addComponent( meshTransform );
223  meshEntity->setParent( this );
224 #endif
225  onSkyboxSettingsChanged();
226 
227  // force initial update of chunked entities
228  onCameraChanged();
229  // force initial update of eye dome shading
230  onEyeDomeShadingSettingsChanged();
231  // force initial update of debugging setting of preview quads
232  onDebugShadowMapSettingsChanged();
233  onDebugDepthMapSettingsChanged();
234 
235  mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
236  onCameraMovementSpeedChanged();
237 }
238 
240 {
241  QgsRectangle extent = mMap.terrainGenerator()->extent();
242  float side = std::max( extent.width(), extent.height() );
243  mCameraController->resetView( side ); // assuming FOV being 45 degrees
244 }
245 
247 {
248  return mTerrain ? mTerrain->pendingJobsCount() : 0;
249 }
250 
252 {
253  int count = 0;
254  for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
255  count += entity->pendingJobsCount();
256  return count;
257 }
258 
260 {
261  if ( mPickHandlers.isEmpty() )
262  {
263  // we need to add object pickers
264  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
265  {
266  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
267  chunkedEntity->setPickingEnabled( true );
268  }
269  }
270 
271  mPickHandlers.append( pickHandler );
272 }
273 
275 {
276  mPickHandlers.removeOne( pickHandler );
277 
278  if ( mPickHandlers.isEmpty() )
279  {
280  // we need to remove pickers
281  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
282  {
283  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
284  chunkedEntity->setPickingEnabled( false );
285  }
286  }
287 }
288 
289 void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid )
290 {
291  QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
292  if ( !layer )
293  return;
294 
295  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
296  if ( !vlayer )
297  return;
298 
299  for ( Qgs3DMapScenePickHandler *pickHandler : std::as_const( mPickHandlers ) )
300  {
301  pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
302  }
303 }
304 
305 float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
306 {
307  Qt3DRender::QCamera *camera = mCameraController->camera();
308  float fov = camera->fieldOfView();
309  QRect rect = mCameraController->viewport();
310  float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
311 
312  // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
313  // with explanation of the math.
314  float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
315  float err = frustumWidthAtDistance * epsilon / screenSizePx;
316  return err;
317 }
318 
319 QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
320 {
321  Qt3DRender::QCamera *camera = cameraController->camera();
322  QgsChunkedEntity::SceneState state;
323  state.cameraFov = camera->fieldOfView();
324  state.cameraPos = camera->position();
325  QRect rect = cameraController->viewport();
326  state.screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
327  state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
328  return state;
329 }
330 
331 void Qgs3DMapScene::onCameraChanged()
332 {
333  if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
334  {
335  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
336  const float viewWidthFromCenter = mCameraController->distance();
337  const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
338  mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
339  }
340 
341  updateScene();
342  bool changedCameraPlanes = updateCameraNearFarPlanes();
343 
344  if ( changedCameraPlanes )
345  {
346  // repeat update of entities - because we have updated camera's near/far planes,
347  // the active nodes may have changed as well
348  updateScene();
349  updateCameraNearFarPlanes();
350  }
351 
352  onShadowSettingsChanged();
353 }
354 
355 void removeQLayerComponentsFromHierarchy( Qt3DCore::QEntity *entity )
356 {
357  QVector<Qt3DCore::QComponent *> toBeRemovedComponents;
358  for ( Qt3DCore::QComponent *component : entity->components() )
359  {
360  Qt3DRender::QLayer *layer = qobject_cast<Qt3DRender::QLayer *>( component );
361  if ( layer != nullptr )
362  toBeRemovedComponents.push_back( layer );
363  }
364  for ( Qt3DCore::QComponent *component : toBeRemovedComponents )
365  entity->removeComponent( component );
366  for ( Qt3DCore::QEntity *obj : entity->findChildren<Qt3DCore::QEntity *>() )
367  {
368  if ( obj != nullptr )
370  }
371 }
372 
373 void addQLayerComponentsToHierarchy( Qt3DCore::QEntity *entity, const QVector<Qt3DRender::QLayer *> layers )
374 {
375  for ( Qt3DRender::QLayer *layer : layers )
376  entity->addComponent( layer );
377  for ( Qt3DCore::QEntity *child : entity->findChildren<Qt3DCore::QEntity *>() )
378  {
379  if ( child != nullptr )
380  addQLayerComponentsToHierarchy( child, layers );
381  }
382 }
383 
384 void Qgs3DMapScene::updateScene()
385 {
386  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );
387  for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
388  {
389  if ( entity->isEnabled() )
390  entity->update( _sceneState( mCameraController ) );
391  }
392  updateSceneState();
393 }
394 
395 static void _updateNearFarPlane( const QList<QgsChunkNode *> &activeNodes, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar )
396 {
397  for ( QgsChunkNode *node : activeNodes )
398  {
399  // project each corner of bbox to camera coordinates
400  // and determine closest and farthest point.
401  QgsAABB bbox = node->bbox();
402  for ( int i = 0; i < 8; ++i )
403  {
404  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
405  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
406  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
407  QVector4D pc = viewMatrix * p;
408 
409  float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
410  if ( dst < fnear )
411  fnear = dst;
412  if ( dst > ffar )
413  ffar = dst;
414  }
415  }
416 }
417 
418 bool Qgs3DMapScene::updateCameraNearFarPlanes()
419 {
420  // Update near and far plane from the terrain.
421  // this needs to be done with great care as we have kind of circular dependency here:
422  // active nodes are culled based on the current frustum (which involves near + far plane)
423  // and then based on active nodes we set near and far plane.
424  //
425  // All of this is just heuristics assuming that all other stuff is being rendered somewhere
426  // around the area where the terrain is.
427  //
428  // Near/far plane is setup in order to make best use of the depth buffer to avoid:
429  // 1. precision errors - if the range is too great
430  // 2. unwanted clipping of scene - if the range is too small
431 
432  if ( mTerrain )
433  {
434  Qt3DRender::QCamera *camera = cameraController()->camera();
435  QMatrix4x4 viewMatrix = camera->viewMatrix();
436  float fnear = 1e9;
437  float ffar = 0;
438 
439  QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
440 
441  // it could be that there are no active nodes - they could be all culled or because root node
442  // is not yet loaded - we still need at least something to understand bounds of our scene
443  // so lets use the root node
444  if ( activeNodes.isEmpty() )
445  activeNodes << mTerrain->rootNode();
446 
447  _updateNearFarPlane( activeNodes, viewMatrix, fnear, ffar );
448 
449  // Also involve all the other chunked entities to make sure that they will not get
450  // clipped by the near or far plane
451  for ( QgsChunkedEntity *e : std::as_const( mChunkEntities ) )
452  {
453  if ( e != mTerrain )
454  _updateNearFarPlane( e->activeNodes(), viewMatrix, fnear, ffar );
455  }
456 
457  if ( fnear < 1 )
458  fnear = 1; // does not really make sense to use negative far plane (behind camera)
459 
460  if ( fnear == 1e9 && ffar == 0 )
461  {
462  // the update didn't work out... this should not happen
463  // well at least temporarily use some conservative starting values
464  qWarning() << "oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
465  fnear = 1;
466  ffar = 1e9;
467  }
468 
469  // set near/far plane - with some tolerance in front/behind expected near/far planes
470  float newFar = ffar * 2;
471  float newNear = fnear / 2;
472  if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
473  {
474  camera->setFarPlane( newFar );
475  camera->setNearPlane( newNear );
476  return true;
477  }
478  }
479  else
480  qWarning() << "no terrain - not setting near/far plane";
481 
482  return false;
483 }
484 
485 void Qgs3DMapScene::onFrameTriggered( float dt )
486 {
487  mCameraController->frameTriggered( dt );
488 
489  for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
490  {
491  if ( entity->isEnabled() && entity->needsUpdate() )
492  {
493  QgsDebugMsgLevel( QStringLiteral( "need for update" ), 2 );
494  entity->update( _sceneState( mCameraController ) );
495  }
496  }
497 
498  updateSceneState();
499 
500  // lock changing the FPS counter to 5 fps
501  static int frameCount = 0;
502  static float accumulatedTime = 0.0f;
503 
504  if ( !mMap.isFpsCounterEnabled() )
505  {
506  frameCount = 0;
507  accumulatedTime = 0;
508  return;
509  }
510 
511  frameCount++;
512  accumulatedTime += dt;
513  if ( accumulatedTime >= 0.2f )
514  {
515  float fps = ( float )frameCount / accumulatedTime;
516  frameCount = 0;
517  accumulatedTime = 0.0f;
518  emit fpsCountChanged( fps );
519  }
520 }
521 
522 void Qgs3DMapScene::createTerrain()
523 {
524  if ( mTerrain )
525  {
526  mChunkEntities.removeOne( mTerrain );
527 
528  mTerrain->deleteLater();
529  mTerrain = nullptr;
530 
531  emit terrainEntityChanged();
532  }
533 
534  if ( !mTerrainUpdateScheduled )
535  {
536  // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
537  QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
538  mTerrainUpdateScheduled = true;
539  setSceneState( Updating );
540  }
541 }
542 
543 void Qgs3DMapScene::createTerrainDeferred()
544 {
545  double tile0width = mMap.terrainGenerator()->extent().width();
546  int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
547  QgsAABB rootBbox = mMap.terrainGenerator()->rootChunkBbox( mMap );
548  float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
549  mMap.terrainGenerator()->setupQuadtree( rootBbox, rootError, maxZoomLevel );
550 
551  mTerrain = new QgsTerrainEntity( mMap );
552  //mTerrain->setEnabled(false);
553  mTerrain->setParent( this );
554 
555  if ( mMap.showTerrainBoundingBoxes() )
556  mTerrain->setShowBoundingBoxes( true );
557 
558  mCameraController->setTerrainEntity( mTerrain );
559 
560  mChunkEntities << mTerrain;
561 
562  onCameraChanged(); // force update of the new terrain
563 
564  // make sure that renderers for layers are re-created as well
565  const QList<QgsMapLayer *> layers = mMap.layers();
566  for ( QgsMapLayer *layer : layers )
567  {
568  // remove old entity - if any
569  removeLayerEntity( layer );
570 
571  // add new entity - if any 3D renderer
572  addLayerEntity( layer );
573  }
574 
575  mTerrainUpdateScheduled = false;
576 
577  connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
578  connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
579 
580  emit terrainEntityChanged();
581 }
582 
583 void Qgs3DMapScene::onBackgroundColorChanged()
584 {
585  mEngine->setClearColor( mMap.backgroundColor() );
586 }
587 
588 void Qgs3DMapScene::updateLights()
589 {
590  for ( Qt3DCore::QEntity *entity : std::as_const( mLightEntities ) )
591  entity->deleteLater();
592  mLightEntities.clear();
593  for ( Qt3DCore::QEntity *entity : std::as_const( mLightOriginEntities ) )
594  entity->deleteLater();
595  mLightOriginEntities.clear();
596 
597  auto createLightOriginEntity = [ = ]( QVector3D translation, const QColor & color )->Qt3DCore::QEntity *
598  {
599  Qt3DCore::QEntity *originEntity = new Qt3DCore::QEntity;
600 
601  Qt3DCore::QTransform *trLightOriginCenter = new Qt3DCore::QTransform;
602  trLightOriginCenter->setTranslation( translation );
603  originEntity->addComponent( trLightOriginCenter );
604 
605  Qt3DExtras::QPhongMaterial *materialLightOriginCenter = new Qt3DExtras::QPhongMaterial;
606  materialLightOriginCenter->setAmbient( color );
607  originEntity->addComponent( materialLightOriginCenter );
608 
609  Qt3DExtras::QSphereMesh *rendererLightOriginCenter = new Qt3DExtras::QSphereMesh;
610  rendererLightOriginCenter->setRadius( 20 );
611  originEntity->addComponent( rendererLightOriginCenter );
612 
613  originEntity->setEnabled( true );
614  originEntity->setParent( this );
615 
616  return originEntity;
617  };
618 
619  const auto newPointLights = mMap.pointLights();
620  for ( const QgsPointLightSettings &pointLightSettings : newPointLights )
621  {
622  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
623  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
624  lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
625  pointLightSettings.position().y(),
626  pointLightSettings.position().z() ) );
627 
628  Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
629  light->setColor( pointLightSettings.color() );
630  light->setIntensity( pointLightSettings.intensity() );
631 
632  light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
633  light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
634  light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
635 
636  lightEntity->addComponent( light );
637  lightEntity->addComponent( lightTransform );
638  lightEntity->setParent( this );
639  mLightEntities << lightEntity;
640 
641  if ( mMap.showLightSourceOrigins() )
642  mLightOriginEntities << createLightOriginEntity( lightTransform->translation(), pointLightSettings.color() );
643  }
644 
645  const auto newDirectionalLights = mMap.directionalLights();
646  for ( const QgsDirectionalLightSettings &directionalLightSettings : newDirectionalLights )
647  {
648  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
649  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
650 
651  Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight;
652  light->setColor( directionalLightSettings.color() );
653  light->setIntensity( directionalLightSettings.intensity() );
654  QgsVector3D direction = directionalLightSettings.direction();
655  light->setWorldDirection( QVector3D( direction.x(), direction.y(), direction.z() ) );
656 
657  lightEntity->addComponent( light );
658  lightEntity->addComponent( lightTransform );
659  lightEntity->setParent( this );
660  mLightEntities << lightEntity;
661  }
662 
663  onShadowSettingsChanged();
664 }
665 
666 void Qgs3DMapScene::updateCameraLens()
667 {
668  mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
669  mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
670  onCameraChanged();
671 }
672 
673 void Qgs3DMapScene::onRenderersChanged()
674 {
675  // remove entities (if any)
676  qDeleteAll( mRenderersEntities );
677  mRenderersEntities.clear();
678 
679  // re-add entities from new set of renderers
680  const QList<QgsAbstract3DRenderer *> renderers = mMap.renderers();
681  for ( const QgsAbstract3DRenderer *renderer : renderers )
682  {
683  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
684  if ( newEntity )
685  {
686  newEntity->setParent( this );
687  finalizeNewEntity( newEntity );
688  mRenderersEntities[renderer] = newEntity;
689  }
690  }
691 }
692 
693 void Qgs3DMapScene::onLayerRenderer3DChanged()
694 {
695  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
696  Q_ASSERT( layer );
697 
698  // remove old entity - if any
699  removeLayerEntity( layer );
700 
701  // add new entity - if any 3D renderer
702  addLayerEntity( layer );
703 }
704 
705 void Qgs3DMapScene::onLayersChanged()
706 {
707  QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
708  QList<QgsMapLayer *> layersAdded;
709  const QList<QgsMapLayer *> layers = mMap.layers();
710  for ( QgsMapLayer *layer : layers )
711  {
712  if ( !layersBefore.contains( layer ) )
713  {
714  layersAdded << layer;
715  }
716  else
717  {
718  layersBefore.remove( layer );
719  }
720  }
721 
722  // what is left in layersBefore are layers that have been removed
723  for ( QgsMapLayer *layer : std::as_const( layersBefore ) )
724  {
725  removeLayerEntity( layer );
726  }
727 
728  for ( QgsMapLayer *layer : std::as_const( layersAdded ) )
729  {
730  addLayerEntity( layer );
731  }
732 }
733 
735 {
736  for ( auto layer : mLayerEntities.keys() )
737  {
738  if ( layer->temporalProperties()->isActive() )
739  {
740  removeLayerEntity( layer );
741  addLayerEntity( layer );
742  }
743  }
744 }
745 
746 void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
747 {
748  bool needsSceneUpdate = false;
749  QgsAbstract3DRenderer *renderer = layer->renderer3D();
750  if ( renderer )
751  {
752  // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
753  // It has happened before that renderer pointed to a different layer (probably after copying a style).
754  // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
755  // the vector layer 3D renderer classes are not available.
756  if ( layer->type() == QgsMapLayerType::VectorLayer &&
757  ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
758  {
759  static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
760  if ( renderer->type() == QLatin1String( "vector" ) )
761  {
762  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
763  if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry )
764  {
765  const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
766  if ( pointSymbol->shape() == QgsPoint3DSymbol::Model )
767  {
768  mModelVectorLayers.append( layer );
769  }
770  }
771  }
772  else if ( renderer->type() == QLatin1String( "rulebased" ) )
773  {
774  const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
775  for ( auto rule : rules )
776  {
777  const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
778  if ( pointSymbol && pointSymbol->shape() == QgsPoint3DSymbol::Model )
779  {
780  mModelVectorLayers.append( layer );
781  break;
782  }
783  }
784  }
785  }
786  else if ( layer->type() == QgsMapLayerType::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
787  {
788  QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
789  meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
790 
791  // Before entity creation, set the maximum texture size
792  // Not very clean, but for now, only place found in the workflow to do that simple
793  QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
794  sym->setMaximumTextureSize( maximumTextureSize() );
795  meshRenderer->setSymbol( sym );
796  }
797  else if ( layer->type() == QgsMapLayerType::PointCloudLayer && renderer->type() == QLatin1String( "pointcloud" ) )
798  {
799  QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
800  pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
801  }
802 
803  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
804  if ( newEntity )
805  {
806  newEntity->setParent( this );
807  mLayerEntities.insert( layer, newEntity );
808 
809  finalizeNewEntity( newEntity );
810 
811  if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
812  {
813  mChunkEntities.append( chunkedNewEntity );
814  needsSceneUpdate = true;
815 
816  chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
817  connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject, this, &Qgs3DMapScene::onLayerEntityPickedObject );
818 
819  connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
820  {
821  finalizeNewEntity( entity );
822  } );
823 
824  connect( chunkedNewEntity, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
825  }
826  }
827  }
828 
829  if ( needsSceneUpdate )
830  onCameraChanged(); // needed for chunked entities
831 
832  connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
833 
834  if ( layer->type() == QgsMapLayerType::VectorLayer )
835  {
836  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
837  connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
838  connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
839  }
840 
841  if ( layer->type() == QgsMapLayerType::MeshLayer )
842  {
843  connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
844  }
845 
846  if ( layer->type() == QgsMapLayerType::PointCloudLayer )
847  connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
848 }
849 
850 void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
851 {
852  Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
853 
854  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
855  {
856  mChunkEntities.removeOne( chunkedEntity );
857  }
858 
859  if ( entity )
860  entity->deleteLater();
861 
862  disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
863 
864  if ( layer->type() == QgsMapLayerType::VectorLayer )
865  {
866  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
867  disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
868  disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
869  mModelVectorLayers.removeAll( layer );
870  }
871 
872  if ( layer->type() == QgsMapLayerType::MeshLayer )
873  {
874  disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
875  }
876 
877  if ( layer->type() == QgsMapLayerType::PointCloudLayer )
878  disconnect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
879 }
880 
881 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
882 {
883  // this is probably not the best place for material-specific configuration,
884  // maybe this could be more generalized when other materials need some specific treatment
885  for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
886  {
887  connect( mCameraController, &QgsCameraController::viewportChanged, lm, [lm, this]
888  {
889  lm->setViewportSize( mCameraController->viewport().size() );
890  } );
891 
892  lm->setViewportSize( cameraController()->viewport().size() );
893  }
894  // configure billboard's viewport when the viewport is changed.
895  for ( QgsPoint3DBillboardMaterial *bm : newEntity->findChildren<QgsPoint3DBillboardMaterial *>() )
896  {
897  connect( mCameraController, &QgsCameraController::viewportChanged, bm, [bm, this]
898  {
899  bm->setViewportSize( mCameraController->viewport().size() );
900  } );
901 
902  bm->setViewportSize( mCameraController->viewport().size() );
903  }
904 }
905 
906 int Qgs3DMapScene::maximumTextureSize() const
907 {
908  QSurface *surface = mEngine->surface();
909  QOpenGLContext context;
910  context.create();
911  context.makeCurrent( surface );
912  QOpenGLFunctions openglFunctions( &context );
913  GLint size;
914  openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
915  return int( size );
916 }
917 
918 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
919 {
920  mEntityCameraViewCenter = new Qt3DCore::QEntity;
921 
922  Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
923  mEntityCameraViewCenter->addComponent( trCameraViewCenter );
924  connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
925  {
926  trCameraViewCenter->setTranslation( camera->viewCenter() );
927  } );
928 
929  Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
930  materialCameraViewCenter->setAmbient( Qt::red );
931  mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
932 
933  Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
934  rendererCameraViewCenter->setRadius( 10 );
935  mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
936 
937  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
938  mEntityCameraViewCenter->setParent( this );
939 
940  connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
941  {
942  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
943  } );
944 }
945 
946 void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
947 {
948  if ( mSceneState == state )
949  return;
950  mSceneState = state;
951  emit sceneStateChanged();
952 }
953 
954 void Qgs3DMapScene::updateSceneState()
955 {
956  if ( mTerrainUpdateScheduled )
957  {
958  setSceneState( Updating );
959  return;
960  }
961 
962  for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
963  {
964  if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
965  {
966  setSceneState( Updating );
967  return;
968  }
969  }
970 
971  setSceneState( Ready );
972 }
973 
974 void Qgs3DMapScene::onSkyboxSettingsChanged()
975 {
976  QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
977  if ( mSkybox != nullptr )
978  {
979  mSkybox->deleteLater();
980  mSkybox = nullptr;
981  }
982 
983  mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
984 
985  if ( mMap.isSkyboxEnabled() )
986  {
987  QMap<QString, QString> faces;
988  switch ( skyboxSettings.skyboxType() )
989  {
991  faces = skyboxSettings.cubeMapFacesPaths();
992  mSkybox = new QgsCubeFacesSkyboxEntity(
993  faces[QStringLiteral( "posX" )], faces[QStringLiteral( "posY" )], faces[QStringLiteral( "posZ" )],
994  faces[QStringLiteral( "negX" )], faces[QStringLiteral( "negY" )], faces[QStringLiteral( "negZ" )],
995  this
996  );
997  break;
999  mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
1000  break;
1001  }
1002  }
1003 }
1004 
1005 void Qgs3DMapScene::onShadowSettingsChanged()
1006 {
1007  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1008 
1009  QList<QgsDirectionalLightSettings> directionalLights = mMap.directionalLights();
1010  QgsShadowSettings shadowSettings = mMap.shadowSettings();
1011  int selectedLight = shadowSettings.selectedDirectionalLight();
1012  if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLights.count() )
1013  {
1014  shadowRenderingFrameGraph->setShadowRenderingEnabled( true );
1015  shadowRenderingFrameGraph->setShadowBias( shadowSettings.shadowBias() );
1016  shadowRenderingFrameGraph->setShadowMapResolution( shadowSettings.shadowMapResolution() );
1017  QgsDirectionalLightSettings light = directionalLights[selectedLight];
1018  shadowRenderingFrameGraph->setupDirectionalLight( light, shadowSettings.maximumShadowRenderingDistance() );
1019  }
1020  else
1021  shadowRenderingFrameGraph->setShadowRenderingEnabled( false );
1022 }
1023 
1024 void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
1025 {
1026  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1027  shadowRenderingFrameGraph->setupShadowMapDebugging( mMap.debugShadowMapEnabled(), mMap.debugShadowMapCorner(), mMap.debugShadowMapSize() );
1028 }
1029 
1030 void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1031 {
1032  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1033  shadowRenderingFrameGraph->setupDepthMapDebugging( mMap.debugDepthMapEnabled(), mMap.debugDepthMapCorner(), mMap.debugDepthMapSize() );
1034 }
1035 
1036 void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1037 {
1038  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1039 
1040  bool edlEnabled = mMap.eyeDomeLightingEnabled();
1041  double edlStrength = mMap.eyeDomeLightingStrength();
1042  double edlDistance = mMap.eyeDomeLightingDistance();
1043  shadowRenderingFrameGraph->setupEyeDomeLighting( edlEnabled, edlStrength, edlDistance );
1044 }
1045 
1046 void Qgs3DMapScene::onCameraMovementSpeedChanged()
1047 {
1048  mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1049 }
1050 
1052 {
1053  QVector<QString> notParsedLayers;
1054  Qgs3DSceneExporter exporter;
1055 
1056  exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1057  exporter.setSmoothEdges( exportSettings.smoothEdges() );
1058  exporter.setExportNormals( exportSettings.exportNormals() );
1059  exporter.setExportTextures( exportSettings.exportTextures() );
1060  exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1061  exporter.setScale( exportSettings.scale() );
1062 
1063  for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1064  {
1065  QgsMapLayer *layer = it.key();
1066  Qt3DCore::QEntity *rootEntity = it.value();
1067  QgsMapLayerType layerType = layer->type();
1068  switch ( layerType )
1069  {
1071  if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1072  notParsedLayers.push_back( layer->name() );
1073  break;
1080  notParsedLayers.push_back( layer->name() );
1081  break;
1082  }
1083  }
1084 
1085  if ( mTerrain )
1086  exporter.parseTerrain( mTerrain, "Terrain" );
1087 
1088  exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1089 
1090  if ( !notParsedLayers.empty() )
1091  {
1092  QString message = tr( "The following layers were not exported:" ) + "\n";
1093  for ( const QString &layerName : notParsedLayers )
1094  message += layerName + "\n";
1095  QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText );
1096  }
1097 }
1098 
1099 QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1100 {
1101  QVector<const QgsChunkNode *> chunks;
1102  if ( !mLayerEntities.contains( layer ) ) return chunks;
1103  if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[ layer ] ) )
1104  {
1105  for ( QgsChunkNode *n : c->activeNodes() )
1106  chunks.push_back( n );
1107  }
1108  return chunks;
1109 }
Manages the various settings the user can choose from when exporting a 3D scene 3.
bool exportNormals() const
Returns whether normals will be exported.
int terrrainResolution() const
Returns the terrain resolution.
QString sceneFolderPath() const
Returns the scene folder path.
float scale() const
Returns the scale of the exported model.
int terrainTextureResolution() const
Returns the terrain texture resolution.
QString sceneName() const
Returns the scene name.
bool smoothEdges() const
Returns whether triangles edges will look smooth.
bool exportTextures() const
Returns whether textures will be exported.
void unregisterPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object pick...
QVector< const QgsChunkNode * > getLayerActiveChunkNodes(QgsMapLayer *layer)
Returns the active chunk nodes of layer.
void exportScene(const Qgs3DMapExportSettings &exportSettings)
Exports the scene according to the scene export settings.
void terrainPendingJobsCountChanged()
Emitted when the number of terrain's pending jobs changes.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes.
void registerPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Registers an object that will get results of pick events on 3D entities. Does not take ownership of t...
SceneState
Enumeration of possible states of the 3D scene.
Definition: qgs3dmapscene.h:95
@ Ready
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:96
@ Updating
The scene is still being loaded/updated.
Definition: qgs3dmapscene.h:97
int totalPendingJobsCount() const
Returns number of pending jobs for all chunked entities.
void updateTemporal()
Updates the temporale entities.
void totalPendingJobsCountChanged()
Emitted when the total number of pending jobs changes.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is activated or deactivated.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:77
void sceneStateChanged()
Emitted when the scene's state has changed.
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
float worldSpaceError(float epsilon, float distance)
Given screen error (in pixels) and distance from camera (in 3D world coordinates),...
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
Qgs3DMapScene(const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine)
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
void mapTileResolutionChanged()
Emitted when the map tile resoulution has changed.
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
Qt::Corner debugDepthMapCorner() const
Returns the corner where the shadow map preview is displayed.
void renderersChanged()
Emitted when the list of map's extra renderers have been modified.
QList< QgsAbstract3DRenderer * > renderers() const
Returns list of extra 3D renderers.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
void eyeDomeLightingDistanceChanged()
Emitted when the eye dome lighting distance has changed.
void terrainShadingChanged()
Emitted when terrain shading enabled flag or terrain shading material has changed.
double cameraMovementSpeed() const
Returns the camera movement speed.
Qt3DRender::QCameraLens::ProjectionType projectionType() const
Returns the camera lens' projection type.
bool debugDepthMapEnabled() const
Returns whether the shadow map debugging is enabled.
bool isSkyboxEnabled() const
Returns whether the skybox is enabled.
void debugDepthMapSettingsChanged()
Emitted when depth map debugging has changed.
QList< QgsDirectionalLightSettings > directionalLights() const
Returns list of directional lights defined in the scene.
double eyeDomeLightingStrength() const
Returns the eye dome lighting strength value.
void backgroundColorChanged()
Emitted when the background color has changed.
Qt::Corner debugShadowMapCorner() const
Returns the corner where the shadow map preview is displayed.
bool showCameraViewCenter() const
Returns whether to show camera's view center as a sphere (for debugging)
void directionalLightsChanged()
Emitted when the list of directional lights changes.
void shadowSettingsChanged()
Emitted when shadow rendering settings are changed.
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
void eyeDomeLightingEnabledChanged()
Emitted when the flag whether eye dome lighting is used has changed.
void skyboxSettingsChanged()
Emitted when skybox settings are changed.
QgsShadowSettings shadowSettings() const
Returns the current configuration of shadows.
void pointLightsChanged()
Emitted when the list of point lights changes.
double debugDepthMapSize() const
Returns the size of the shadow map preview.
void projectionTypeChanged()
Emitted when the camera lens projection type changes.
float fieldOfView() const
Returns the camera lens' field of view.
int eyeDomeLightingDistance() const
Returns the eye dome lighting distance value (contributes to the contrast of the image)
void showLightSourceOriginsChanged()
Emitted when the flag whether light source origins are shown has changed.
QColor backgroundColor() const
Returns background color of the 3D map view.
double debugShadowMapSize() const
Returns the size of the shadow map preview.
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
void maxTerrainScreenErrorChanged()
Emitted when the maximum terrain screen error has changed.
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
bool debugShadowMapEnabled() const
Returns whether the shadow map debugging is enabled.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is enabled or disabled.
void layersChanged()
Emitted when the list of map layers for 3d rendering has changed.
void eyeDomeLightingStrengthChanged()
Emitted when the eye dome lighting strength has changed.
QgsSkyboxSettings skyboxSettings() const
Returns the current configuration of the skybox.
void cameraMovementSpeedChanged()
Emitted when the camera movement speed was changed.
bool eyeDomeLightingEnabled() const
Returns whether eye dome lighting is used.
bool isFpsCounterEnabled() const
Returns whether FPS counter label is enabled.
void fieldOfViewChanged()
Emitted when the camera lens field of view changes.
QList< QgsPointLightSettings > pointLights() const
Returns list of point lights defined in the scene.
QList< QgsMapLayer * > layers() const
Returns the list of 3D map layers to be rendered in the scene.
QgsCameraController::NavigationMode cameraNavigationMode() const
Returns the navigation mode used by the camera.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
bool showLightSourceOrigins() const
Returns whether to show light source origins as a sphere (for debugging)
void debugShadowMapSettingsChanged()
Emitted when shadow map debugging has changed.
void showCameraViewCenterChanged()
Emitted when the flag whether camera's view center is shown has changed.
void maxTerrainGroundErrorChanged()
Emitted when the maximum terrain ground error has changed.
Entity that handles the exporting of 3D scene.
void setExportTextures(bool exportTextures)
Sets whether the textures will be exported.
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
void save(const QString &sceneName, const QString &sceneFolderPath)
Saves the scene to a .obj file.
void setTerrainResolution(int resolution)
Sets the terrain resolution.
void setTerrainTextureResolution(int resolution)
Sets the terrain texture resolution.
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
void setScale(float scale)
Sets the scale of the exported 3D model.
void setExportNormals(bool exportNormals)
Sets whether the normals will be exported.
void setSmoothEdges(bool smoothEdges)
Sets whether the triangles will look smooth.
static int maxZoomLevel(double tile0width, double tileResolution, double maxError)
Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom le...
Definition: qgs3dutils.cpp:174
3
Definition: qgsaabb.h:34
float yMax
Definition: qgsaabb.h:85
float xMax
Definition: qgsaabb.h:84
float xMin
Definition: qgsaabb.h:81
float zMax
Definition: qgsaabb.h:86
float yMin
Definition: qgsaabb.h:82
float zMin
Definition: qgsaabb.h:83
virtual void setClearColor(const QColor &color)=0
Sets background color of the scene.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
virtual Qt3DRender::QCamera * camera()=0
Returns pointer to the engine's camera entity.
virtual QSurface * surface() const =0
Returns the surface of the engine.
QgsShadowRenderingFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
virtual void setFrustumCullingEnabled(bool enabled)=0
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
Base class for all renderers that may to participate in 3D view.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
virtual Qt3DCore::QEntity * createEntity(const Qgs3DMapSettings &map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates.
void setCameraNavigationMode(QgsCameraController::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
Qt3DRender::QCamera * camera
float distance() const
Returns distance of the camera from the point it is looking at.
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene.
void cameraChanged()
Emitted when camera has been updated.
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates)
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void viewportChanged()
Emitted when viewport rectangle has been updated.
A skybox constructed from a 6 cube faces.
Base class for all map layer types.
Definition: qgsmaplayer.h:70
QString name
Definition: qgsmaplayer.h:73
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
QgsMapLayerType type
Definition: qgsmaplayer.h:77
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
void rendererChanged()
Signal emitted when renderer is changed.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1258
void setMaximumTextureSize(int maximumTextureSize)
Sets the maximum texture size supported by the hardware Used to store the GL_MAX_TEXTURE_SIZE value t...
QgsMesh3DSymbol * clone() const override SIP_FACTORY
Returns a new instance of the symbol with the same settings.
3D renderer that renders all mesh triangles of a mesh layer.
void setSymbol(QgsMesh3DSymbol *symbol)
Sets 3D symbol associated with the renderer.
const QgsMesh3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
void setLayer(QgsMeshLayer *layer)
Sets vector layer associated with the renderer.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
A skybox constructed from a panoramic image.
Shape shape() const
Returns 3D shape for points.
QVariantMap shapeProperties() const
Returns a key-value dictionary of point shape properties.
3D renderer that renders all points from a point cloud layer
void setLayer(QgsPointCloudLayer *layer)
Sets point cloud layer associated with the renderer.
Represents a map layer supporting display of point clouds.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QList< QgsRuleBased3DRenderer::Rule * > RuleList
void setupDepthMapDebugging(bool enabled, Qt::Corner corner, double size)
Sets the depth map debugging view port.
void setupShadowMapDebugging(bool enabled, Qt::Corner corner, double size)
Sets the shadow map debugging view port.
void setShadowBias(float shadowBias)
Sets the shadow bias value.
void setShadowMapResolution(int resolution)
Sets the resolution of the shadow map.
void setupEyeDomeLighting(bool enabled, double strength, int distance)
Sets eye dome lighting shading related settings.
void setupDirectionalLight(const QgsDirectionalLightSettings &light, float maximumShadowRenderingDistance)
Sets shadow rendering to use a directional light.
void setShadowRenderingEnabled(bool enabled)
Sets whether the shadow rendering is enabled.
class containing the configuration of shadows rendering 3
int selectedDirectionalLight() const
Returns the selected direcctional light used to cast shadows.
bool renderShadows() const
Returns whether shadow rendering is enabled.
int shadowMapResolution() const
Returns the resolution of the shadow map texture used to generate the shadows.
double maximumShadowRenderingDistance() const
Returns the maximum shadow rendering distance accounted for when rendering shadows Objects further aw...
double shadowBias() const
Returns the shadow bias used to correct the numerical imprecision of shadows (for the depth test) Thi...
Contains the configuration of a skybox entity.
QgsSkyboxEntity::SkyboxType skyboxType() const
Returns the type of the skybox.
QString panoramicTexturePath() const
Returns the panoramic texture path of a skybox of type "Panormaic skybox".
QMap< QString, QString > cubeMapFacesPaths() const
Returns a map containing the path of each texture specified by the user.
void remoteSourceFetched(const QString &url)
Emitted when the cache has finished retrieving a 3D model from a remote url.
bool isActive() const
Returns true if the temporal property is active.
virtual QgsRectangle extent() const =0
extent of the terrain in terrain's CRS
virtual float rootChunkError(const Qgs3DMapSettings &map) const
Returns error of the root chunk in world coordinates.
virtual QgsAABB rootChunkBbox(const Qgs3DMapSettings &map) const
Returns bounding box of the root chunk.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
3D renderer that renders all features of a vector layer with the same 3D symbol.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void layerModified()
Emitted when modifications has been done on layer.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:46
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition: qgis.h:615
void addQLayerComponentsToHierarchy(Qt3DCore::QEntity *entity, const QVector< Qt3DRender::QLayer * > layers)
QgsChunkedEntity::SceneState _sceneState(QgsCameraController *cameraController)
void removeQLayerComponentsFromHierarchy(Qt3DCore::QEntity *entity)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39