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