QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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  // TODO: strange - setting OnDemand render policy still keeps QGIS busy (Qt 5.9.0)
95  // actually it is more busy than with the default "Always" policy although there are no changes in the scene.
96  //mRenderer->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
97 
98 #if QT_VERSION >= 0x050900
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 #endif
102 
103  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
104 
105  // Camera
106  float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
107  mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
108 
109  mFrameAction = new Qt3DLogic::QFrameAction();
110  connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
111  this, &Qgs3DMapScene::onFrameTriggered );
112  addComponent( mFrameAction ); // takes ownership
113 
114  // Camera controlling
115  mCameraController = new QgsCameraController( this ); // attaches to the scene
116  mCameraController->setViewport( viewportRect );
117  mCameraController->setCamera( mEngine->camera() );
118  mCameraController->resetView( 1000 );
119 
120  addCameraViewCenterEntity( mEngine->camera() );
121  updateLights();
122 
123  // create terrain entity
124 
125  createTerrainDeferred();
126  connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
127  connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
128  connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
129  connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
130  connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
131  connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
132  connect( &map, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapScene::updateLights );
133  connect( &map, &Qgs3DMapSettings::directionalLightsChanged, this, &Qgs3DMapScene::updateLights );
134  connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
135  connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
136  connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
137  connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged );
138  connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
139  connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
140  connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
141  connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
142  connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
143  connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
144  connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
146  connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
147 
148  connect( QgsApplication::instance()->sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
149  {
150  const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
151  for ( QgsMapLayer *layer : modelVectorLayers )
152  {
153  QgsAbstract3DRenderer *renderer = layer->renderer3D();
154  if ( renderer )
155  {
156  if ( renderer->type() == QLatin1String( "vector" ) )
157  {
158  const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
159  if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
160  {
161  removeLayerEntity( layer );
162  addLayerEntity( layer );
163  }
164  }
165  else if ( renderer->type() == QLatin1String( "rulebased" ) )
166  {
167  const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
168  for ( auto rule : rules )
169  {
170  const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
171  if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
172  {
173  removeLayerEntity( layer );
174  addLayerEntity( layer );
175  break;
176  }
177  }
178  }
179  }
180  }
181  } );
182 
183  // create entities of renderers
184 
185  onRenderersChanged();
186 
187  // listen to changes of layers in order to add/remove 3D renderer entities
188  connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
189 
190 
191 #if 0
192  ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
193  testChunkEntity->setEnabled( false );
194  testChunkEntity->setParent( this );
195  chunkEntities << testChunkEntity;
196 #endif
197 
198  connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
199  connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged );
200 
201 #if 0
202  // experiments with loading of existing 3D models.
203 
204  // scene loader only gets loaded only when added to a scene...
205  // it loads everything: geometries, materials, transforms, lights, cameras (if any)
206  Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity;
207  Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader;
208  loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
209  loaderEntity->addComponent( loader );
210  loaderEntity->setParent( this );
211 
212  // mesh loads just geometry as one geometry...
213  // so if there are different materials (e.g. colors) used in the model, this information is lost
214  Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity;
215  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
216  mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
217  meshEntity->addComponent( mesh );
218  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
219  material->setAmbient( Qt::red );
220  meshEntity->addComponent( material );
221  Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform;
222  meshTransform->setScale( 1 );
223  meshEntity->addComponent( meshTransform );
224  meshEntity->setParent( this );
225 #endif
226  onSkyboxSettingsChanged();
227 
228  // force initial update of chunked entities
229  onCameraChanged();
230  // force initial update of eye dome shading
231  onEyeDomeShadingSettingsChanged();
232  // force initial update of debugging setting of preview quads
233  onDebugShadowMapSettingsChanged();
234  onDebugDepthMapSettingsChanged();
235 
236  mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
237  onCameraMovementSpeedChanged();
238 }
239 
241 {
242  QgsRectangle extent = mMap.terrainGenerator()->extent();
243  float side = std::max( extent.width(), extent.height() );
244  mCameraController->resetView( side ); // assuming FOV being 45 degrees
245 }
246 
248 {
249  return mTerrain ? mTerrain->pendingJobsCount() : 0;
250 }
251 
253 {
254  int count = 0;
255  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
256  count += entity->pendingJobsCount();
257  return count;
258 }
259 
261 {
262  if ( mPickHandlers.isEmpty() )
263  {
264  // we need to add object pickers
265  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
266  {
267  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
268  chunkedEntity->setPickingEnabled( true );
269  }
270  }
271 
272  mPickHandlers.append( pickHandler );
273 }
274 
276 {
277  mPickHandlers.removeOne( pickHandler );
278 
279  if ( mPickHandlers.isEmpty() )
280  {
281  // we need to remove pickers
282  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
283  {
284  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
285  chunkedEntity->setPickingEnabled( false );
286  }
287  }
288 }
289 
290 void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid )
291 {
292  QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
293  if ( !layer )
294  return;
295 
296  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
297  if ( !vlayer )
298  return;
299 
300  for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
301  {
302  pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
303  }
304 }
305 
306 float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
307 {
308  Qt3DRender::QCamera *camera = mCameraController->camera();
309  float fov = camera->fieldOfView();
310  QRect rect = mCameraController->viewport();
311  float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
312 
313  // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
314  // with explanation of the math.
315  float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
316  float err = frustumWidthAtDistance * epsilon / screenSizePx;
317  return err;
318 }
319 
320 QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
321 {
322  Qt3DRender::QCamera *camera = cameraController->camera();
323  QgsChunkedEntity::SceneState state;
324  state.cameraFov = camera->fieldOfView();
325  state.cameraPos = camera->position();
326  QRect rect = cameraController->viewport();
327  state.screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
328  state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
329  return state;
330 }
331 
332 void Qgs3DMapScene::onCameraChanged()
333 {
334  if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
335  {
336  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
337  const float viewWidthFromCenter = mCameraController->distance();
338  const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
339  mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
340  }
341 
342  updateScene();
343  bool changedCameraPlanes = updateCameraNearFarPlanes();
344 
345  if ( changedCameraPlanes )
346  {
347  // repeat update of entities - because we have updated camera's near/far planes,
348  // the active nodes may have changed as well
349  updateScene();
350  updateCameraNearFarPlanes();
351  }
352 
353  onShadowSettingsChanged();
354 }
355 
356 void removeQLayerComponentsFromHierarchy( Qt3DCore::QEntity *entity )
357 {
358  QVector<Qt3DCore::QComponent *> toBeRemovedComponents;
359  for ( Qt3DCore::QComponent *component : entity->components() )
360  {
361  Qt3DRender::QLayer *layer = qobject_cast<Qt3DRender::QLayer *>( component );
362  if ( layer != nullptr )
363  toBeRemovedComponents.push_back( layer );
364  }
365  for ( Qt3DCore::QComponent *component : toBeRemovedComponents )
366  entity->removeComponent( component );
367  for ( Qt3DCore::QEntity *obj : entity->findChildren<Qt3DCore::QEntity *>() )
368  {
369  if ( obj != nullptr )
371  }
372 }
373 
374 void addQLayerComponentsToHierarchy( Qt3DCore::QEntity *entity, const QVector<Qt3DRender::QLayer *> layers )
375 {
376  for ( Qt3DRender::QLayer *layer : layers )
377  entity->addComponent( layer );
378  for ( Qt3DCore::QEntity *child : entity->findChildren<Qt3DCore::QEntity *>() )
379  {
380  if ( child != nullptr )
381  addQLayerComponentsToHierarchy( child, layers );
382  }
383 }
384 
385 void Qgs3DMapScene::updateScene()
386 {
387  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );
388  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
389  {
390  if ( entity->isEnabled() )
391  entity->update( _sceneState( mCameraController ) );
392  }
393 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
394  QgsWindow3DEngine *windowEngine = qobject_cast<QgsWindow3DEngine *>( mEngine );
395  if ( windowEngine != nullptr )
396  {
397  QVector<Qt3DRender::QLayer *> layers;
398  layers.push_back( windowEngine->shadowRenderingFrameGraph()->castShadowsLayer() );
399  layers.push_back( windowEngine->shadowRenderingFrameGraph()->forwardRenderLayer() );
401  addQLayerComponentsToHierarchy( this, layers );
402  }
403 #endif
404  updateSceneState();
405 }
406 
407 static void _updateNearFarPlane( const QList<QgsChunkNode *> &activeNodes, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar )
408 {
409  for ( QgsChunkNode *node : activeNodes )
410  {
411  // project each corner of bbox to camera coordinates
412  // and determine closest and farthest point.
413  QgsAABB bbox = node->bbox();
414  for ( int i = 0; i < 8; ++i )
415  {
416  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
417  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
418  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
419  QVector4D pc = viewMatrix * p;
420 
421  float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
422  if ( dst < fnear )
423  fnear = dst;
424  if ( dst > ffar )
425  ffar = dst;
426  }
427  }
428 }
429 
430 bool Qgs3DMapScene::updateCameraNearFarPlanes()
431 {
432  // Update near and far plane from the terrain.
433  // this needs to be done with great care as we have kind of circular dependency here:
434  // active nodes are culled based on the current frustum (which involves near + far plane)
435  // and then based on active nodes we set near and far plane.
436  //
437  // All of this is just heuristics assuming that all other stuff is being rendered somewhere
438  // around the area where the terrain is.
439  //
440  // Near/far plane is setup in order to make best use of the depth buffer to avoid:
441  // 1. precision errors - if the range is too great
442  // 2. unwanted clipping of scene - if the range is too small
443 
444  if ( mTerrain )
445  {
446  Qt3DRender::QCamera *camera = cameraController()->camera();
447  QMatrix4x4 viewMatrix = camera->viewMatrix();
448  float fnear = 1e9;
449  float ffar = 0;
450 
451  QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
452 
453  // it could be that there are no active nodes - they could be all culled or because root node
454  // is not yet loaded - we still need at least something to understand bounds of our scene
455  // so lets use the root node
456  if ( activeNodes.isEmpty() )
457  activeNodes << mTerrain->rootNode();
458 
459  _updateNearFarPlane( activeNodes, viewMatrix, fnear, ffar );
460 
461  // Also involve all the other chunked entities to make sure that they will not get
462  // clipped by the near or far plane
463  for ( QgsChunkedEntity *e : qgis::as_const( mChunkEntities ) )
464  {
465  if ( e != mTerrain )
466  _updateNearFarPlane( e->activeNodes(), viewMatrix, fnear, ffar );
467  }
468 
469  if ( fnear < 1 )
470  fnear = 1; // does not really make sense to use negative far plane (behind camera)
471 
472  if ( fnear == 1e9 && ffar == 0 )
473  {
474  // the update didn't work out... this should not happen
475  // well at least temporarily use some conservative starting values
476  qWarning() << "oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
477  fnear = 1;
478  ffar = 1e9;
479  }
480 
481  // set near/far plane - with some tolerance in front/behind expected near/far planes
482  float newFar = ffar * 2;
483  float newNear = fnear / 2;
484  if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
485  {
486  camera->setFarPlane( newFar );
487  camera->setNearPlane( newNear );
488  return true;
489  }
490  }
491  else
492  qWarning() << "no terrain - not setting near/far plane";
493 
494  return false;
495 }
496 
497 void Qgs3DMapScene::onFrameTriggered( float dt )
498 {
499  mCameraController->frameTriggered( dt );
500 
501  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
502  {
503  if ( entity->isEnabled() && entity->needsUpdate() )
504  {
505  QgsDebugMsgLevel( QStringLiteral( "need for update" ), 2 );
506  entity->update( _sceneState( mCameraController ) );
507  }
508  }
509 
510  updateSceneState();
511 
512  // lock changing the FPS counter to 5 fps
513  static int frameCount = 0;
514  static float accumulatedTime = 0.0f;
515 
516  if ( !mMap.isFpsCounterEnabled() )
517  {
518  frameCount = 0;
519  accumulatedTime = 0;
520  return;
521  }
522 
523  frameCount++;
524  accumulatedTime += dt;
525  if ( accumulatedTime >= 0.2f )
526  {
527  float fps = ( float )frameCount / accumulatedTime;
528  frameCount = 0;
529  accumulatedTime = 0.0f;
530  emit fpsCountChanged( fps );
531  }
532 }
533 
534 void Qgs3DMapScene::createTerrain()
535 {
536  if ( mTerrain )
537  {
538  mChunkEntities.removeOne( mTerrain );
539 
540  mTerrain->deleteLater();
541  mTerrain = nullptr;
542 
543  emit terrainEntityChanged();
544  }
545 
546  if ( !mTerrainUpdateScheduled )
547  {
548  // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
549  QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
550  mTerrainUpdateScheduled = true;
551  setSceneState( Updating );
552  }
553 }
554 
555 void Qgs3DMapScene::createTerrainDeferred()
556 {
557  double tile0width = mMap.terrainGenerator()->extent().width();
558  int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
559  QgsAABB rootBbox = mMap.terrainGenerator()->rootChunkBbox( mMap );
560  float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
561  mMap.terrainGenerator()->setupQuadtree( rootBbox, rootError, maxZoomLevel );
562 
563  mTerrain = new QgsTerrainEntity( mMap );
564  //mTerrain->setEnabled(false);
565  mTerrain->setParent( this );
566 
567  if ( mMap.showTerrainBoundingBoxes() )
568  mTerrain->setShowBoundingBoxes( true );
569 
570  mCameraController->setTerrainEntity( mTerrain );
571 
572  mChunkEntities << mTerrain;
573 
574  onCameraChanged(); // force update of the new terrain
575 
576  // make sure that renderers for layers are re-created as well
577  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
578  {
579  // remove old entity - if any
580  removeLayerEntity( layer );
581 
582  // add new entity - if any 3D renderer
583  addLayerEntity( layer );
584  }
585 
586  mTerrainUpdateScheduled = false;
587 
588  connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
589  connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
590 
591  emit terrainEntityChanged();
592 }
593 
594 void Qgs3DMapScene::onBackgroundColorChanged()
595 {
596  mEngine->setClearColor( mMap.backgroundColor() );
597 }
598 
599 void Qgs3DMapScene::updateLights()
600 {
601  for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
602  entity->deleteLater();
603  mLightEntities.clear();
604  for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightOriginEntities ) )
605  entity->deleteLater();
606  mLightOriginEntities.clear();
607 
608  auto createLightOriginEntity = [ = ]( QVector3D translation, const QColor & color )->Qt3DCore::QEntity *
609  {
610  Qt3DCore::QEntity *originEntity = new Qt3DCore::QEntity;
611 
612  Qt3DCore::QTransform *trLightOriginCenter = new Qt3DCore::QTransform;
613  trLightOriginCenter->setTranslation( translation );
614  originEntity->addComponent( trLightOriginCenter );
615 
616  Qt3DExtras::QPhongMaterial *materialLightOriginCenter = new Qt3DExtras::QPhongMaterial;
617  materialLightOriginCenter->setAmbient( color );
618  originEntity->addComponent( materialLightOriginCenter );
619 
620  Qt3DExtras::QSphereMesh *rendererLightOriginCenter = new Qt3DExtras::QSphereMesh;
621  rendererLightOriginCenter->setRadius( 20 );
622  originEntity->addComponent( rendererLightOriginCenter );
623 
624  originEntity->setEnabled( true );
625  originEntity->setParent( this );
626 
627  return originEntity;
628  };
629 
630  const auto newPointLights = mMap.pointLights();
631  for ( const QgsPointLightSettings &pointLightSettings : newPointLights )
632  {
633  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
634  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
635  lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
636  pointLightSettings.position().y(),
637  pointLightSettings.position().z() ) );
638 
639  Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
640  light->setColor( pointLightSettings.color() );
641  light->setIntensity( pointLightSettings.intensity() );
642 
643  light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
644  light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
645  light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
646 
647  lightEntity->addComponent( light );
648  lightEntity->addComponent( lightTransform );
649  lightEntity->setParent( this );
650  mLightEntities << lightEntity;
651 
652  if ( mMap.showLightSourceOrigins() )
653  mLightOriginEntities << createLightOriginEntity( lightTransform->translation(), pointLightSettings.color() );
654  }
655 
656  const auto newDirectionalLights = mMap.directionalLights();
657  for ( const QgsDirectionalLightSettings &directionalLightSettings : newDirectionalLights )
658  {
659  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
660  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
661 
662  Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight;
663  light->setColor( directionalLightSettings.color() );
664  light->setIntensity( directionalLightSettings.intensity() );
665  QgsVector3D direction = directionalLightSettings.direction();
666  light->setWorldDirection( QVector3D( direction.x(), direction.y(), direction.z() ) );
667 
668  lightEntity->addComponent( light );
669  lightEntity->addComponent( lightTransform );
670  lightEntity->setParent( this );
671  mLightEntities << lightEntity;
672  }
673 
674  onShadowSettingsChanged();
675 }
676 
677 void Qgs3DMapScene::updateCameraLens()
678 {
679  mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
680  mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
681  onCameraChanged();
682 }
683 
684 void Qgs3DMapScene::onRenderersChanged()
685 {
686  // remove entities (if any)
687  qDeleteAll( mRenderersEntities );
688  mRenderersEntities.clear();
689 
690  // re-add entities from new set of renderers
691  const QList<QgsAbstract3DRenderer *> renderers = mMap.renderers();
692  for ( const QgsAbstract3DRenderer *renderer : renderers )
693  {
694  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
695  if ( newEntity )
696  {
697  newEntity->setParent( this );
698  finalizeNewEntity( newEntity );
699  mRenderersEntities[renderer] = newEntity;
700  }
701  }
702 }
703 
704 void Qgs3DMapScene::onLayerRenderer3DChanged()
705 {
706  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
707  Q_ASSERT( layer );
708 
709  // remove old entity - if any
710  removeLayerEntity( layer );
711 
712  // add new entity - if any 3D renderer
713  addLayerEntity( layer );
714 }
715 
716 void Qgs3DMapScene::onLayersChanged()
717 {
718  QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
719  QList<QgsMapLayer *> layersAdded;
720  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
721  {
722  if ( !layersBefore.contains( layer ) )
723  {
724  layersAdded << layer;
725  }
726  else
727  {
728  layersBefore.remove( layer );
729  }
730  }
731 
732  // what is left in layersBefore are layers that have been removed
733  Q_FOREACH ( QgsMapLayer *layer, layersBefore )
734  {
735  removeLayerEntity( layer );
736  }
737 
738  Q_FOREACH ( QgsMapLayer *layer, layersAdded )
739  {
740  addLayerEntity( layer );
741  }
742 }
743 
745 {
746  for ( auto layer : mLayerEntities.keys() )
747  {
748  if ( layer->temporalProperties()->isActive() )
749  {
750  removeLayerEntity( layer );
751  addLayerEntity( layer );
752  }
753  }
754 }
755 
756 void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
757 {
758  bool needsSceneUpdate = false;
759  QgsAbstract3DRenderer *renderer = layer->renderer3D();
760  if ( renderer )
761  {
762  // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
763  // It has happened before that renderer pointed to a different layer (probably after copying a style).
764  // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
765  // the vector layer 3D renderer classes are not available.
766  if ( layer->type() == QgsMapLayerType::VectorLayer &&
767  ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
768  {
769  static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
770  if ( renderer->type() == QLatin1String( "vector" ) )
771  {
772  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
773  if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry )
774  {
775  const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
776  if ( pointSymbol->shape() == QgsPoint3DSymbol::Model )
777  {
778  mModelVectorLayers.append( layer );
779  }
780  }
781  }
782  else if ( renderer->type() == QLatin1String( "rulebased" ) )
783  {
784  const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
785  for ( auto rule : rules )
786  {
787  const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
788  if ( pointSymbol && pointSymbol->shape() == QgsPoint3DSymbol::Model )
789  {
790  mModelVectorLayers.append( layer );
791  break;
792  }
793  }
794  }
795  }
796  else if ( layer->type() == QgsMapLayerType::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
797  {
798  QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
799  meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
800 
801  // Before entity creation, set the maximum texture size
802  // Not very clean, but for now, only place found in the workflow to do that simple
803  QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
804  sym->setMaximumTextureSize( maximumTextureSize() );
805  meshRenderer->setSymbol( sym );
806  }
807  else if ( layer->type() == QgsMapLayerType::PointCloudLayer && renderer->type() == QLatin1String( "pointcloud" ) )
808  {
809  QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
810  pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
811  }
812 
813  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
814  if ( newEntity )
815  {
816  newEntity->setParent( this );
817  mLayerEntities.insert( layer, newEntity );
818 
819  finalizeNewEntity( newEntity );
820 
821  if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
822  {
823  mChunkEntities.append( chunkedNewEntity );
824  needsSceneUpdate = true;
825 
826  chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
827  connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject, this, &Qgs3DMapScene::onLayerEntityPickedObject );
828 
829  connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
830  {
831  finalizeNewEntity( entity );
832  } );
833 
834  connect( chunkedNewEntity, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
835  }
836  }
837  }
838 
839  if ( needsSceneUpdate )
840  onCameraChanged(); // needed for chunked entities
841 
842  connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
843 
844  if ( layer->type() == QgsMapLayerType::VectorLayer )
845  {
846  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
847  connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
848  connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
849  }
850 
851  if ( layer->type() == QgsMapLayerType::MeshLayer )
852  {
853  connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
854  }
855 
856  if ( layer->type() == QgsMapLayerType::PointCloudLayer )
857  connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
858 }
859 
860 void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
861 {
862  Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
863 
864  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
865  {
866  mChunkEntities.removeOne( chunkedEntity );
867  }
868 
869  if ( entity )
870  entity->deleteLater();
871 
872  disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
873 
874  if ( layer->type() == QgsMapLayerType::VectorLayer )
875  {
876  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
877  disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
878  disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
879  mModelVectorLayers.removeAll( layer );
880  }
881 
882  if ( layer->type() == QgsMapLayerType::MeshLayer )
883  {
884  disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
885  }
886 
887  if ( layer->type() == QgsMapLayerType::PointCloudLayer )
888  disconnect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
889 }
890 
891 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
892 {
893  // this is probably not the best place for material-specific configuration,
894  // maybe this could be more generalized when other materials need some specific treatment
895  for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
896  {
897  connect( mCameraController, &QgsCameraController::viewportChanged, lm, [lm, this]
898  {
899  lm->setViewportSize( mCameraController->viewport().size() );
900  } );
901 
902  lm->setViewportSize( cameraController()->viewport().size() );
903  }
904  // configure billboard's viewport when the viewport is changed.
905  for ( QgsPoint3DBillboardMaterial *bm : newEntity->findChildren<QgsPoint3DBillboardMaterial *>() )
906  {
907  connect( mCameraController, &QgsCameraController::viewportChanged, bm, [bm, this]
908  {
909  bm->setViewportSize( mCameraController->viewport().size() );
910  } );
911 
912  bm->setViewportSize( mCameraController->viewport().size() );
913  }
914 }
915 
916 int Qgs3DMapScene::maximumTextureSize() const
917 {
918  QSurface *surface = mEngine->surface();
919  QOpenGLContext context;
920  context.create();
921  context.makeCurrent( surface );
922  QOpenGLFunctions openglFunctions( &context );
923  GLint size;
924  openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
925  return int( size );
926 }
927 
928 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
929 {
930  mEntityCameraViewCenter = new Qt3DCore::QEntity;
931 
932  Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
933  mEntityCameraViewCenter->addComponent( trCameraViewCenter );
934  connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
935  {
936  trCameraViewCenter->setTranslation( camera->viewCenter() );
937  } );
938 
939  Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
940  materialCameraViewCenter->setAmbient( Qt::red );
941  mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
942 
943  Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
944  rendererCameraViewCenter->setRadius( 10 );
945  mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
946 
947  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
948  mEntityCameraViewCenter->setParent( this );
949 
950  connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
951  {
952  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
953  } );
954 }
955 
956 void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
957 {
958  if ( mSceneState == state )
959  return;
960  mSceneState = state;
961  emit sceneStateChanged();
962 }
963 
964 void Qgs3DMapScene::updateSceneState()
965 {
966  if ( mTerrainUpdateScheduled )
967  {
968  setSceneState( Updating );
969  return;
970  }
971 
972  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
973  {
974  if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
975  {
976  setSceneState( Updating );
977  return;
978  }
979  }
980 
981  setSceneState( Ready );
982 }
983 
984 void Qgs3DMapScene::onSkyboxSettingsChanged()
985 {
986  QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
987  if ( mSkybox != nullptr )
988  {
989  mSkybox->deleteLater();
990  mSkybox = nullptr;
991  }
992 
993  mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
994 
995  if ( mMap.isSkyboxEnabled() )
996  {
997  QMap<QString, QString> faces;
998  switch ( skyboxSettings.skyboxType() )
999  {
1001  faces = skyboxSettings.cubeMapFacesPaths();
1002  mSkybox = new QgsCubeFacesSkyboxEntity(
1003  faces[QStringLiteral( "posX" )], faces[QStringLiteral( "posY" )], faces[QStringLiteral( "posZ" )],
1004  faces[QStringLiteral( "negX" )], faces[QStringLiteral( "negY" )], faces[QStringLiteral( "negZ" )],
1005  this
1006  );
1007  break;
1009  mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
1010  break;
1011  }
1012  }
1013 }
1014 
1015 void Qgs3DMapScene::onShadowSettingsChanged()
1016 {
1017  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1018 
1019  QList<QgsDirectionalLightSettings> directionalLights = mMap.directionalLights();
1020  QgsShadowSettings shadowSettings = mMap.shadowSettings();
1021  int selectedLight = shadowSettings.selectedDirectionalLight();
1022  if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLights.count() )
1023  {
1024  shadowRenderingFrameGraph->setShadowRenderingEnabled( true );
1025  shadowRenderingFrameGraph->setShadowBias( shadowSettings.shadowBias() );
1026  shadowRenderingFrameGraph->setShadowMapResolution( shadowSettings.shadowMapResolution() );
1027  QgsDirectionalLightSettings light = directionalLights[selectedLight];
1028  shadowRenderingFrameGraph->setupDirectionalLight( light, shadowSettings.maximumShadowRenderingDistance() );
1029  }
1030  else
1031  shadowRenderingFrameGraph->setShadowRenderingEnabled( false );
1032 }
1033 
1034 void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
1035 {
1036  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1037  shadowRenderingFrameGraph->setupShadowMapDebugging( mMap.debugShadowMapEnabled(), mMap.debugShadowMapCorner(), mMap.debugShadowMapSize() );
1038 }
1039 
1040 void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1041 {
1042  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1043  shadowRenderingFrameGraph->setupDepthMapDebugging( mMap.debugDepthMapEnabled(), mMap.debugDepthMapCorner(), mMap.debugDepthMapSize() );
1044 }
1045 
1046 void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1047 {
1048  QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1049 
1050  bool edlEnabled = mMap.eyeDomeLightingEnabled();
1051  double edlStrength = mMap.eyeDomeLightingStrength();
1052  double edlDistance = mMap.eyeDomeLightingDistance();
1053  shadowRenderingFrameGraph->setupEyeDomeLighting( edlEnabled, edlStrength, edlDistance );
1054 }
1055 
1056 void Qgs3DMapScene::onCameraMovementSpeedChanged()
1057 {
1058  mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1059 }
1060 
1062 {
1063  QVector<QString> notParsedLayers;
1064  Qgs3DSceneExporter exporter;
1065 
1066  exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1067  exporter.setSmoothEdges( exportSettings.smoothEdges() );
1068  exporter.setExportNormals( exportSettings.exportNormals() );
1069  exporter.setExportTextures( exportSettings.exportTextures() );
1070  exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1071  exporter.setScale( exportSettings.scale() );
1072 
1073  for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1074  {
1075  QgsMapLayer *layer = it.key();
1076  Qt3DCore::QEntity *rootEntity = it.value();
1077  QgsMapLayerType layerType = layer->type();
1078  switch ( layerType )
1079  {
1081  if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1082  notParsedLayers.push_back( layer->name() );
1083  break;
1090  notParsedLayers.push_back( layer->name() );
1091  break;
1092  }
1093  }
1094 
1095  if ( mTerrain )
1096  exporter.parseTerrain( mTerrain, "Terrain" );
1097 
1098  exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1099 
1100  if ( !notParsedLayers.empty() )
1101  {
1102  QString message = tr( "The following layers were not exported:" ) + "\n";
1103  for ( const QString &layerName : notParsedLayers )
1104  message += layerName + "\n";
1105  QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText );
1106  }
1107 }
1108 
1109 QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1110 {
1111  QVector<const QgsChunkNode *> chunks;
1112  if ( !mLayerEntities.contains( layer ) ) return chunks;
1113  if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[ layer ] ) )
1114  {
1115  for ( QgsChunkNode *n : c->activeNodes() )
1116  chunks.push_back( n );
1117  }
1118  return chunks;
1119 }
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:167
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:85
QString name
Definition: qgsmaplayer.h:88
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:92
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:1231
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:209
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
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: qgsmaplayer.h:69
@ 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:331
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