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