QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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/QRenderSettings>
26 #include <Qt3DRender/QSceneLoader>
27 #include <Qt3DExtras/QForwardRenderer>
28 #include <Qt3DExtras/QPhongMaterial>
29 #include <Qt3DExtras/QSkyboxEntity>
30 #include <Qt3DExtras/QSphereMesh>
31 #include <Qt3DLogic/QFrameAction>
32 
33 #include <QOpenGLContext>
34 #include <QOpenGLFunctions>
35 #include <QTimer>
36 
37 #include "qgsaabb.h"
38 #include "qgsabstract3dengine.h"
40 #include "qgs3dmapsettings.h"
41 #include "qgs3dutils.h"
42 #include "qgsabstract3drenderer.h"
43 #include "qgscameracontroller.h"
44 #include "qgschunkedentity_p.h"
45 #include "qgschunknode_p.h"
46 #include "qgseventtracing.h"
47 #include "qgsmeshlayer.h"
48 #include "qgsmeshlayer3drenderer.h"
49 #include "qgsrulebased3drenderer.h"
50 #include "qgsterrainentity_p.h"
51 #include "qgsterraingenerator.h"
53 #include "qgsvectorlayer.h"
57 
58 #include "qgslinematerial_p.h"
59 
61  : mMap( map )
62  , mEngine( engine )
63 {
64 
65  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
66  onBackgroundColorChanged();
67 
68  // TODO: strange - setting OnDemand render policy still keeps QGIS busy (Qt 5.9.0)
69  // actually it is more busy than with the default "Always" policy although there are no changes in the scene.
70  //mRenderer->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
71 
72 #if QT_VERSION >= 0x050900
73  // we want precise picking of terrain (also bounding volume picking does not seem to work - not sure why)
74  mEngine->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
75 #endif
76 
77  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
78 
79  // Camera
80  float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
81  mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
82 
83  mFrameAction = new Qt3DLogic::QFrameAction();
84  connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
85  this, &Qgs3DMapScene::onFrameTriggered );
86  addComponent( mFrameAction ); // takes ownership
87 
88  // Camera controlling
89  mCameraController = new QgsCameraController( this ); // attaches to the scene
90  mCameraController->setViewport( viewportRect );
91  mCameraController->setCamera( mEngine->camera() );
92  mCameraController->resetView( 1000 );
93 
94  addCameraViewCenterEntity( mEngine->camera() );
95 
96  // create terrain entity
97 
98  createTerrainDeferred();
99  connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
100  connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
101  connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
102  connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
103  connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
104  connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
105  connect( &map, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapScene::updateLights );
106  connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
107  connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged );
108 
109  // create entities of renderers
110 
111  onRenderersChanged();
112 
113  // listen to changes of layers in order to add/remove 3D renderer entities
114  connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
115 
116  updateLights();
117 
118 #if 0
119  ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
120  testChunkEntity->setEnabled( false );
121  testChunkEntity->setParent( this );
122  chunkEntities << testChunkEntity;
123 #endif
124 
125  connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
126  connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged );
127 
128 #if 0
129  // experiments with loading of existing 3D models.
130 
131  // scene loader only gets loaded only when added to a scene...
132  // it loads everything: geometries, materials, transforms, lights, cameras (if any)
133  Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity;
134  Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader;
135  loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
136  loaderEntity->addComponent( loader );
137  loaderEntity->setParent( this );
138 
139  // mesh loads just geometry as one geometry...
140  // so if there are different materials (e.g. colors) used in the model, this information is lost
141  Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity;
142  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
143  mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
144  meshEntity->addComponent( mesh );
145  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
146  material->setAmbient( Qt::red );
147  meshEntity->addComponent( material );
148  Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform;
149  meshTransform->setScale( 1 );
150  meshEntity->addComponent( meshTransform );
151  meshEntity->setParent( this );
152 #endif
153 
154  if ( map.hasSkyboxEnabled() )
155  {
156  Qt3DExtras::QSkyboxEntity *skybox = new Qt3DExtras::QSkyboxEntity;
157  skybox->setBaseName( map.skyboxFileBase() );
158  skybox->setExtension( map.skyboxFileExtension() );
159  skybox->setParent( this );
160 
161  // docs say frustum culling must be disabled for skybox.
162  // it _somehow_ works even when frustum culling is enabled with some camera positions,
163  // but then when zoomed in more it would disappear - so let's keep frustum culling disabled
164  mEngine->setFrustumCullingEnabled( false );
165 
166  // cppcheck wrongly believes skyBox will leak
167  // cppcheck-suppress memleak
168  }
169 
170  // force initial update of chunked entities
171  onCameraChanged();
172 }
173 
175 {
176  QgsRectangle extent = mMap.terrainGenerator()->extent();
177  float side = std::max( extent.width(), extent.height() );
178  mCameraController->resetView( side ); // assuming FOV being 45 degrees
179 }
180 
182 {
183  return mTerrain ? mTerrain->pendingJobsCount() : 0;
184 }
185 
187 {
188  int count = 0;
189  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
190  count += entity->pendingJobsCount();
191  return count;
192 }
193 
195 {
196  if ( mPickHandlers.isEmpty() )
197  {
198  // we need to add object pickers
199  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
200  {
201  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
202  chunkedEntity->setPickingEnabled( true );
203  }
204  }
205 
206  mPickHandlers.append( pickHandler );
207 }
208 
210 {
211  mPickHandlers.removeOne( pickHandler );
212 
213  if ( mPickHandlers.isEmpty() )
214  {
215  // we need to remove pickers
216  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
217  {
218  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
219  chunkedEntity->setPickingEnabled( false );
220  }
221  }
222 }
223 
224 void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid )
225 {
226  QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
227  if ( !layer )
228  return;
229 
230  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
231  if ( !vlayer )
232  return;
233 
234  for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
235  {
236  pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
237  }
238 }
239 
240 
241 float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
242 {
243  Qt3DRender::QCamera *camera = mCameraController->camera();
244  float fov = camera->fieldOfView();
245  QRect rect = mCameraController->viewport();
246  float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
247 
248  // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
249  // with explanation of the math.
250  float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
251  float err = frustumWidthAtDistance * epsilon / screenSizePx;
252  return err;
253 }
254 
255 QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
256 {
257  Qt3DRender::QCamera *camera = cameraController->camera();
258  QgsChunkedEntity::SceneState state;
259  state.cameraFov = camera->fieldOfView();
260  state.cameraPos = camera->position();
261  QRect rect = cameraController->viewport();
262  state.screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
263  state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
264  return state;
265 }
266 
267 void Qgs3DMapScene::onCameraChanged()
268 {
269  updateScene();
270  bool changedCameraPlanes = updateCameraNearFarPlanes();
271 
272  if ( changedCameraPlanes )
273  {
274  // repeat update of entities - because we have updated camera's near/far planes,
275  // the active nodes may have changed as well
276  updateScene();
277  updateCameraNearFarPlanes();
278  }
279 }
280 
281 void Qgs3DMapScene::updateScene()
282 {
283  QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );
284 
285  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
286  {
287  if ( entity->isEnabled() )
288  entity->update( _sceneState( mCameraController ) );
289  }
290 
291  updateSceneState();
292 }
293 
294 bool Qgs3DMapScene::updateCameraNearFarPlanes()
295 {
296  // Update near and far plane from the terrain.
297  // this needs to be done with great care as we have kind of circular dependency here:
298  // active nodes are culled based on the current frustum (which involves near + far plane)
299  // and then based on active nodes we set near and far plane.
300  //
301  // All of this is just heuristics assuming that all other stuff is being rendered somewhere
302  // around the area where the terrain is.
303  //
304  // Near/far plane is setup in order to make best use of the depth buffer to avoid:
305  // 1. precision errors - if the range is too great
306  // 2. unwanted clipping of scene - if the range is too small
307 
308  if ( mTerrain )
309  {
310  Qt3DRender::QCamera *camera = cameraController()->camera();
311  QMatrix4x4 viewMatrix = camera->viewMatrix();
312  float fnear = 1e9;
313  float ffar = 0;
314 
315  QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
316 
317  // it could be that there are no active nodes - they could be all culled or because root node
318  // is not yet loaded - we still need at least something to understand bounds of our scene
319  // so lets use the root node
320  if ( activeNodes.isEmpty() )
321  activeNodes << mTerrain->rootNode();
322 
323  Q_FOREACH ( QgsChunkNode *node, activeNodes )
324  {
325  // project each corner of bbox to camera coordinates
326  // and determine closest and farthest point.
327  QgsAABB bbox = node->bbox();
328  for ( int i = 0; i < 8; ++i )
329  {
330  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
331  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
332  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
333  QVector4D pc = viewMatrix * p;
334 
335  float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
336  if ( dst < fnear )
337  fnear = dst;
338  if ( dst > ffar )
339  ffar = dst;
340  }
341  }
342  if ( fnear < 1 )
343  fnear = 1; // does not really make sense to use negative far plane (behind camera)
344 
345  if ( fnear == 1e9 && ffar == 0 )
346  {
347  // the update didn't work out... this should not happen
348  // well at least temporarily use some conservative starting values
349  qDebug() << "oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
350  fnear = 1;
351  ffar = 1e9;
352  }
353 
354  // set near/far plane - with some tolerance in front/behind expected near/far planes
355  float newFar = ffar * 2;
356  float newNear = fnear / 2;
357  if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
358  {
359  camera->setFarPlane( newFar );
360  camera->setNearPlane( newNear );
361  return true;
362  }
363  }
364  else
365  qDebug() << "no terrain - not setting near/far plane";
366 
367  return false;
368 }
369 
370 void Qgs3DMapScene::onFrameTriggered( float dt )
371 {
372  mCameraController->frameTriggered( dt );
373 
374  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
375  {
376  if ( entity->isEnabled() && entity->needsUpdate() )
377  {
378  qDebug() << "need for update";
379  entity->update( _sceneState( mCameraController ) );
380  }
381  }
382 
383  updateSceneState();
384 }
385 
386 void Qgs3DMapScene::createTerrain()
387 {
388  if ( mTerrain )
389  {
390  mChunkEntities.removeOne( mTerrain );
391 
392  mTerrain->deleteLater();
393  mTerrain = nullptr;
394 
395  emit terrainEntityChanged();
396  }
397 
398  if ( !mTerrainUpdateScheduled )
399  {
400  // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
401  QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
402  mTerrainUpdateScheduled = true;
403  setSceneState( Updating );
404  }
405 }
406 
407 void Qgs3DMapScene::createTerrainDeferred()
408 {
409  double tile0width = mMap.terrainGenerator()->extent().width();
410  int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
411 
412  mTerrain = new QgsTerrainEntity( maxZoomLevel, mMap );
413  //mTerrain->setEnabled(false);
414  mTerrain->setParent( this );
415 
416  if ( mMap.showTerrainBoundingBoxes() )
417  mTerrain->setShowBoundingBoxes( true );
418 
419  mCameraController->setTerrainEntity( mTerrain );
420 
421  mChunkEntities << mTerrain;
422 
423  onCameraChanged(); // force update of the new terrain
424 
425  // make sure that renderers for layers are re-created as well
426  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
427  {
428  // remove old entity - if any
429  removeLayerEntity( layer );
430 
431  // add new entity - if any 3D renderer
432  addLayerEntity( layer );
433  }
434 
435  mTerrainUpdateScheduled = false;
436 
437  connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
438  connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
439 
440  emit terrainEntityChanged();
441 }
442 
443 void Qgs3DMapScene::onBackgroundColorChanged()
444 {
445  mEngine->setClearColor( mMap.backgroundColor() );
446 }
447 
448 
449 void Qgs3DMapScene::updateLights()
450 {
451  for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
452  entity->deleteLater();
453  mLightEntities.clear();
454 
455  const auto newPointLights = mMap.pointLights();
456  for ( const QgsPointLightSettings &pointLightSettings : newPointLights )
457  {
458  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
459  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
460  lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
461  pointLightSettings.position().y(),
462  pointLightSettings.position().z() ) );
463 
464  Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
465  light->setColor( pointLightSettings.color() );
466  light->setIntensity( pointLightSettings.intensity() );
467 
468  light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
469  light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
470  light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
471 
472  lightEntity->addComponent( light );
473  lightEntity->addComponent( lightTransform );
474  lightEntity->setParent( this );
475  mLightEntities << lightEntity;
476  }
477 }
478 
479 void Qgs3DMapScene::updateCameraLens()
480 {
481  mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
482  onCameraChanged();
483 }
484 
485 void Qgs3DMapScene::onRenderersChanged()
486 {
487  // remove entities (if any)
488  qDeleteAll( mRenderersEntities );
489  mRenderersEntities.clear();
490 
491  // re-add entities from new set of renderers
492  const QList<QgsAbstract3DRenderer *> renderers = mMap.renderers();
493  for ( const QgsAbstract3DRenderer *renderer : renderers )
494  {
495  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
496  if ( newEntity )
497  {
498  newEntity->setParent( this );
499  finalizeNewEntity( newEntity );
500  mRenderersEntities[renderer] = newEntity;
501  }
502  }
503 }
504 
505 void Qgs3DMapScene::onLayerRenderer3DChanged()
506 {
507  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
508  Q_ASSERT( layer );
509 
510  // remove old entity - if any
511  removeLayerEntity( layer );
512 
513  // add new entity - if any 3D renderer
514  addLayerEntity( layer );
515 }
516 
517 void Qgs3DMapScene::onLayersChanged()
518 {
519  QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
520  QList<QgsMapLayer *> layersAdded;
521  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
522  {
523  if ( !layersBefore.contains( layer ) )
524  {
525  layersAdded << layer;
526  }
527  else
528  {
529  layersBefore.remove( layer );
530  }
531  }
532 
533  // what is left in layersBefore are layers that have been removed
534  Q_FOREACH ( QgsMapLayer *layer, layersBefore )
535  {
536  removeLayerEntity( layer );
537  }
538 
539  Q_FOREACH ( QgsMapLayer *layer, layersAdded )
540  {
541  addLayerEntity( layer );
542  }
543 }
544 
546 {
547  for ( auto layer : mLayerEntities.keys() )
548  {
549  if ( layer->temporalProperties()->isActive() )
550  {
551  removeLayerEntity( layer );
552  addLayerEntity( layer );
553  }
554  }
555 }
556 
557 void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
558 {
559  bool needsSceneUpdate = false;
560  QgsAbstract3DRenderer *renderer = layer->renderer3D();
561  if ( renderer )
562  {
563  // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
564  // It has happened before that renderer pointed to a different layer (probably after copying a style).
565  // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
566  // the vector layer 3D renderer classes are not available.
567  if ( layer->type() == QgsMapLayerType::VectorLayer &&
568  ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
569  {
570  static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
571  }
572  else if ( layer->type() == QgsMapLayerType::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
573  {
574  QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
575  meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
576 
577  // Before entity creation, set the maximum texture size
578  // Not very clean, but for now, only place found in the workflow to do that simple
579  QgsMesh3DSymbol *sym = new QgsMesh3DSymbol( *meshRenderer->symbol() );
580  sym->setMaximumTextureSize( maximumTextureSize() );
581  meshRenderer->setSymbol( sym );
582  }
583 
584  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
585  if ( newEntity )
586  {
587  newEntity->setParent( this );
588  mLayerEntities.insert( layer, newEntity );
589 
590  finalizeNewEntity( newEntity );
591 
592  if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
593  {
594  mChunkEntities.append( chunkedNewEntity );
595  needsSceneUpdate = true;
596 
597  chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
598  connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject, this, &Qgs3DMapScene::onLayerEntityPickedObject );
599 
600  connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
601  {
602  finalizeNewEntity( entity );
603  } );
604 
605  connect( chunkedNewEntity, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
606  }
607  }
608  }
609 
610  if ( needsSceneUpdate )
611  onCameraChanged(); // needed for chunked entities
612 
613  connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
614 
615  if ( layer->type() == QgsMapLayerType::VectorLayer )
616  {
617  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
618  connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
619  }
620 
621  if ( layer->type() == QgsMapLayerType::MeshLayer )
622  {
623  connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
624  }
625 
626 }
627 
628 void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
629 {
630  Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
631 
632  if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
633  {
634  mChunkEntities.removeOne( chunkedEntity );
635  }
636 
637  if ( entity )
638  entity->deleteLater();
639 
640  disconnect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
641 
642  if ( layer->type() == QgsMapLayerType::VectorLayer )
643  {
644  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
645  disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
646  }
647 
648  if ( layer->type() == QgsMapLayerType::MeshLayer )
649  {
650  disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
651  }
652 }
653 
654 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
655 {
656  // this is probably not the best place for material-specific configuration,
657  // maybe this could be more generalized when other materials need some specific treatment
658  for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
659  {
660  connect( mCameraController, &QgsCameraController::viewportChanged, lm, [lm, this]
661  {
662  lm->setViewportSize( mCameraController->viewport().size() );
663  } );
664 
665  lm->setViewportSize( cameraController()->viewport().size() );
666  }
667  // configure billboard's viewport when the viewport is changed.
668  for ( QgsPoint3DBillboardMaterial *bm : newEntity->findChildren<QgsPoint3DBillboardMaterial *>() )
669  {
670  connect( mCameraController, &QgsCameraController::viewportChanged, bm, [bm, this]
671  {
672  bm->setViewportSize( mCameraController->viewport().size() );
673  } );
674 
675  bm->setViewportSize( mCameraController->viewport().size() );
676  }
677 }
678 
679 int Qgs3DMapScene::maximumTextureSize() const
680 {
681  QSurface *surface = mEngine->surface();
682  QOpenGLContext context;
683  context.create();
684  context.makeCurrent( surface );
685  QOpenGLFunctions openglFunctions( &context );
686  GLint size;
687  openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
688  return int( size );
689 }
690 
691 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
692 {
693  mEntityCameraViewCenter = new Qt3DCore::QEntity;
694 
695  Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
696  mEntityCameraViewCenter->addComponent( trCameraViewCenter );
697  connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
698  {
699  trCameraViewCenter->setTranslation( camera->viewCenter() );
700  } );
701 
702  Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
703  materialCameraViewCenter->setAmbient( Qt::red );
704  mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
705 
706  Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
707  rendererCameraViewCenter->setRadius( 10 );
708  mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
709 
710  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
711  mEntityCameraViewCenter->setParent( this );
712 
713  connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
714  {
715  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
716  } );
717 }
718 
719 void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
720 {
721  if ( mSceneState == state )
722  return;
723  mSceneState = state;
724  emit sceneStateChanged();
725 }
726 
727 void Qgs3DMapScene::updateSceneState()
728 {
729  if ( mTerrainUpdateScheduled )
730  {
731  setSceneState( Updating );
732  return;
733  }
734 
735  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
736  {
737  if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
738  {
739  setSceneState( Updating );
740  return;
741  }
742  }
743 
744  setSceneState( Ready );
745 }
Qgs3DMapSettings::backgroundColorChanged
void backgroundColorChanged()
Emitted when the background color has changed.
Qgs3DUtils::maxZoomLevel
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
QgsPoint3DBillboardMaterial
Definition: qgspoint3dbillboardmaterial.h:36
qgstessellatedpolygongeometry.h
QgsCameraController::resetView
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates)
Definition: qgscameracontroller.cpp:176
Qgs3DMapScene::registerPickHandler
void registerPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Registers an object that will get results of pick events on 3D entities. Does not take ownership of t...
Definition: qgs3dmapscene.cpp:194
QgsMapLayerType::MeshLayer
@ MeshLayer
Added in 3.2.
QgsMapLayerType::VectorLayer
@ VectorLayer
Qgs3DMapSettings::maxTerrainGroundError
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
Definition: qgs3dmapsettings.cpp:446
QgsAbstract3DRenderer::type
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
qgsaabb.h
Qgs3DMapScene::terrainPendingJobsCountChanged
void terrainPendingJobsCountChanged()
Emitted when the number of terrain's pending jobs changes.
QgsAbstract3DRenderer
Definition: qgsabstract3drenderer.h:48
Qgs3DMapSettings::mapTileResolution
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
Definition: qgs3dmapsettings.cpp:418
Qgs3DMapSettings::showCameraViewCenterChanged
void showCameraViewCenterChanged()
Emitted when the flag whether camera's view center is shown has changed.
QgsCameraController::frameTriggered
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
Definition: qgscameracontroller.cpp:171
QgsMeshLayer3DRenderer::symbol
const QgsMesh3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Definition: qgsmeshlayer3drenderer.cpp:68
qgschunknode_p.h
Qgs3DMapSettings::mapTileResolutionChanged
void mapTileResolutionChanged()
Emitted when the map tile resoulution has changed.
QgsMesh3DSymbol
Definition: qgsmesh3dsymbol.h:42
qgsabstract3drenderer.h
QgsAbstract3DEngine::setClearColor
virtual void setClearColor(const QColor &color)=0
Sets background color of the scene.
QgsAbstract3DEngine::size
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
Qgs3DMapSettings::terrainGeneratorChanged
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
QgsAbstractVectorLayer3DRenderer
Definition: qgsabstractvectorlayer3drenderer.h:77
QgsAABB::zMax
float zMax
Definition: qgsaabb.h:83
QgsAABB::yMin
float yMin
Definition: qgsaabb.h:79
qgspoint3dbillboardmaterial.h
Qgs3DMapScene::Updating
@ Updating
The scene is still being loaded/updated.
Definition: qgs3dmapscene.h:88
Qgs3DMapSettings::renderersChanged
void renderersChanged()
Emitted when the list of map's extra renderers have been modified.
QgsRectangle
Definition: qgsrectangle.h:41
Qgs3DMapScene::sceneStateChanged
void sceneStateChanged()
Emitted when the scene's state has changed.
qgslinematerial_p.h
Qgs3DMapSettings::maxTerrainScreenErrorChanged
void maxTerrainScreenErrorChanged()
Emitted when the maximum terrain screen error has changed.
qgseventtracing.h
qgsmaplayertemporalproperties.h
Qgs3DMapSettings::hasSkyboxEnabled
bool hasSkyboxEnabled() const
Returns whether skybox is enabled.
Definition: qgs3dmapsettings.h:291
qgsmeshlayer3drenderer.h
qgsFloatNear
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition: qgis.h:330
QgsMapLayer::renderer3D
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
Definition: qgsmaplayer.cpp:1812
QgsTemporalProperty::isActive
bool isActive() const
Returns true if the temporal property is active.
Definition: qgstemporalproperty.cpp:36
QgsCameraController::camera
Qt3DRender::QCamera camera
Definition: qgscameracontroller.h:62
qgs3dutils.h
QgsVectorLayer::selectionChanged
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
Qgs3DMapScene::cameraController
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:68
qgschunkedentity_p.h
QgsMeshLayer
Definition: qgsmeshlayer.h:94
qgsterraingenerator.h
Qgs3DMapScene::updateTemporal
void updateTemporal()
Updates the temporale entities.
Definition: qgs3dmapscene.cpp:545
QgsPointLightSettings
Definition: qgspointlightsettings.h:38
Qgs3DMapScene::worldSpaceError
float worldSpaceError(float epsilon, float distance)
Given screen error (in pixels) and distance from camera (in 3D world coordinates),...
Definition: qgs3dmapscene.cpp:241
Qgs3DMapSettings::backgroundColor
QColor backgroundColor() const
Returns background color of the 3D map view.
Definition: qgs3dmapsettings.cpp:348
_sceneState
QgsChunkedEntity::SceneState _sceneState(QgsCameraController *cameraController)
Definition: qgs3dmapscene.cpp:255
Qgs3DMapSettings::terrainVerticalScaleChanged
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
QgsMapLayer::temporalProperties
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1194
QgsAABB::xMin
float xMin
Definition: qgsaabb.h:78
QgsCameraController::viewportChanged
void viewportChanged()
Emitted when viewport rectangle has been updated.
QgsAABB
Definition: qgsaabb.h:33
Qgs3DMapScene::unregisterPickHandler
void unregisterPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object pick...
Definition: qgs3dmapscene.cpp:209
qgsabstract3dengine.h
QgsMeshLayer3DRenderer
Definition: qgsmeshlayer3drenderer.h:59
QgsCameraController::viewport
QRect viewport
Definition: qgscameracontroller.h:63
Qgs3DMapSettings
Definition: qgs3dmapsettings.h:51
QgsCameraController::setCamera
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene.
Definition: qgscameracontroller.cpp:71
Qgs3DMapSettings::skyboxFileExtension
QString skyboxFileExtension() const
Returns extension part of filenames of skybox (see setSkybox())
Definition: qgs3dmapsettings.h:295
Qgs3DMapScene::terrainEntityChanged
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
Qgs3DMapSettings::fieldOfView
float fieldOfView() const
Returns the camera lens' field of view.
Definition: qgs3dmapsettings.h:338
Qgs3DMapSettings::fieldOfViewChanged
void fieldOfViewChanged()
Emitted when the camera lens field of view changes.
qgscameracontroller.h
QgsCameraController::setTerrainEntity
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
Definition: qgscameracontroller.cpp:63
QgsAbstract3DEngine
Definition: qgsabstract3dengine.h:56
QgsMapLayer::rendererChanged
void rendererChanged()
Signal emitted when renderer is changed.
QgsAbstract3DRenderer::createEntity
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.
Qgs3DMapSettings::pointLightsChanged
void pointLightsChanged()
Emitted when the list of point lights changes.
QgsAbstract3DEngine::camera
virtual Qt3DRender::QCamera * camera()=0
Returns pointer to the engine's camera entity.
Qgs3DMapSettings::terrainGenerator
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data.
Definition: qgs3dmapsettings.h:228
qgsmeshlayer.h
qgsvectorlayer.h
QgsAbstract3DEngine::setFrustumCullingEnabled
virtual void setFrustumCullingEnabled(bool enabled)=0
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
qgsterrainentity_p.h
Qgs3DMapSettings::terrainShadingChanged
void terrainShadingChanged()
Emitted when terrain shading enabled flag or terrain shading material has changed.
Qgs3DMapScene::terrainPendingJobsCount
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
Definition: qgs3dmapscene.cpp:181
QgsCameraController::setViewport
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates.
Definition: qgscameracontroller.cpp:84
qgs3dmapsettings.h
Qgs3DMapScene::SceneState
SceneState
Enumeration of possible states of the 3D scene.
Definition: qgs3dmapscene.h:85
Qgs3DMapScene::Ready
@ Ready
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:87
qgsrulebased3drenderer.h
Qgs3DMapScene::totalPendingJobsCount
int totalPendingJobsCount() const
Returns number of pending jobs for all chunked entities.
Definition: qgs3dmapscene.cpp:186
Qgs3DMapScene::viewZoomFull
void viewZoomFull()
Resets camera view to show the whole scene (top view)
Definition: qgs3dmapscene.cpp:174
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsMapLayer
Definition: qgsmaplayer.h:81
Qgs3DMapSettings::layers
QList< QgsMapLayer * > layers() const
Returns the list of map layers to be rendered as a texture of the terrain.
Definition: qgs3dmapsettings.cpp:397
Qgs3DMapSettings::renderers
QList< QgsAbstract3DRenderer * > renderers() const
Returns list of extra 3D renderers.
Definition: qgs3dmapsettings.h:281
Qgs3DMapScene::Qgs3DMapScene
Qgs3DMapScene(const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine)
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
Definition: qgs3dmapscene.cpp:60
QgsAABB::yMax
float yMax
Definition: qgsaabb.h:82
QgsRectangle::height
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
qgs3dmapscenepickhandler.h
QgsMeshLayer3DRenderer::setLayer
void setLayer(QgsMeshLayer *layer)
Sets vector layer associated with the renderer.
Definition: qgsmeshlayer3drenderer.cpp:53
qgsvectorlayer3drenderer.h
Qgs3DMapSettings::showTerrainBoundingBoxes
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
Definition: qgs3dmapsettings.h:300
Qgs3DMapScene::totalPendingJobsCountChanged
void totalPendingJobsCountChanged()
Emitted when the total number of pending jobs changes.
QgsMesh3DSymbol::setMaximumTextureSize
void setMaximumTextureSize(int maximumTextureSize)
Sets the maximum texture size supported by the hardware Used to store the GL_MAX_TEXTURE_SIZE value t...
Definition: qgsmesh3dsymbol.cpp:229
QgsMeshLayer3DRenderer::setSymbol
void setSymbol(QgsMesh3DSymbol *symbol)
Sets 3D symbol associated with the renderer.
Definition: qgsmeshlayer3drenderer.cpp:63
QgsTerrainGenerator::extent
virtual QgsRectangle extent() const =0
extent of the terrain in terrain's CRS
Qgs3DMapSettings::showCameraViewCenter
bool showCameraViewCenter() const
Returns whether to show camera's view center as a sphere (for debugging)
Definition: qgs3dmapsettings.h:316
QgsCameraController
Definition: qgscameracontroller.h:59
QgsAbstract3DEngine::renderSettings
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
QgsAABB::zMin
float zMin
Definition: qgsaabb.h:80
QgsAABB::xMax
float xMax
Definition: qgsaabb.h:81
QgsCameraController::cameraChanged
void cameraChanged()
Emitted when camera has been updated.
Qgs3DMapSettings::skyboxFileBase
QString skyboxFileBase() const
Returns base part of filenames of skybox (see setSkybox())
Definition: qgs3dmapsettings.h:293
QgsMapLayer::renderer3DChanged
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
QgsMapLayer::type
QgsMapLayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:129
Qgs3DMapSettings::maxTerrainGroundErrorChanged
void maxTerrainGroundErrorChanged()
Emitted when the maximum terrain ground error has changed.
QgsRectangle::width
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Qgs3DMapScenePickHandler
Definition: qgs3dmapscenepickhandler.h:35
Qgs3DMapSettings::pointLights
QList< QgsPointLightSettings > pointLights() const
Returns list of point lights defined in the scene.
Definition: qgs3dmapsettings.h:326
qgs3dmapscene.h
QgsFeatureId
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsAbstract3DEngine::surface
virtual QSurface * surface() const =0
Returns the surface of the engine.
Qgs3DMapSettings::layersChanged
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.