QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
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/QRenderSettings>
21#include <Qt3DRender/QSceneLoader>
22#include <Qt3DExtras/QForwardRenderer>
23#include <Qt3DExtras/QPhongMaterial>
24#include <Qt3DExtras/QPhongAlphaMaterial>
25#include <Qt3DExtras/QDiffuseSpecularMaterial>
26#include <Qt3DExtras/QSphereMesh>
27#include <Qt3DLogic/QFrameAction>
28#include <Qt3DRender/QEffect>
29#include <Qt3DRender/QTechnique>
30#include <Qt3DRender/QRenderPass>
31#include <Qt3DRender/QRenderState>
32#include <Qt3DRender/QCullFace>
33#include <Qt3DRender/QDepthTest>
34#include <QSurface>
35#include <QUrl>
36#include <QtMath>
37
38#include <QOpenGLContext>
39#include <QOpenGLFunctions>
40#include <QTimer>
41
42#include "qgs3daxis.h"
43#include "qgslogger.h"
44#include "qgsapplication.h"
45#include "qgsaabb.h"
46#include "qgsabstract3dengine.h"
47#include "qgs3dmapsettings.h"
48#include "qgs3dutils.h"
50#include "qgscameracontroller.h"
51#include "qgschunkedentity_p.h"
52#include "qgschunknode_p.h"
53#include "qgseventtracing.h"
54#include "qgsmeshlayer.h"
56#include "qgspoint3dsymbol.h"
58#include "qgspointcloudlayer.h"
60#include "qgssourcecache.h"
61#include "qgsterrainentity_p.h"
62#include "qgsterraingenerator.h"
63#include "qgstiledscenelayer.h"
66#include "qgsvectorlayer.h"
71#include "qgslinematerial_p.h"
72#include "qgs3dsceneexporter.h"
74#include "qgsmessageoutput.h"
75#include "qgsframegraph.h"
76
77#include "qgsskyboxentity.h"
78#include "qgsskyboxsettings.h"
79
80#include "qgswindow3dengine.h"
81#include "qgspointcloudlayer.h"
82
83std::function< QMap< QString, Qgs3DMapScene * >() > Qgs3DMapScene::sOpenScenesFunction = [] { return QMap< QString, Qgs3DMapScene * >(); };
84
86 : mMap( map )
87 , mEngine( engine )
88{
89
90 connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
91 onBackgroundColorChanged();
92
93 // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
94 // even if there's no change. Switching to "on demand" should only re-render when something has changed
95 // and we save quite a lot of resources
96 mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
97
98 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
99
100 // Camera
101 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
102 mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
103
104 mFrameAction = new Qt3DLogic::QFrameAction();
105 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
106 this, &Qgs3DMapScene::onFrameTriggered );
107 addComponent( mFrameAction ); // takes ownership
108
109 // Camera controlling
110 mCameraController = new QgsCameraController( this ); // attaches to the scene
111 mCameraController->resetView( 1000 );
112
113 addCameraViewCenterEntity( mEngine->camera() );
114 addCameraRotationCenterEntity( mCameraController );
115 updateLights();
116
117 // create terrain entity
118
119 createTerrainDeferred();
120 connect( &map, &Qgs3DMapSettings::extentChanged, this, &Qgs3DMapScene::createTerrain );
121 connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
122 connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
123 connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
124 connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
125 connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
126 connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
127 connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
128 connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
129 connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
130 connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
131 connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
132 connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
133 connect( &map, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapScene::onAmbientOcclusionSettingsChanged );
134 connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
135 connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
136 connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
137 connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
138 connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
140 connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
141 connect( &map, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapScene::onCameraNavigationModeChanged );
142 connect( &map, &Qgs3DMapSettings::debugOverlayEnabledChanged, this, &Qgs3DMapScene::onDebugOverlayEnabledChanged );
143
144 connect( &map, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapScene::on3DAxisSettingsChanged );
145
146 connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
147 {
148 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
149 for ( QgsMapLayer *layer : modelVectorLayers )
150 {
151 QgsAbstract3DRenderer *renderer = layer->renderer3D();
152 if ( renderer )
153 {
154 if ( renderer->type() == QLatin1String( "vector" ) )
155 {
156 const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
157 if ( pointSymbol->shapeProperty( QStringLiteral( "model" ) ).toString() == url )
158 {
159 removeLayerEntity( layer );
160 addLayerEntity( layer );
161 }
162 }
163 else if ( renderer->type() == QLatin1String( "rulebased" ) )
164 {
165 const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
166 for ( auto rule : rules )
167 {
168 const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
169 if ( pointSymbol->shapeProperty( QStringLiteral( "model" ) ).toString() == url )
170 {
171 removeLayerEntity( layer );
172 addLayerEntity( layer );
173 break;
174 }
175 }
176 }
177 }
178 }
179 } );
180
181 // listen to changes of layers in order to add/remove 3D renderer entities
182 connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
183
184 connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
185 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, this, &Qgs3DMapScene::onCameraChanged );
186
187 onSkyboxSettingsChanged();
188
189 // force initial update of chunked entities
190 onCameraChanged();
191 // force initial update of eye dome shading
192 onEyeDomeShadingSettingsChanged();
193 // force initial update of debugging setting of preview quads
194 onDebugShadowMapSettingsChanged();
195 onDebugDepthMapSettingsChanged();
196 // force initial update of ambient occlusion settings
197 onAmbientOcclusionSettingsChanged();
198
199 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
200 onCameraMovementSpeedChanged();
201
202 on3DAxisSettingsChanged();
203}
204
206{
207 const QgsDoubleRange yRange = elevationRange();
208 const QgsRectangle extent = sceneExtent();
209 const double side = std::max( extent.width(), extent.height() );
210 double d = side / 2 / std::tan( cameraController()->camera()->fieldOfView() / 2 * M_PI / 180 );
211 d += yRange.isInfinite() ? 0. : yRange.upper();
212 mCameraController->resetView( static_cast< float >( d ) );
213 return;
214}
215
217{
218 QgsPointXY center = extent.center();
219 QgsVector3D centerWorld = mMap.mapToWorldCoordinates( QVector3D( center.x(), center.y(), 0 ) );
220 QgsVector3D p1 = mMap.mapToWorldCoordinates( QVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
221 QgsVector3D p2 = mMap.mapToWorldCoordinates( QVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
222
223 float xSide = std::abs( p1.x() - p2.x() );
224 float ySide = std::abs( p1.z() - p2.z() );
225 if ( xSide < ySide )
226 {
227 float fov = 2 * std::atan( std::tan( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2 ) * cameraController()->camera()->aspectRatio() );
228 float r = xSide / 2.0f / std::tan( fov / 2.0f );
229 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
230 }
231 else
232 {
233 float fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
234 float r = ySide / 2.0f / std::tan( fov / 2.0f );
235 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
236 }
237}
238
239QVector<QgsPointXY> Qgs3DMapScene::viewFrustum2DExtent() const
240{
241 Qt3DRender::QCamera *camera = mCameraController->camera();
242 QVector<QgsPointXY> extent;
243 QVector<int> pointsOrder = { 0, 1, 3, 2 };
244 for ( int i : pointsOrder )
245 {
246 const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : mEngine->size().width(), ( ( i >> 1 ) & 1 ) ? 0 : mEngine->size().height() );
247 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, mEngine->size(), camera );
248 QVector3D dir = ray.direction();
249 if ( dir.y() == 0.0 )
250 dir.setY( 0.000001 );
251 double t = - ray.origin().y() / dir.y();
252 if ( t < 0 )
253 {
254 // If the projected point is on the back of the camera we choose the farthest point in the front
255 t = camera->farPlane();
256 }
257 else
258 {
259 // If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
260 t = std::min<float>( t, camera->farPlane() );
261 }
262 QVector3D planePoint = ray.origin() + t * dir;
263 QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
264 extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
265 }
266 return extent;
267}
268
270{
271 return mTerrain ? mTerrain->pendingJobsCount() : 0;
272}
273
275{
276 int count = 0;
277 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
278 count += entity->pendingJobsCount();
279 return count;
280}
281
282float Qgs3DMapScene::worldSpaceError( float epsilon, float distance ) const
283{
284 Qt3DRender::QCamera *camera = mCameraController->camera();
285 float fov = camera->fieldOfView();
286 const QSize size = mEngine->size();
287 float screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
288
289 // see Qgs3DUtils::screenSpaceError() for the inverse calculation (world space error to screen space error)
290 // with explanation of the math.
291 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
292 float err = frustumWidthAtDistance * epsilon / screenSizePx;
293 return err;
294}
295
296void Qgs3DMapScene::onCameraChanged()
297{
298 if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
299 {
300 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
301 const float viewWidthFromCenter = mCameraController->distance();
302 const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
303 mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
304 }
305
306 updateScene( true );
307 bool changedCameraPlanes = updateCameraNearFarPlanes();
308
309 if ( changedCameraPlanes )
310 {
311 // repeat update of entities - because we have updated camera's near/far planes,
312 // the active nodes may have changed as well
313 updateScene( true );
314 updateCameraNearFarPlanes();
315 }
316
317 onShadowSettingsChanged();
318
319 QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
320 emit viewed2DExtentFrom3DChanged( extent2D );
321}
322
323void Qgs3DMapScene::updateScene( bool forceUpdate )
324{
325 if ( forceUpdate )
326 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );
327
328 Qgs3DMapSceneEntity::SceneContext sceneContext;
329 Qt3DRender::QCamera *camera = mEngine->camera();
330 sceneContext.cameraFov = camera->fieldOfView();
331 sceneContext.cameraPos = camera->position();
332 const QSize size = mEngine->size();
333 sceneContext.screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
334 sceneContext.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
335
336
337 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
338 {
339 if ( forceUpdate || ( entity->isEnabled() && entity->needsUpdate() ) )
340 {
341 entity->handleSceneUpdate( sceneContext );
342 if ( entity->hasReachedGpuMemoryLimit() )
344 }
345 }
346
347 updateSceneState();
348}
349
350bool Qgs3DMapScene::updateCameraNearFarPlanes()
351{
352 // Update near and far plane from the terrain.
353 // this needs to be done with great care as we have kind of circular dependency here:
354 // active nodes are culled based on the current frustum (which involves near + far plane)
355 // and then based on active nodes we set near and far plane.
356 //
357 // All of this is just heuristics assuming that all other stuff is being rendered somewhere
358 // around the area where the terrain is.
359 //
360 // Near/far plane is setup in order to make best use of the depth buffer to avoid:
361 // 1. precision errors - if the range is too great
362 // 2. unwanted clipping of scene - if the range is too small
363
364 Qt3DRender::QCamera *camera = cameraController()->camera();
365 QMatrix4x4 viewMatrix = camera->viewMatrix();
366 float fnear = 1e9;
367 float ffar = 0;
368
369 // Iterate all scene entities to make sure that they will not get
370 // clipped by the near or far plane
371 for ( Qgs3DMapSceneEntity *se : std::as_const( mSceneEntities ) )
372 {
373 const QgsRange<float> depthRange = se->getNearFarPlaneRange( viewMatrix );
374
375 fnear = std::min( fnear, depthRange.lower() );
376 ffar = std::max( ffar, depthRange.upper() );
377 }
378
379 if ( fnear < 1 )
380 fnear = 1; // does not really make sense to use negative far plane (behind camera)
381
382 // the update didn't work out... this can happen if the scene does not contain
383 // any Qgs3DMapSceneEntity. Use the scene extent to compute near and far planes
384 // as a fallback.
385 if ( fnear == 1e9 && ffar == 0 )
386 {
387 QgsDoubleRange sceneYRange = elevationRange();
388 sceneYRange = sceneYRange.isInfinite() ? QgsDoubleRange( 0.0, 0.0 ) : sceneYRange;
389 const QgsAABB sceneBbox = Qgs3DUtils::mapToWorldExtent( mMap.extent(), sceneYRange.lower(), sceneYRange.upper(), mMap.origin() );
390 Qgs3DUtils::computeBoundingBoxNearFarPlanes( sceneBbox, viewMatrix, fnear, ffar );
391 }
392
393 // when zooming in a lot, fnear can become smaller than ffar. This should not happen
394 if ( fnear > ffar )
395 std::swap( fnear, ffar );
396
397 // set near/far plane - with some tolerance in front/behind expected near/far planes
398 float newFar = ffar * 2;
399 float newNear = fnear / 2;
400 if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
401 {
402 camera->setFarPlane( newFar );
403 camera->setNearPlane( newNear );
404 return true;
405 }
406
407 return false;
408}
409
410void Qgs3DMapScene::onFrameTriggered( float dt )
411{
412 mCameraController->frameTriggered( dt );
413
414 updateScene();
415
416 // lock changing the FPS counter to 5 fps
417 static int frameCount = 0;
418 static float accumulatedTime = 0.0f;
419
420 if ( !mMap.isFpsCounterEnabled() )
421 {
422 frameCount = 0;
423 accumulatedTime = 0;
424 return;
425 }
426
427 frameCount++;
428 accumulatedTime += dt;
429 if ( accumulatedTime >= 0.2f )
430 {
431 float fps = ( float )frameCount / accumulatedTime;
432 frameCount = 0;
433 accumulatedTime = 0.0f;
434 emit fpsCountChanged( fps );
435 }
436}
437
438void Qgs3DMapScene::createTerrain()
439{
440 if ( mTerrain )
441 {
442 mSceneEntities.removeOne( mTerrain );
443
444 delete mTerrain;
445 mTerrain = nullptr;
446 }
447
448 if ( !mTerrainUpdateScheduled )
449 {
450 // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
451 QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
452 mTerrainUpdateScheduled = true;
453 setSceneState( Updating );
454 }
455 else
456 {
458 }
459}
460
461void Qgs3DMapScene::createTerrainDeferred()
462{
463 if ( mMap.terrainRenderingEnabled() && mMap.terrainGenerator() )
464 {
465 double tile0width = mMap.terrainGenerator()->rootChunkExtent().width();
466 int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
467 QgsAABB rootBbox = mMap.terrainGenerator()->rootChunkBbox( mMap );
468 float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
469 const QgsAABB clippingBbox = Qgs3DUtils::mapToWorldExtent( mMap.extent(), rootBbox.zMin, rootBbox.zMax, mMap.origin() );
470 mMap.terrainGenerator()->setupQuadtree( rootBbox, rootError, maxZoomLevel, clippingBbox );
471
472 mTerrain = new QgsTerrainEntity( mMap );
473 mTerrain->setParent( this );
474 mTerrain->setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
475
476 mSceneEntities << mTerrain;
477
478 connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
479 connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
480 }
481 else
482 {
483 mTerrain = nullptr;
484 }
485
486 // make sure that renderers for layers are re-created as well
487 const QList<QgsMapLayer *> layers = mMap.layers();
488 for ( QgsMapLayer *layer : layers )
489 {
490 // remove old entity - if any
491 removeLayerEntity( layer );
492
493 // add new entity - if any 3D renderer
494 addLayerEntity( layer );
495 }
496
498 onCameraChanged(); // force update of the new terrain
499 mTerrainUpdateScheduled = false;
500}
501
502void Qgs3DMapScene::onBackgroundColorChanged()
503{
504 mEngine->setClearColor( mMap.backgroundColor() );
505}
506
507void Qgs3DMapScene::updateLights()
508{
509 for ( Qt3DCore::QEntity *entity : std::as_const( mLightEntities ) )
510 entity->deleteLater();
511 mLightEntities.clear();
512
513 const QList< QgsLightSource * > newLights = mMap.lightSources();
514 for ( const QgsLightSource *source : newLights )
515 {
516 mLightEntities.append( source->createEntity( mMap, this ) );
517 }
518
519 onShadowSettingsChanged();
520}
521
522void Qgs3DMapScene::updateCameraLens()
523{
524 mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
525 mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
526 onCameraChanged();
527}
528
529void Qgs3DMapScene::onLayerRenderer3DChanged()
530{
531 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
532 Q_ASSERT( layer );
533
534 // remove old entity - if any
535 removeLayerEntity( layer );
536
537 // add new entity - if any 3D renderer
538 addLayerEntity( layer );
539}
540
541void Qgs3DMapScene::onLayersChanged()
542{
543 QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
544 QList<QgsMapLayer *> layersAdded;
545 const QList<QgsMapLayer *> layers = mMap.layers();
546 for ( QgsMapLayer *layer : layers )
547 {
548 if ( !layersBefore.contains( layer ) )
549 {
550 layersAdded << layer;
551 }
552 else
553 {
554 layersBefore.remove( layer );
555 }
556 }
557
558 // what is left in layersBefore are layers that have been removed
559 for ( QgsMapLayer *layer : std::as_const( layersBefore ) )
560 {
561 removeLayerEntity( layer );
562 }
563
564 for ( QgsMapLayer *layer : std::as_const( layersAdded ) )
565 {
566 addLayerEntity( layer );
567 }
568}
569
571{
572 const QList<QgsMapLayer * > layers = mLayerEntities.keys();
573 for ( QgsMapLayer *layer : layers )
574 {
575 if ( QgsMapLayerTemporalProperties *temporalProperties = layer->temporalProperties() )
576 {
577 if ( temporalProperties->isActive() )
578 {
579 removeLayerEntity( layer );
580 addLayerEntity( layer );
581 }
582 }
583 }
584}
585
586void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
587{
588 bool needsSceneUpdate = false;
589 QgsAbstract3DRenderer *renderer = layer->renderer3D();
590 if ( renderer )
591 {
592 // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
593 // It has happened before that renderer pointed to a different layer (probably after copying a style).
594 // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
595 // the vector layer 3D renderer classes are not available.
596 if ( layer->type() == Qgis::LayerType::Vector &&
597 ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
598 {
599 static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
600 if ( renderer->type() == QLatin1String( "vector" ) )
601 {
602 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
603 if ( vlayer->geometryType() == Qgis::GeometryType::Point )
604 {
605 const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
606 if ( pointSymbol->shape() == Qgis::Point3DShape::Model )
607 {
608 mModelVectorLayers.append( layer );
609 }
610 }
611 }
612 else if ( renderer->type() == QLatin1String( "rulebased" ) )
613 {
614 const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
615 for ( auto rule : rules )
616 {
617 const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
618 if ( pointSymbol && pointSymbol->shape() == Qgis::Point3DShape::Model )
619 {
620 mModelVectorLayers.append( layer );
621 break;
622 }
623 }
624 }
625 }
626 else if ( layer->type() == Qgis::LayerType::Mesh && renderer->type() == QLatin1String( "mesh" ) )
627 {
628 QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
629 meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
630
631 // Before entity creation, set the maximum texture size
632 // Not very clean, but for now, only place found in the workflow to do that simple
633 QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
634 sym->setMaximumTextureSize( maximumTextureSize() );
635 meshRenderer->setSymbol( sym );
636 }
637 else if ( layer->type() == Qgis::LayerType::PointCloud && renderer->type() == QLatin1String( "pointcloud" ) )
638 {
639 QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
640 pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
641 }
642 else if ( layer->type() == Qgis::LayerType::TiledScene && renderer->type() == QLatin1String( "tiledscene" ) )
643 {
644 QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
645 tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
646 }
647
648 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
649 if ( newEntity )
650 {
651 newEntity->setParent( this );
652 mLayerEntities.insert( layer, newEntity );
653
654 finalizeNewEntity( newEntity );
655
656 if ( Qgs3DMapSceneEntity *sceneNewEntity = qobject_cast<Qgs3DMapSceneEntity *>( newEntity ) )
657 {
658 needsSceneUpdate = true;
659 mSceneEntities.append( sceneNewEntity );
660
661 connect( sceneNewEntity, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
662 {
663 finalizeNewEntity( entity );
664 // this ensures to update the near/far planes with the exact bounding box of the new entity.
665 updateCameraNearFarPlanes();
666 } );
667
668 connect( sceneNewEntity, &Qgs3DMapSceneEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
669 }
670 }
671 }
672
673 if ( needsSceneUpdate )
674 onCameraChanged(); // needed for chunked entities
675
676 connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
677
678 if ( layer->type() == Qgis::LayerType::Vector )
679 {
680 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
681 connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
682 connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
683 }
684
685 if ( layer->type() == Qgis::LayerType::Mesh )
686 {
687 connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
688 }
689
690 if ( layer->type() == Qgis::LayerType::PointCloud )
691 {
692 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
693 connect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
694 connect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
695 }
696}
697
698void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
699{
700 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
701
702 if ( Qgs3DMapSceneEntity *sceneEntity = qobject_cast<Qgs3DMapSceneEntity *>( entity ) )
703 {
704 mSceneEntities.removeOne( sceneEntity );
705 }
706
707 if ( entity )
708 entity->deleteLater();
709
710 disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
711
712 if ( layer->type() == Qgis::LayerType::Vector )
713 {
714 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
715 disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
716 disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
717 mModelVectorLayers.removeAll( layer );
718 }
719
720 if ( layer->type() == Qgis::LayerType::Mesh )
721 {
722 disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
723 }
724
725 if ( layer->type() == Qgis::LayerType::PointCloud )
726 {
727 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
728 disconnect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
729 disconnect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
730 }
731}
732
733void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
734{
735 // this is probably not the best place for material-specific configuration,
736 // maybe this could be more generalized when other materials need some specific treatment
737 const QList< QgsLineMaterial *> childLineMaterials = newEntity->findChildren<QgsLineMaterial *>();
738 for ( QgsLineMaterial *lm : childLineMaterials )
739 {
740 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, lm, [lm, this]
741 {
742 lm->setViewportSize( mEngine->size() );
743 } );
744
745 lm->setViewportSize( mEngine->size() );
746 }
747 // configure billboard's viewport when the viewport is changed.
748 const QList< QgsPoint3DBillboardMaterial *> childBillboardMaterials = newEntity->findChildren<QgsPoint3DBillboardMaterial *>();
749 for ( QgsPoint3DBillboardMaterial *bm : childBillboardMaterials )
750 {
751 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, bm, [bm, this]
752 {
753 bm->setViewportSize( mEngine->size() );
754 } );
755
756 bm->setViewportSize( mEngine->size() );
757 }
758
759 // Finalize adding the 3D transparent objects by adding the layer components to the entities
760 QgsFrameGraph *frameGraph = mEngine->frameGraph();
761 Qt3DRender::QLayer *transparentLayer = frameGraph->transparentObjectLayer();
762 const QList< Qt3DRender::QMaterial *> childMaterials = newEntity->findChildren<Qt3DRender::QMaterial *>();
763 for ( Qt3DRender::QMaterial *material : childMaterials )
764 {
765 // This handles the phong material without data defined properties.
766 if ( Qt3DExtras::QDiffuseSpecularMaterial *ph = qobject_cast<Qt3DExtras::QDiffuseSpecularMaterial *>( material ) )
767 {
768 if ( ph->diffuse().value<QColor>().alphaF() != 1.0f )
769 {
770 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( ph->parent() );
771 if ( entity && !entity->components().contains( transparentLayer ) )
772 {
773 entity->addComponent( transparentLayer );
774 }
775 }
776 }
777 else
778 {
779 // This handles the phong material with data defined properties, the textured case and point (instanced) symbols.
780 Qt3DRender::QEffect *effect = material->effect();
781 if ( effect )
782 {
783 const QVector< Qt3DRender::QParameter *> parameters = effect->parameters();
784 for ( const Qt3DRender::QParameter *parameter : parameters )
785 {
786 if ( parameter->name() == "opacity" && parameter->value() != 1.0f )
787 {
788 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( material->parent() );
789 if ( entity && !entity->components().contains( transparentLayer ) )
790 {
791 entity->addComponent( transparentLayer );
792 }
793 break;
794 }
795 }
796 }
797 }
798 }
799}
800
801int Qgs3DMapScene::maximumTextureSize() const
802{
803 QSurface *surface = mEngine->surface();
804 QOpenGLContext context;
805 context.create();
806 bool success = context.makeCurrent( surface );
807
808 if ( success )
809 {
810 QOpenGLFunctions openglFunctions = QOpenGLFunctions( &context );
811
812 GLint size;
813 openglFunctions.initializeOpenGLFunctions();
814 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
815 return int( size );
816 }
817 else
818 {
819 return 4096; //we can't have a context to defined the max texture size, we use this reasonable value
820 }
821
822}
823
824void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
825{
826 mEntityCameraViewCenter = new Qt3DCore::QEntity;
827
828 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
829 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
830 connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
831 {
832 trCameraViewCenter->setTranslation( camera->viewCenter() );
833 } );
834
835 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
836 materialCameraViewCenter->setAmbient( Qt::red );
837 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
838
839 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
840 rendererCameraViewCenter->setRadius( 10 );
841 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
842
843 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
844 mEntityCameraViewCenter->setParent( this );
845
846 connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
847 {
848 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
849 } );
850}
851
852void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
853{
854 if ( mSceneState == state )
855 return;
856 mSceneState = state;
857 emit sceneStateChanged();
858}
859
860void Qgs3DMapScene::updateSceneState()
861{
862 if ( mTerrainUpdateScheduled )
863 {
864 setSceneState( Updating );
865 return;
866 }
867
868 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
869 {
870 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
871 {
872 setSceneState( Updating );
873 return;
874 }
875 }
876
877 setSceneState( Ready );
878}
879
880void Qgs3DMapScene::onSkyboxSettingsChanged()
881{
882 QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
883 if ( mSkybox != nullptr )
884 {
885 mSkybox->deleteLater();
886 mSkybox = nullptr;
887 }
888
889 mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
890
891 if ( mMap.isSkyboxEnabled() )
892 {
893 QMap<QString, QString> faces;
894 switch ( skyboxSettings.skyboxType() )
895 {
897 faces = skyboxSettings.cubeMapFacesPaths();
898 mSkybox = new QgsCubeFacesSkyboxEntity(
899 faces[QStringLiteral( "posX" )], faces[QStringLiteral( "posY" )], faces[QStringLiteral( "posZ" )],
900 faces[QStringLiteral( "negX" )], faces[QStringLiteral( "negY" )], faces[QStringLiteral( "negZ" )],
901 this
902 );
903 break;
905 mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
906 break;
907 }
908 }
909}
910
911void Qgs3DMapScene::onShadowSettingsChanged()
912{
913 QgsFrameGraph *frameGraph = mEngine->frameGraph();
914
915 const QList< QgsLightSource * > lightSources = mMap.lightSources();
916 QList< QgsDirectionalLightSettings * > directionalLightSources;
917 for ( QgsLightSource *source : lightSources )
918 {
919 if ( source->type() == Qgis::LightSourceType::Directional )
920 {
921 directionalLightSources << qgis::down_cast< QgsDirectionalLightSettings * >( source );
922 }
923 }
924
925 QgsShadowSettings shadowSettings = mMap.shadowSettings();
926 int selectedLight = shadowSettings.selectedDirectionalLight();
927 if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLightSources.count() )
928 {
929 frameGraph->setShadowRenderingEnabled( true );
930 frameGraph->setShadowBias( shadowSettings.shadowBias() );
931 frameGraph->setShadowMapResolution( shadowSettings.shadowMapResolution() );
932 QgsDirectionalLightSettings light = *directionalLightSources.at( selectedLight );
933 frameGraph->setupDirectionalLight( light, shadowSettings.maximumShadowRenderingDistance() );
934 }
935 else
936 frameGraph->setShadowRenderingEnabled( false );
937}
938
939void Qgs3DMapScene::onAmbientOcclusionSettingsChanged()
940{
941 QgsFrameGraph *frameGraph = mEngine->frameGraph();
942 QgsAmbientOcclusionSettings ambientOcclusionSettings = mMap.ambientOcclusionSettings();
943 frameGraph->setAmbientOcclusionEnabled( ambientOcclusionSettings.isEnabled() );
944 frameGraph->setAmbientOcclusionRadius( ambientOcclusionSettings.radius() );
945 frameGraph->setAmbientOcclusionIntensity( ambientOcclusionSettings.intensity() );
946 frameGraph->setAmbientOcclusionThreshold( ambientOcclusionSettings.threshold() );
947}
948
949void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
950{
952}
953
954void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
955{
957}
958
959void Qgs3DMapScene::onDebugOverlayEnabledChanged()
960{
962 mEngine->renderSettings()->setRenderPolicy( mMap.isDebugOverlayEnabled() ? Qt3DRender::QRenderSettings::Always : Qt3DRender::QRenderSettings::OnDemand );
963}
964
965void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
966{
967 bool edlEnabled = mMap.eyeDomeLightingEnabled();
968 double edlStrength = mMap.eyeDomeLightingStrength();
969 double edlDistance = mMap.eyeDomeLightingDistance();
970 mEngine->frameGraph()->setupEyeDomeLighting( edlEnabled, edlStrength, edlDistance );
971}
972
973void Qgs3DMapScene::onCameraMovementSpeedChanged()
974{
975 mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
976}
977
978void Qgs3DMapScene::onCameraNavigationModeChanged()
979{
980 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
981}
982
984{
985 QVector<QString> notParsedLayers;
986 Qgs3DSceneExporter exporter;
987
988 exporter.setTerrainResolution( exportSettings.terrrainResolution() );
989 exporter.setSmoothEdges( exportSettings.smoothEdges() );
990 exporter.setExportNormals( exportSettings.exportNormals() );
991 exporter.setExportTextures( exportSettings.exportTextures() );
992 exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
993 exporter.setScale( exportSettings.scale() );
994
995 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
996 {
997 QgsMapLayer *layer = it.key();
998 Qt3DCore::QEntity *rootEntity = it.value();
999 Qgis::LayerType layerType = layer->type();
1000 switch ( layerType )
1001 {
1003 if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1004 notParsedLayers.push_back( layer->name() );
1005 break;
1014 notParsedLayers.push_back( layer->name() );
1015 break;
1016 }
1017 }
1018
1019 if ( mTerrain )
1020 exporter.parseTerrain( mTerrain, "Terrain" );
1021
1022 exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1023
1024 if ( !notParsedLayers.empty() )
1025 {
1026 QString message = tr( "The following layers were not exported:" ) + "\n";
1027 for ( const QString &layerName : notParsedLayers )
1028 message += layerName + "\n";
1029 QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText );
1030 }
1031}
1032
1033QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1034{
1035 QVector<const QgsChunkNode *> chunks;
1036 if ( !mLayerEntities.contains( layer ) ) return chunks;
1037 if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[ layer ] ) )
1038 {
1039 const QList< QgsChunkNode * > activeNodes = c->activeNodes();
1040 for ( QgsChunkNode *n : activeNodes )
1041 chunks.push_back( n );
1042 }
1043 return chunks;
1044}
1045
1047{
1048 return mMap.extent();
1049}
1050
1052{
1053 double yMin = std::numeric_limits< double >::max();
1054 double yMax = std::numeric_limits< double >::lowest();
1055 if ( mMap.terrainRenderingEnabled() && mTerrain )
1056 {
1057 const QgsAABB bbox = mTerrain->rootNode()->bbox();
1058 yMin = std::min( yMin, static_cast< double >( bbox.yMin ) );
1059 yMax = std::max( yMax, static_cast< double >( bbox.yMax ) );
1060 }
1061
1062 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); it++ )
1063 {
1064 QgsMapLayer *layer = it.key();
1065 switch ( layer->type() )
1066 {
1068 {
1069 QgsPointCloudLayer *pcl = qobject_cast< QgsPointCloudLayer *>( layer );
1070 QgsDoubleRange zRange = pcl->elevationProperties()->calculateZRange( pcl );
1071 yMin = std::min( yMin, zRange.lower() );
1072 yMax = std::max( yMax, zRange.upper() );
1073 break;
1074 }
1076 {
1077 QgsMeshLayer *meshLayer = qobject_cast< QgsMeshLayer *>( layer );
1078 QgsAbstract3DRenderer *renderer3D = meshLayer->renderer3D();
1079 if ( renderer3D )
1080 {
1081 QgsMeshLayer3DRenderer *meshLayerRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer3D );
1082 const int verticalGroupDatasetIndex = meshLayerRenderer->symbol()->verticalDatasetGroupIndex();
1083 const QgsMeshDatasetGroupMetadata verticalGroupMetadata = meshLayer->datasetGroupMetadata( verticalGroupDatasetIndex );
1084 const double verticalScale = meshLayerRenderer->symbol()->verticalScale();
1085 yMin = std::min( yMin, verticalGroupMetadata.minimum() * verticalScale );
1086 yMax = std::max( yMax, verticalGroupMetadata.maximum() * verticalScale );
1087 }
1088 break;
1089 }
1091 {
1092 QgsTiledSceneLayer *sceneLayer = qobject_cast< QgsTiledSceneLayer *>( layer );
1093 const QgsDoubleRange zRange = sceneLayer->elevationProperties()->calculateZRange( sceneLayer );
1094 if ( !zRange.isInfinite() && !zRange.isEmpty() )
1095 {
1096 yMin = std::min( yMin, zRange.lower() );
1097 yMax = std::max( yMax, zRange.upper() );
1098 }
1099 break;
1100 }
1107 break;
1108 }
1109 }
1110 const QgsDoubleRange yRange( std::min( yMin, std::numeric_limits<double>::max() ),
1111 std::max( yMax, std::numeric_limits<double>::lowest() ) );
1112 return yRange.isEmpty() ? QgsDoubleRange() : yRange;
1113}
1114
1115QMap< QString, Qgs3DMapScene * > Qgs3DMapScene::openScenes()
1116{
1117 return sOpenScenesFunction();
1118}
1119
1120void Qgs3DMapScene::addCameraRotationCenterEntity( QgsCameraController *controller )
1121{
1122 mEntityRotationCenter = new Qt3DCore::QEntity;
1123
1124 Qt3DCore::QTransform *trRotationCenter = new Qt3DCore::QTransform;
1125 mEntityRotationCenter->addComponent( trRotationCenter );
1126 Qt3DExtras::QPhongMaterial *materialRotationCenter = new Qt3DExtras::QPhongMaterial;
1127 materialRotationCenter->setAmbient( Qt::blue );
1128 mEntityRotationCenter->addComponent( materialRotationCenter );
1129 Qt3DExtras::QSphereMesh *rendererRotationCenter = new Qt3DExtras::QSphereMesh;
1130 rendererRotationCenter->setRadius( 10 );
1131 mEntityRotationCenter->addComponent( rendererRotationCenter );
1132 mEntityRotationCenter->setEnabled( false );
1133 mEntityRotationCenter->setParent( this );
1134
1135 connect( controller, &QgsCameraController::cameraRotationCenterChanged, this, [trRotationCenter]( QVector3D center )
1136 {
1137 trRotationCenter->setTranslation( center );
1138 } );
1139
1140 connect( &mMap, &Qgs3DMapSettings::showCameraRotationCenterChanged, this, [this]
1141 {
1142 mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() );
1143 } );
1144}
1145
1146void Qgs3DMapScene::on3DAxisSettingsChanged()
1147{
1148 if ( m3DAxis )
1149 {
1150 m3DAxis->onAxisSettingsChanged();
1151 }
1152 else
1153 {
1154 if ( QgsWindow3DEngine *engine = dynamic_cast<QgsWindow3DEngine *>( mEngine ) )
1155 {
1156 m3DAxis = new Qgs3DAxis( static_cast<Qgs3DMapCanvas *>( engine->window() ),
1157 engine->root(),
1158 this,
1159 mCameraController,
1160 &mMap );
1161 }
1162 }
1163}
LayerType
Types of layers that can be added to a map.
Definition qgis.h:114
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ Directional
Directional light source.
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
Manages the various settings the user can choose from when exporting a 3D scene 3.
bool exportNormals() const
Returns whether normals will be exported.
int terrrainResolution() const
Returns the terrain resolution.
QString sceneFolderPath() const
Returns the scene folder path.
float scale() const
Returns the scale of the exported model.
int terrainTextureResolution() const
Returns the terrain texture resolution.
QString sceneName() const
Returns the scene name.
bool smoothEdges() const
Returns whether triangles edges will look smooth.
bool exportTextures() const
Returns whether textures will be exported.
QVector< const QgsChunkNode * > getLayerActiveChunkNodes(QgsMapLayer *layer)
Returns the active chunk nodes of layer.
void exportScene(const Qgs3DMapExportSettings &exportSettings)
Exports the scene according to the scene export settings.
void terrainPendingJobsCountChanged()
Emitted when the number of terrain's pending jobs changes.
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes.
void setViewFrom2DExtent(const QgsRectangle &extent)
Resets camera view to show the extent extent (top view)
Qgs3DMapScene(Qgs3DMapSettings &map, QgsAbstract3DEngine *engine)
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
QgsAbstract3DEngine * engine() const
Returns the abstract 3D engine.
void gpuMemoryLimitReached()
Emitted when one of the entities reaches its GPU memory limit and it is not possible to lower the GPU...
QgsDoubleRange elevationRange() const
Returns the scene's elevation range.
QgsCameraController * cameraController() const
Returns camera controller.
SceneState
Enumeration of possible states of the 3D scene.
@ Ready
The scene is fully loaded/updated.
@ Updating
The scene is still being loaded/updated.
int totalPendingJobsCount() const
Returns number of pending jobs for all chunked entities.
void updateTemporal()
Updates the temporale entities.
static Q_DECL_DEPRECATED QMap< QString, Qgs3DMapScene * > openScenes()
Returns a map of 3D map scenes (by name) open in the QGIS application.
void totalPendingJobsCountChanged()
Emitted when the total number of pending jobs changes.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is activated or deactivated.
QgsRectangle sceneExtent() const
Returns the scene extent in the map's CRS.
void sceneStateChanged()
Emitted when the scene's state has changed.
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
QList< QgsMapLayer * > layers() const
Returns the layers that contain chunked entities.
QVector< QgsPointXY > viewFrustum2DExtent() const
Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid.
float worldSpaceError(float epsilon, float distance) const
Given screen error (in pixels) and distance from camera (in 3D world coordinates),...
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
static std::function< QMap< QString, Qgs3DMapScene * >() > sOpenScenesFunction
Static function for returning open 3D map scenes.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
void extentChanged()
Emitted when the 3d view's 2d extent has changed.
void mapTileResolutionChanged()
Emitted when the map tile resoulution has changed.
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
bool isDebugOverlayEnabled() const
Returns whether debug overlay is enabled.
Qt::Corner debugDepthMapCorner() const
Returns the corner where the shadow map preview is displayed.
void eyeDomeLightingDistanceChanged()
Emitted when the eye dome lighting distance has changed.
void terrainShadingChanged()
Emitted when terrain shading enabled flag or terrain shading material has changed.
QgsVector3D mapToWorldCoordinates(const QgsVector3D &mapCoords) const
Converts map coordinates to 3D world coordinates (applies offset and turns (x,y,z) into (x,...
double cameraMovementSpeed() const
Returns the camera movement speed.
Qt3DRender::QCameraLens::ProjectionType projectionType() const
Returns the camera lens' projection type.
bool debugDepthMapEnabled() const
Returns whether the shadow map debugging is enabled.
bool isSkyboxEnabled() const
Returns whether the skybox is enabled.
void debugDepthMapSettingsChanged()
Emitted when depth map debugging has changed.
Qgis::NavigationMode cameraNavigationMode() const
Returns the navigation mode used by the camera.
double eyeDomeLightingStrength() const
Returns the eye dome lighting strength value.
void backgroundColorChanged()
Emitted when the background color has changed.
Qt::Corner debugShadowMapCorner() const
Returns the corner where the shadow map preview is displayed.
bool showCameraViewCenter() const
Returns whether to show camera's view center as a sphere (for debugging)
void showCameraRotationCenterChanged()
Emitted when the flag whether camera's rotation center is shown has changed.
void cameraNavigationModeChanged()
Emitted when the camera navigation mode was changed.
void shadowSettingsChanged()
Emitted when shadow rendering settings are changed.
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
void eyeDomeLightingEnabledChanged()
Emitted when the flag whether eye dome lighting is used has changed.
void debugOverlayEnabledChanged(bool debugOverlayEnabled)
Emitted when the debug overaly is enabled or disabled.
void skyboxSettingsChanged()
Emitted when skybox settings are changed.
QgsShadowSettings shadowSettings() const
Returns the current configuration of shadows.
QList< QgsLightSource * > lightSources() const
Returns list of directional light sources defined in the scene.
double debugDepthMapSize() const
Returns the size of the shadow map preview.
void projectionTypeChanged()
Emitted when the camera lens projection type changes.
float fieldOfView() const
Returns the camera lens' field of view.
QgsAmbientOcclusionSettings ambientOcclusionSettings() const
Returns the current configuration of screen space ambient occlusion.
QgsRectangle extent() const
Returns the 3D scene's 2D extent in the 3D scene's CRS.
int eyeDomeLightingDistance() const
Returns the eye dome lighting distance value (contributes to the contrast of the image)
void lightSourcesChanged()
Emitted when any of the light source settings in the map changes.
void showLightSourceOriginsChanged()
Emitted when the flag whether light source origins are shown has changed.
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
QColor backgroundColor() const
Returns background color of the 3D map view.
double debugShadowMapSize() const
Returns the size of the shadow map preview.
QgsVector3D worldToMapCoordinates(const QgsVector3D &worldCoords) const
Converts 3D world coordinates to map coordinates (applies offset and turns (x,y,z) into (x,...
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
void maxTerrainScreenErrorChanged()
Emitted when the maximum terrain screen error has changed.
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
bool debugShadowMapEnabled() const
Returns whether the shadow map debugging is enabled.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is enabled or disabled.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
void ambientOcclusionSettingsChanged()
Emitted when ambient occlusion rendering settings are changed.
void layersChanged()
Emitted when the list of map layers for 3d rendering has changed.
void eyeDomeLightingStrengthChanged()
Emitted when the eye dome lighting strength has changed.
QgsSkyboxSettings skyboxSettings() const
Returns the current configuration of the skybox.
void cameraMovementSpeedChanged()
Emitted when the camera movement speed was changed.
bool eyeDomeLightingEnabled() const
Returns whether eye dome lighting is used.
bool isFpsCounterEnabled() const
Returns whether FPS counter label is enabled.
void fieldOfViewChanged()
Emitted when the camera lens field of view changes.
QList< QgsMapLayer * > layers() const
Returns the list of 3D map layers to be rendered in the scene.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
void debugShadowMapSettingsChanged()
Emitted when shadow map debugging has changed.
void showCameraViewCenterChanged()
Emitted when the flag whether camera's view center is shown has changed.
void maxTerrainGroundErrorChanged()
Emitted when the maximum terrain ground error has changed.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
bool showCameraRotationCenter() const
Returns whether to show camera's rotation center as a sphere (for debugging)
Entity that handles the exporting of 3D scene.
void setExportTextures(bool exportTextures)
Sets whether the textures will be exported.
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
void save(const QString &sceneName, const QString &sceneFolderPath)
Saves the scene to a .obj file.
void setTerrainResolution(int resolution)
Sets the terrain resolution.
void setTerrainTextureResolution(int resolution)
Sets the terrain texture resolution.
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
void setScale(float scale)
Sets the scale of the exported 3D model.
void setExportNormals(bool exportNormals)
Sets whether the normals will be exported.
void setSmoothEdges(bool smoothEdges)
Sets whether the triangles will look smooth.
static int maxZoomLevel(double tile0width, double tileResolution, double maxError)
Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom le...
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
static void computeBoundingBoxNearFarPlanes(const QgsAABB &bbox, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar)
This routine computes nearPlane farPlane from the closest and farthest corners point of bounding box ...
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
float yMax
Definition qgsaabb.h:90
float zMax
Definition qgsaabb.h:91
float yMin
Definition qgsaabb.h:87
float zMin
Definition qgsaabb.h:88
void sizeChanged()
Emitted after a call to setSize()
virtual QSurface * surface() const =0
Returns the surface of the engine.
virtual Qt3DRender::QCamera * camera()=0
Returns pointer to the engine's camera entity.
virtual void setClearColor(const QColor &color)=0
Sets background color of the scene.
virtual void setFrustumCullingEnabled(bool enabled)=0
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
QgsFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
Base class for all renderers that may to participate in 3D view.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
virtual Qt3DCore::QEntity * createEntity(const Qgs3DMapSettings &map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
class containing the configuration of ambient occlusion rendering 3
float radius() const
Returns the radius parameter of the ambient occlusion effect.
bool isEnabled() const
Returns whether ambient occlusion effect is enabled.
float intensity() const
Returns the shading factor of the ambient occlusion effect.
float threshold() const
Returns at what amount of occlusion the effect will kick in.
static QgsSourceCache * sourceCache()
Returns the application's source cache, used for caching embedded and remote source strings as local ...
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
float distance() const
Returns distance of the camera from the point it is looking at.
void setCameraNavigationMode(Qgis::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
void cameraChanged()
Emitted when camera has been updated.
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates)
void setViewFromTop(float worldX, float worldY, float distance, float yaw=0)
Sets camera to look down towards given point in world coordinate, in given distance from plane with z...
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
A skybox constructed from a 6 cube faces.
QgsRange which stores a range of double values.
Definition qgsrange.h:231
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:285
void setShadowBias(float shadowBias)
Sets the shadow bias value.
void setupShadowMapDebugging(bool enabled, Qt::Corner corner, double size)
Sets the shadow map debugging view port.
void setShadowMapResolution(int resolution)
Sets the resolution of the shadow map.
void setAmbientOcclusionIntensity(float intensity)
Sets the ambient occlusion intensity.
Qt3DRender::QLayer * transparentObjectLayer()
Returns a layer object used to indicate that the object is transparent.
void setDebugOverlayEnabled(bool enabled)
Sets whether debug overlay is enabled.
void setShadowRenderingEnabled(bool enabled)
Sets whether the shadow rendering is enabled.
void setupDepthMapDebugging(bool enabled, Qt::Corner corner, double size)
Sets the depth map debugging view port.
void setupDirectionalLight(const QgsDirectionalLightSettings &light, float maximumShadowRenderingDistance)
Sets shadow rendering to use a directional light.
void setAmbientOcclusionThreshold(float threshold)
Sets the ambient occlusion threshold.
void setupEyeDomeLighting(bool enabled, double strength, int distance)
Sets eye dome lighting shading related settings.
void setAmbientOcclusionEnabled(bool enabled)
Sets whether Screen Space Ambient Occlusion will be enabled.
void setAmbientOcclusionRadius(float radius)
Sets the ambient occlusion radius.
virtual QgsDoubleRange calculateZRange(QgsMapLayer *layer) const
Attempts to calculate the overall elevation or z range for the specified layer, using the settings de...
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition qgsmaplayer.h:75
QString name
Definition qgsmaplayer.h:79
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
Qgis::LayerType type
Definition qgsmaplayer.h:85
void rendererChanged()
Signal emitted when renderer is changed.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
void layerModified()
Emitted when modifications has been done on layer.
double verticalScale() const
Returns mesh vertical scale.
int verticalDatasetGroupIndex() const
Returns the index of the dataset group that will be used to render the vertical component of the 3D m...
void setMaximumTextureSize(int maximumTextureSize)
Sets the maximum texture size supported by the hardware Used to store the GL_MAX_TEXTURE_SIZE value t...
QgsMesh3DSymbol * clone() const override SIP_FACTORY
Returns a new instance of the symbol with the same settings.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
double minimum() const
Returns minimum scalar value/vector magnitude present for whole dataset group.
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
3D renderer that renders all mesh triangles of a mesh layer.
void setSymbol(QgsMesh3DSymbol *symbol)
Sets 3D symbol associated with the renderer.
const QgsMesh3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
void setLayer(QgsMeshLayer *layer)
Sets vector layer associated with the renderer.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
A skybox constructed from a panoramic image.
void setViewportSize(const QSizeF size)
Set the size of the view port.
Qgis::Point3DShape shape() const
Returns 3D shape for points.
QVariant shapeProperty(const QString &property) const
Returns the value for a specific shape property.
3D renderer that renders all points from a point cloud layer
void setLayer(QgsPointCloudLayer *layer)
Sets point cloud layer associated with the renderer.
Represents a map layer supporting display of point clouds.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
A template based class for storing ranges (lower to upper values).
Definition qgsrange.h:46
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:78
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:85
bool isEmpty() const
Returns true if the range is empty, ie the lower bound equals (or exceeds) the upper bound and either...
Definition qgsrange.h:125
A representation of a ray in 3D.
Definition qgsray3d.h:31
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QgsPointXY center() const
Returns the center point of the rectangle.
double height() const
Returns the height of the rectangle.
QList< QgsRuleBased3DRenderer::Rule * > RuleList
class containing the configuration of shadows rendering 3
int selectedDirectionalLight() const
Returns the selected direcctional light used to cast shadows.
bool renderShadows() const
Returns whether shadow rendering is enabled.
int shadowMapResolution() const
Returns the resolution of the shadow map texture used to generate the shadows.
double maximumShadowRenderingDistance() const
Returns the maximum shadow rendering distance accounted for when rendering shadows Objects further aw...
double shadowBias() const
Returns the shadow bias used to correct the numerical imprecision of shadows (for the depth test) Thi...
Contains the configuration of a skybox entity.
QMap< QString, QString > cubeMapFacesPaths() const
Returns a map containing the path of each texture specified by the user.
QgsSkyboxEntity::SkyboxType skyboxType() const
Returns the type of the skybox.
QString panoramicTexturePath() const
Returns the panoramic texture path of a skybox of type "Panormaic skybox".
void remoteSourceFetched(const QString &url)
Emitted when the cache has finished retrieving a 3D model from a remote url.
virtual float rootChunkError(const Qgs3DMapSettings &map) const
Returns error of the root chunk in world coordinates.
virtual QgsAABB rootChunkBbox(const Qgs3DMapSettings &map) const
Returns bounding box of the root chunk.
virtual QgsRectangle rootChunkExtent() const =0
extent of the terrain's root chunk in terrain's CRS
void setLayer(QgsTiledSceneLayer *layer)
Sets tiled scene layer associated with the renderer.
Represents a map layer supporting display of tiled scene objects.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:52
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
3D renderer that renders all features of a vector layer with the same 3D symbol.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition qgis.h:5476