QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 <QTimer>
34 
35 #include "qgsaabb.h"
36 #include "qgsabstract3dengine.h"
38 #include "qgs3dmapsettings.h"
39 #include "qgs3dutils.h"
40 #include "qgsabstract3drenderer.h"
41 #include "qgscameracontroller.h"
42 #include "qgschunkedentity_p.h"
43 #include "qgschunknode_p.h"
44 #include "qgsterrainentity_p.h"
45 #include "qgsterraingenerator.h"
47 #include "qgsvectorlayer.h"
49 
50 
52  : mMap( map )
53  , mEngine( engine )
54 {
55 
56  connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
57  onBackgroundColorChanged();
58 
59  // TODO: strange - setting OnDemand render policy still keeps QGIS busy (Qt 5.9.0)
60  // actually it is more busy than with the default "Always" policy although there are no changes in the scene.
61  //mRenderer->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
62 
63 #if QT_VERSION >= 0x050900
64  // we want precise picking of terrain (also bounding volume picking does not seem to work - not sure why)
65  mEngine->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
66 #endif
67 
68  QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
69 
70  // Camera
71  float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
72  mEngine->camera()->lens()->setPerspectiveProjection( 45.0f, aspectRatio, 10.f, 10000.0f );
73 
74  mFrameAction = new Qt3DLogic::QFrameAction();
75  connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
76  this, &Qgs3DMapScene::onFrameTriggered );
77  addComponent( mFrameAction ); // takes ownership
78 
79  // Camera controlling
80  mCameraController = new QgsCameraController( this ); // attaches to the scene
81  mCameraController->setViewport( viewportRect );
82  mCameraController->setCamera( mEngine->camera() );
83  mCameraController->resetView( 1000 );
84 
85  addCameraViewCenterEntity( mEngine->camera() );
86 
87  // create terrain entity
88 
89  createTerrain();
90  connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
91  connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
92  connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
93  connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
94  connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
95 
96  // create entities of renderers
97 
98  Q_FOREACH ( const QgsAbstract3DRenderer *renderer, map.renderers() )
99  {
100  Qt3DCore::QEntity *newEntity = renderer->createEntity( map );
101  newEntity->setParent( this );
102  }
103 
104  // listen to changes of layers in order to add/remove 3D renderer entities
105  connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
106 
107  Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity;
108  Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform;
109  lightTransform->setTranslation( QVector3D( 0, 1000, 0 ) );
110  // defaults: white, intensity 0.5
111  // attenuation: constant 1.0, linear 0.0, quadratic 0.0
112  Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight;
113  light->setConstantAttenuation( 0 );
114  //light->setColor(Qt::white);
115  //light->setIntensity(0.5);
116  lightEntity->addComponent( light );
117  lightEntity->addComponent( lightTransform );
118  lightEntity->setParent( this );
119 
120 
121 #if 0
122  ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
123  testChunkEntity->setEnabled( false );
124  testChunkEntity->setParent( this );
125  chunkEntities << testChunkEntity;
126 #endif
127 
128  connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
129  connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged );
130 
131 #if 0
132  // experiments with loading of existing 3D models.
133 
134  // scene loader only gets loaded only when added to a scene...
135  // it loads everything: geometries, materials, transforms, lights, cameras (if any)
136  Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity;
137  Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader;
138  loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
139  loaderEntity->addComponent( loader );
140  loaderEntity->setParent( this );
141 
142  // mesh loads just geometry as one geometry...
143  // so if there are different materials (e.g. colors) used in the model, this information is lost
144  Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity;
145  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
146  mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
147  meshEntity->addComponent( mesh );
148  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
149  material->setAmbient( Qt::red );
150  meshEntity->addComponent( material );
151  Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform;
152  meshTransform->setScale( 1 );
153  meshEntity->addComponent( meshTransform );
154  meshEntity->setParent( this );
155 #endif
156 
157  if ( map.hasSkyboxEnabled() )
158  {
159  Qt3DExtras::QSkyboxEntity *skybox = new Qt3DExtras::QSkyboxEntity;
160  skybox->setBaseName( map.skyboxFileBase() );
161  skybox->setExtension( map.skyboxFileExtension() );
162  skybox->setParent( this );
163 
164  // docs say frustum culling must be disabled for skybox.
165  // it _somehow_ works even when frustum culling is enabled with some camera positions,
166  // but then when zoomed in more it would disappear - so let's keep frustum culling disabled
167  mEngine->setFrustumCullingEnabled( false );
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  if ( mPickHandlers.isEmpty() )
189  {
190  // we need to add object pickers
191  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
192  {
193  Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( entity );
194  entity->addComponent( picker );
195  connect( picker, &Qt3DRender::QObjectPicker::clicked, this, &Qgs3DMapScene::onLayerEntityPickEvent );
196  }
197  }
198 
199  mPickHandlers.append( pickHandler );
200 }
201 
203 {
204  mPickHandlers.removeOne( pickHandler );
205 
206  if ( mPickHandlers.isEmpty() )
207  {
208  // we need to remove pickers
209  for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
210  {
211  Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
212  picker->deleteLater();
213  }
214  }
215 }
216 
217 float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
218 {
219  Qt3DRender::QCamera *camera = mCameraController->camera();
220  float fov = camera->fieldOfView();
221  QRect rect = mCameraController->viewport();
222  float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
223 
224  // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
225  // with explanation of the math.
226  float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
227  float err = frustumWidthAtDistance * epsilon / screenSizePx;
228  return err;
229 }
230 
231 QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
232 {
233  Qt3DRender::QCamera *camera = cameraController->camera();
234  QgsChunkedEntity::SceneState state;
235  state.cameraFov = camera->fieldOfView();
236  state.cameraPos = camera->position();
237  QRect rect = cameraController->viewport();
238  state.screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
239  state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
240  return state;
241 }
242 
243 void Qgs3DMapScene::onCameraChanged()
244 {
245  updateScene();
246  bool changedCameraPlanes = updateCameraNearFarPlanes();
247 
248  if ( changedCameraPlanes )
249  {
250  // repeat update of entities - because we have updated camera's near/far planes,
251  // the active nodes may have changed as well
252  updateScene();
253  updateCameraNearFarPlanes();
254  }
255 }
256 
257 void Qgs3DMapScene::updateScene()
258 {
259  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
260  {
261  if ( entity->isEnabled() )
262  entity->update( _sceneState( mCameraController ) );
263  }
264 
265  updateSceneState();
266 }
267 
268 bool Qgs3DMapScene::updateCameraNearFarPlanes()
269 {
270  // Update near and far plane from the terrain.
271  // this needs to be done with great care as we have kind of circular dependency here:
272  // active nodes are culled based on the current frustum (which involves near + far plane)
273  // and then based on active nodes we set near and far plane.
274  //
275  // All of this is just heuristics assuming that all other stuff is being rendered somewhere
276  // around the area where the terrain is.
277  //
278  // Near/far plane is setup in order to make best use of the depth buffer to avoid:
279  // 1. precision errors - if the range is too great
280  // 2. unwanted clipping of scene - if the range is too small
281 
282  if ( mTerrain )
283  {
284  Qt3DRender::QCamera *camera = cameraController()->camera();
285  QMatrix4x4 viewMatrix = camera->viewMatrix();
286  float fnear = 1e9;
287  float ffar = 0;
288 
289  QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
290 
291  // it could be that there are no active nodes - they could be all culled or because root node
292  // is not yet loaded - we still need at least something to understand bounds of our scene
293  // so lets use the root node
294  if ( activeNodes.isEmpty() )
295  activeNodes << mTerrain->rootNode();
296 
297  Q_FOREACH ( QgsChunkNode *node, activeNodes )
298  {
299  // project each corner of bbox to camera coordinates
300  // and determine closest and farthest point.
301  QgsAABB bbox = node->bbox();
302  for ( int i = 0; i < 8; ++i )
303  {
304  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
305  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
306  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
307  QVector4D pc = viewMatrix * p;
308 
309  float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
310  if ( dst < fnear )
311  fnear = dst;
312  if ( dst > ffar )
313  ffar = dst;
314  }
315  }
316  if ( fnear < 1 )
317  fnear = 1; // does not really make sense to use negative far plane (behind camera)
318 
319  if ( fnear == 1e9 && ffar == 0 )
320  {
321  // the update didn't work out... this should not happen
322  // well at least temprarily use some conservative starting values
323  qDebug() << "oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
324  fnear = 1;
325  ffar = 1e9;
326  }
327 
328  // set near/far plane - with some tolerance in front/behind expected near/far planes
329  float newFar = ffar * 2;
330  float newNear = fnear / 2;
331  if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
332  {
333  camera->setFarPlane( newFar );
334  camera->setNearPlane( newNear );
335  return true;
336  }
337  }
338  else
339  qDebug() << "no terrain - not setting near/far plane";
340 
341  return false;
342 }
343 
344 void Qgs3DMapScene::onFrameTriggered( float dt )
345 {
346  mCameraController->frameTriggered( dt );
347 
348  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
349  {
350  if ( entity->isEnabled() && entity->needsUpdate() )
351  {
352  qDebug() << "need for update";
353  entity->update( _sceneState( mCameraController ) );
354  }
355  }
356 
357  updateSceneState();
358 }
359 
360 void Qgs3DMapScene::createTerrain()
361 {
362  if ( mTerrain )
363  {
364  mChunkEntities.removeOne( mTerrain );
365 
366  mTerrain->deleteLater();
367  mTerrain = nullptr;
368 
369  emit terrainEntityChanged();
370  }
371 
372  if ( !mTerrainUpdateScheduled )
373  {
374  // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
375  QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
376  mTerrainUpdateScheduled = true;
377  setSceneState( Updating );
378  }
379 }
380 
381 void Qgs3DMapScene::createTerrainDeferred()
382 {
383  double tile0width = mMap.terrainGenerator()->extent().width();
384  int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
385 
386  mTerrain = new QgsTerrainEntity( maxZoomLevel, mMap );
387  //mTerrain->setEnabled(false);
388  mTerrain->setParent( this );
389 
390  if ( mMap.showTerrainBoundingBoxes() )
391  mTerrain->setShowBoundingBoxes( true );
392 
393  mCameraController->setTerrainEntity( mTerrain );
394 
395  mChunkEntities << mTerrain;
396 
397  onCameraChanged(); // force update of the new terrain
398 
399  // make sure that renderers for layers are re-created as well
400  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
401  {
402  // remove old entity - if any
403  removeLayerEntity( layer );
404 
405  // add new entity - if any 3D renderer
406  addLayerEntity( layer );
407  }
408 
409  mTerrainUpdateScheduled = false;
410 
411  connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
412 
413  emit terrainEntityChanged();
414 }
415 
416 void Qgs3DMapScene::onBackgroundColorChanged()
417 {
418  mEngine->setClearColor( mMap.backgroundColor() );
419 }
420 
421 void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
422 {
423  if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
424  return;
425 
426  Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
427  if ( !triangleEvent )
428  return;
429 
430  Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
431  if ( !picker )
432  return;
433 
434  Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
435  if ( !entity )
436  return;
437 
438  QgsMapLayer *layer = mLayerEntities.key( entity );
439  if ( !layer )
440  return;
441 
442  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
443  if ( !vlayer )
444  return;
445 
446  for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
447  {
448  // go figure out feature ID from the triangle index
449  QgsFeatureId fid = -1;
450  for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
451  {
452  // unfortunately we can't access which sub-entity triggered the pick event
453  // so as a temporary workaround let's just ignore the entity with selection
454  // and hope the event was the main entity (QTBUG-58206)
455  if ( geomRenderer->objectName() != QLatin1String( "main" ) )
456  continue;
457 
458  if ( QgsTessellatedPolygonGeometry *g = qobject_cast<QgsTessellatedPolygonGeometry *>( geomRenderer->geometry() ) )
459  {
460  fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
461  break;
462  }
463  }
464  pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection() );
465  }
466 
467 }
468 
469 void Qgs3DMapScene::onLayerRenderer3DChanged()
470 {
471  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
472  Q_ASSERT( layer );
473 
474  // remove old entity - if any
475  removeLayerEntity( layer );
476 
477  // add new entity - if any 3D renderer
478  addLayerEntity( layer );
479 }
480 
481 void Qgs3DMapScene::onLayersChanged()
482 {
483  QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
484  QList<QgsMapLayer *> layersAdded;
485  Q_FOREACH ( QgsMapLayer *layer, mMap.layers() )
486  {
487  if ( !layersBefore.contains( layer ) )
488  {
489  layersAdded << layer;
490  }
491  else
492  {
493  layersBefore.remove( layer );
494  }
495  }
496 
497  // what is left in layersBefore are layers that have been removed
498  Q_FOREACH ( QgsMapLayer *layer, layersBefore )
499  {
500  removeLayerEntity( layer );
501  }
502 
503  Q_FOREACH ( QgsMapLayer *layer, layersAdded )
504  {
505  addLayerEntity( layer );
506  }
507 }
508 
509 void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
510 {
511  QgsAbstract3DRenderer *renderer = layer->renderer3D();
512  if ( renderer )
513  {
514  // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
515  // It has happened before that renderer pointed to a different layer (probably after copying a style).
516  // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
517  // the vector layer 3D renderer class is not available. Maybe we need an intermediate map layer 3D renderer
518  // class in qgis_core that can be used to handle this case nicely.
519  if ( layer->type() == QgsMapLayer::VectorLayer && renderer->type() == QLatin1String( "vector" ) )
520  {
521  static_cast<QgsVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
522  }
523 
524  Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
525  if ( newEntity )
526  {
527  newEntity->setParent( this );
528  mLayerEntities.insert( layer, newEntity );
529 
530  if ( !mPickHandlers.isEmpty() )
531  {
532  Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( newEntity );
533  newEntity->addComponent( picker );
534  connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapScene::onLayerEntityPickEvent );
535  }
536  }
537  }
538 
539  connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
540 
541  if ( layer->type() == QgsMapLayer::VectorLayer )
542  {
543  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
544  connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
545  }
546 }
547 
548 void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
549 {
550  Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
551  if ( entity )
552  entity->deleteLater();
553 
554  disconnect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
555 
556  if ( layer->type() == QgsMapLayer::VectorLayer )
557  {
558  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
559  disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
560  }
561 }
562 
563 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
564 {
565  mEntityCameraViewCenter = new Qt3DCore::QEntity;
566 
567  Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
568  mEntityCameraViewCenter->addComponent( trCameraViewCenter );
569  connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
570  {
571  trCameraViewCenter->setTranslation( camera->viewCenter() );
572  } );
573 
574  Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
575  materialCameraViewCenter->setAmbient( Qt::red );
576  mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
577 
578  Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
579  rendererCameraViewCenter->setRadius( 10 );
580  mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
581 
582  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
583  mEntityCameraViewCenter->setParent( this );
584 
585  connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
586  {
587  mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
588  } );
589 }
590 
591 void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
592 {
593  if ( mSceneState == state )
594  return;
595  mSceneState = state;
596  emit sceneStateChanged();
597 }
598 
599 void Qgs3DMapScene::updateSceneState()
600 {
601  if ( mTerrainUpdateScheduled )
602  {
603  setSceneState( Updating );
604  return;
605  }
606 
607  for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
608  {
609  if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
610  {
611  setSceneState( Updating );
612  return;
613  }
614  }
615 
616  setSceneState( Ready );
617 }
3 Abstract base class for handlers that process pick events from a 3D map scene.
QList< QgsMapLayer * > layers() const
Returns the list of map layers to be rendered as a texture of the terrain.
void cameraChanged()
Emitted when camera has been updated.
3 Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:30
A rectangle specified with double values.
Definition: qgsrectangle.h:40
Base class for all map layer types.
Definition: qgsmaplayer.h:63
void maxTerrainGroundErrorChanged()
Emitted when the maximum terrain ground error has changed.
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
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.
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition: qgis.h:290
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.
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates...
SceneState
Enumeration of possible states of the 3D scene.
Definition: qgs3dmapscene.h:72
void terrainPendingJobsCountChanged()
Emitted when the number of terrain&#39;s pending jobs changes.
float zMax
Definition: qgsaabb.h:80
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world&#39;s coordinates) ...
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
QString skyboxFileBase() const
Returns base part of filenames of skybox (see setSkybox())
3 Definition of the world
void registerPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Registers an object that will get results of pick events on 3D entities. Does not take ownership of t...
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine&#39;s render settings (the frame graph can be accessed from here) ...
float zMin
Definition: qgsaabb.h:77
void viewportChanged()
Emitted when viewport rectangle has been updated.
QColor backgroundColor() const
Returns background color of the 3D map view.
float yMax
Definition: qgsaabb.h:79
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
bool hasSkyboxEnabled() const
Returns whether skybox is enabled.
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
void showCameraViewCenterChanged()
Emitted when the flag whether camera&#39;s view center is shown has changed.
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
3 Base class for 3D engine implementation.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
3 Object that controls camera movement based on user input
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:74
bool showCameraViewCenter() const
Returns whether to show camera&#39;s view center as a sphere (for debugging)
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:73
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
float xMin
Definition: qgsaabb.h:75
3D renderer that renders all features of a vector layer with the same 3D symbol.
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void unregisterPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object pick...
float xMax
Definition: qgsaabb.h:78
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene...
void maxTerrainScreenErrorChanged()
Emitted when the maximum terrain screen error has changed.
QList< QgsAbstract3DRenderer * > renderers() const
Returns list of extra 3D renderers.
float yMin
Definition: qgsaabb.h:76
void sceneStateChanged()
Emitted when the scene&#39;s state has changed.
virtual QSize size() const =0
Returns size of the engine&#39;s rendering area in pixels.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
virtual QgsRectangle extent() const =0
extent of the terrain in terrain&#39;s CRS
QString skyboxFileExtension() const
Returns extension part of filenames of skybox (see setSkybox())
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
void backgroundColorChanged()
Emitted when the background color has changed.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
QgsChunkedEntity::SceneState _sceneState(QgsCameraController *cameraController)
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.
The scene is still being loaded/updated.
Definition: qgs3dmapscene.h:75
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:61
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
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.