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