QGIS API Documentation 3.43.0-Master (c4a2e9c6d2f)
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#include "moc_qgs3dmapscene.cpp"
18
19#include <Qt3DRender/QCamera>
20#include <Qt3DRender/QMesh>
21#include <Qt3DRender/QRenderSettings>
22#include <Qt3DRender/QSceneLoader>
23#include <Qt3DExtras/QForwardRenderer>
24#include <Qt3DExtras/QPhongMaterial>
25#include <Qt3DExtras/QPhongAlphaMaterial>
26#include <Qt3DExtras/QDiffuseSpecularMaterial>
27#include <Qt3DExtras/QSphereMesh>
28#include <Qt3DLogic/QFrameAction>
29#include <Qt3DRender/QEffect>
30#include <Qt3DRender/QTechnique>
31#include <Qt3DRender/QRenderPass>
32#include <Qt3DRender/QRenderState>
33#include <Qt3DRender/QCullFace>
34#include <Qt3DRender/QDepthTest>
35#include <QSurface>
36#include <QUrl>
37#include <QtMath>
38
39#include <QOpenGLContext>
40#include <QOpenGLFunctions>
41#include <QTimer>
42
43#include "qgs3daxis.h"
44#include "qgslogger.h"
45#include "qgsapplication.h"
46#include "qgsaabb.h"
47#include "qgsabstract3dengine.h"
48#include "qgs3dmapsettings.h"
49#include "qgs3dutils.h"
51#include "qgscameracontroller.h"
52#include "qgschunkedentity.h"
53#include "qgschunknode.h"
54#include "qgseventtracing.h"
55#include "qgsgeotransform.h"
57#include "qgsmaterial.h"
58#include "qgsmeshlayer.h"
60#include "qgspoint3dsymbol.h"
62#include "qgspointcloudlayer.h"
64#include "qgssourcecache.h"
65#include "qgsterrainentity.h"
66#include "qgsterraingenerator.h"
67#include "qgstiledscenelayer.h"
70#include "qgsvectorlayer.h"
75
76#include "qgslinematerial_p.h"
77#include "qgs3dsceneexporter.h"
79#include "qgsmessageoutput.h"
80#include "qgsframegraph.h"
82
83#include "qgsskyboxentity.h"
84#include "qgsskyboxsettings.h"
85
86#include "qgswindow3dengine.h"
87#include "qgspointcloudlayer.h"
88#include "qgsshadowrenderview.h"
90
91std::function<QMap<QString, Qgs3DMapScene *>()> Qgs3DMapScene::sOpenScenesFunction = [] { return QMap<QString, Qgs3DMapScene *>(); };
92
94 : mMap( map )
95 , mEngine( engine )
96{
97 connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
98 onBackgroundColorChanged();
99
100 // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
101 // even if there's no change. Switching to "on demand" should only re-render when something has changed
102 // and we save quite a lot of resources
103 mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
104
105 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
106
107 // Get the maximum of clip planes available
108 mMaxClipPlanes = Qgs3DUtils::openGlMaxClipPlanes( mEngine->surface() );
109
110 // Camera
111 float aspectRatio = ( float ) viewportRect.width() / viewportRect.height();
112 mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
113
114 mFrameAction = new Qt3DLogic::QFrameAction();
115 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qgs3DMapScene::onFrameTriggered );
116 addComponent( mFrameAction ); // takes ownership
117
118 // Camera controlling
119 mCameraController = new QgsCameraController( this ); // attaches to the scene
120
121 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
122 mCameraController->resetGlobe( 10'000'000 );
123 else
124 mCameraController->resetView( 1000 );
125
126 addCameraViewCenterEntity( mEngine->camera() );
127 addCameraRotationCenterEntity( mCameraController );
128 updateLights();
129
130 // create terrain entity
131
132 createTerrainDeferred();
133 connect( &map, &Qgs3DMapSettings::extentChanged, this, &Qgs3DMapScene::createTerrain );
134 connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
135
136 connect( &map, &Qgs3DMapSettings::terrainSettingsChanged, this, &Qgs3DMapScene::createTerrain );
137
138 connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
139 connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
140 connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
141 connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
142 connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
143 connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
144 connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
145 connect( &map, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapScene::onAmbientOcclusionSettingsChanged );
146 connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
147 connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
148 connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
149 connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
150 connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
152 connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
153 connect( &map, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapScene::onCameraNavigationModeChanged );
154 connect( &map, &Qgs3DMapSettings::debugOverlayEnabledChanged, this, &Qgs3DMapScene::onDebugOverlayEnabledChanged );
155 connect( &map, &Qgs3DMapSettings::stopUpdatesChanged, this, &Qgs3DMapScene::onStopUpdatesChanged );
156
157 connect( &map, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapScene::on3DAxisSettingsChanged );
158
159 connect( &map, &Qgs3DMapSettings::originChanged, this, &Qgs3DMapScene::onOriginChanged );
160
161 connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [=]( const QString &url ) {
162 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
163 for ( QgsMapLayer *layer : modelVectorLayers )
164 {
165 QgsAbstract3DRenderer *renderer = layer->renderer3D();
166 if ( renderer )
167 {
168 if ( renderer->type() == QLatin1String( "vector" ) )
169 {
170 const QgsPoint3DSymbol *pointSymbol = static_cast<const QgsPoint3DSymbol *>( static_cast<QgsVectorLayer3DRenderer *>( renderer )->symbol() );
171 if ( pointSymbol->shapeProperty( QStringLiteral( "model" ) ).toString() == url )
172 {
173 removeLayerEntity( layer );
174 addLayerEntity( layer );
175 }
176 }
177 else if ( renderer->type() == QLatin1String( "rulebased" ) )
178 {
179 const QgsRuleBased3DRenderer::RuleList rules = static_cast<QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
180 for ( auto rule : rules )
181 {
182 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( rule->symbol() );
183 if ( pointSymbol->shapeProperty( QStringLiteral( "model" ) ).toString() == url )
184 {
185 removeLayerEntity( layer );
186 addLayerEntity( layer );
187 break;
188 }
189 }
190 }
191 }
192 }
193 } );
194
195 // listen to changes of layers in order to add/remove 3D renderer entities
196 connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
197
198 connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
199 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, this, &Qgs3DMapScene::onCameraChanged );
200
201 onSkyboxSettingsChanged();
202
203 // force initial update of chunked entities
204 onCameraChanged();
205 // force initial update of eye dome shading
206 onEyeDomeShadingSettingsChanged();
207 // force initial update of debugging setting of preview quads
208 onDebugShadowMapSettingsChanged();
209 onDebugDepthMapSettingsChanged();
210 // force initial update of ambient occlusion settings
211 onAmbientOcclusionSettingsChanged();
212
213 onCameraMovementSpeedChanged();
214
215 on3DAxisSettingsChanged();
216}
217
219{
220 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
221 {
222 mCameraController->resetGlobe( 10'000'000 );
223 return;
224 }
225
226 const QgsDoubleRange zRange = elevationRange();
227 const QgsRectangle extent = sceneExtent();
228 const double side = std::max( extent.width(), extent.height() );
229 double d = side / 2 / std::tan( cameraController()->camera()->fieldOfView() / 2 * M_PI / 180 );
230 d += zRange.isInfinite() ? 0. : zRange.upper();
231 mCameraController->resetView( static_cast<float>( d ) );
232}
233
235{
236 QgsPointXY center = extent.center();
237 QgsVector3D centerWorld = mMap.mapToWorldCoordinates( QVector3D( center.x(), center.y(), 0 ) );
238 QgsVector3D p1 = mMap.mapToWorldCoordinates( QVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
239 QgsVector3D p2 = mMap.mapToWorldCoordinates( QVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
240
241 float xSide = std::abs( p1.x() - p2.x() );
242 float ySide = std::abs( p1.z() - p2.z() );
243 if ( xSide < ySide )
244 {
245 float fov = 2 * std::atan( std::tan( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2 ) * cameraController()->camera()->aspectRatio() );
246 float r = xSide / 2.0f / std::tan( fov / 2.0f );
247 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
248 }
249 else
250 {
251 float fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
252 float r = ySide / 2.0f / std::tan( fov / 2.0f );
253 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
254 }
255}
256
257QVector<QgsPointXY> Qgs3DMapScene::viewFrustum2DExtent() const
258{
259 Qt3DRender::QCamera *camera = mCameraController->camera();
260 QVector<QgsPointXY> extent;
261 QVector<int> pointsOrder = { 0, 1, 3, 2 };
262 for ( int i : pointsOrder )
263 {
264 const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : mEngine->size().width(), ( ( i >> 1 ) & 1 ) ? 0 : mEngine->size().height() );
265 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, mEngine->size(), camera );
266 QVector3D dir = ray.direction();
267 if ( dir.z() == 0.0 )
268 dir.setZ( 0.000001 );
269 double t = -ray.origin().z() / dir.z();
270 if ( t < 0 )
271 {
272 // If the projected point is on the back of the camera we choose the farthest point in the front
273 t = camera->farPlane();
274 }
275 else
276 {
277 // If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
278 t = std::min<float>( t, camera->farPlane() );
279 }
280 QVector3D planePoint = ray.origin() + t * dir;
281 QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
282 extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
283 }
284 return extent;
285}
286
288{
289 int count = 0;
290 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
291 count += entity->pendingJobsCount();
292 return count;
293}
294
295float Qgs3DMapScene::worldSpaceError( float epsilon, float distance ) const
296{
297 Qt3DRender::QCamera *camera = mCameraController->camera();
298 float fov = camera->fieldOfView();
299 const QSize size = mEngine->size();
300 float screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
301
302 // see Qgs3DUtils::screenSpaceError() for the inverse calculation (world space error to screen space error)
303 // with explanation of the math.
304 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
305 float err = frustumWidthAtDistance * epsilon / screenSizePx;
306 return err;
307}
308
309void Qgs3DMapScene::onCameraChanged()
310{
311 if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
312 {
313 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
314 const float viewWidthFromCenter = mCameraController->distance();
315 const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
316 mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
317 }
318
319 updateScene( true );
320 updateCameraNearFarPlanes();
321
322 onShadowSettingsChanged();
323
324 QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
325 emit viewed2DExtentFrom3DChanged( extent2D );
326
327 // The magic to make things work better in large scenes (e.g. more than 50km across)
328 // is here: we will simply move the origin of the scene, and update transforms
329 // of the camera and all other entities. That should ensure we will not need to deal
330 // with large coordinates in 32-bit floats (and if we do have large coordinates,
331 // because the scene is far from the camera, we don't care, because those errors
332 // end up being tiny when viewed from far away).
333 constexpr float ORIGIN_SHIFT_THRESHOLD = 10'000;
334 if ( mSceneOriginShiftEnabled && mEngine->camera()->position().length() > ORIGIN_SHIFT_THRESHOLD )
335 {
336 const QgsVector3D newOrigin = mMap.origin() + QgsVector3D( mEngine->camera()->position() );
337 QgsDebugMsgLevel( QStringLiteral( "Rebasing scene origin from %1 to %2" ).arg( mMap.origin().toString( 1 ), newOrigin.toString( 1 ) ), 2 );
338 mMap.setOrigin( newOrigin );
339 }
340}
341
342void Qgs3DMapScene::updateScene( bool forceUpdate )
343{
344 if ( !mSceneUpdatesEnabled )
345 {
346 QgsDebugMsgLevel( "Scene update skipped", 2 );
347 return;
348 }
349
350 QgsEventTracing::ScopedEvent traceEvent( QStringLiteral( "3D" ), forceUpdate ? QStringLiteral( "Force update scene" ) : QStringLiteral( "Update scene" ) );
351
352 Qgs3DMapSceneEntity::SceneContext sceneContext;
353 Qt3DRender::QCamera *camera = mEngine->camera();
354 sceneContext.cameraFov = camera->fieldOfView();
355 sceneContext.cameraPos = camera->position();
356 const QSize size = mEngine->size();
357 sceneContext.screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
358
359 // Make our own projection matrix so that frustum culling done by the
360 // entities isn't dependent on the current near/far planes, which would then
361 // require multiple steps to stabilize.
362 // The matrix is constructed just like in QMatrix4x4::perspective(), but for
363 // all elements involving the near and far plane, the limit of the expression
364 // with the far plane going to infinity is taken.
365 float fovRadians = ( camera->fieldOfView() / 2.0f ) * static_cast<float>( M_PI ) / 180.0f;
366 float fovCotan = std::cos( fovRadians ) / std::sin( fovRadians );
367 QMatrix4x4 projMatrix(
368 fovCotan / camera->aspectRatio(), 0, 0, 0,
369 0, fovCotan, 0, 0,
370 0, 0, -1, -2,
371 0, 0, -1, 0
372 );
373 sceneContext.viewProjectionMatrix = projMatrix * camera->viewMatrix();
374
375
376 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
377 {
378 if ( forceUpdate || ( entity->isEnabled() && entity->needsUpdate() ) )
379 {
380 entity->handleSceneUpdate( sceneContext );
381 if ( entity->hasReachedGpuMemoryLimit() )
383 }
384 }
385
386 updateSceneState();
387}
388
389bool Qgs3DMapScene::updateCameraNearFarPlanes()
390{
391 // Update near and far plane from the terrain.
392 // this needs to be done with great care as we have kind of circular dependency here:
393 // active nodes are culled based on the current frustum (which involves near + far plane)
394 // and then based on active nodes we set near and far plane.
395 //
396 // All of this is just heuristics assuming that all other stuff is being rendered somewhere
397 // around the area where the terrain is.
398 //
399 // Near/far plane is setup in order to make best use of the depth buffer to avoid:
400 // 1. precision errors - if the range is too great
401 // 2. unwanted clipping of scene - if the range is too small
402
403 Qt3DRender::QCamera *camera = cameraController()->camera();
404 QMatrix4x4 viewMatrix = camera->viewMatrix();
405 float fnear = 1e9;
406 float ffar = 0;
407
408 // Iterate all scene entities to make sure that they will not get
409 // clipped by the near or far plane
410 for ( Qgs3DMapSceneEntity *se : std::as_const( mSceneEntities ) )
411 {
412 const QgsRange<float> depthRange = se->getNearFarPlaneRange( viewMatrix );
413
414 fnear = std::min( fnear, depthRange.lower() );
415 ffar = std::max( ffar, depthRange.upper() );
416 }
417
418 if ( fnear < 1 )
419 fnear = 1; // does not really make sense to use negative far plane (behind camera)
420
421 // the update didn't work out... this can happen if the scene does not contain
422 // any Qgs3DMapSceneEntity. Use the scene extent to compute near and far planes
423 // as a fallback.
424 if ( fnear == 1e9 && ffar == 0 )
425 {
426 QgsDoubleRange sceneZRange = elevationRange();
427 sceneZRange = sceneZRange.isInfinite() ? QgsDoubleRange( 0.0, 0.0 ) : sceneZRange;
428 const QgsAABB sceneBbox = Qgs3DUtils::mapToWorldExtent( mMap.extent(), sceneZRange.lower(), sceneZRange.upper(), mMap.origin() );
429 Qgs3DUtils::computeBoundingBoxNearFarPlanes( sceneBbox, viewMatrix, fnear, ffar );
430 }
431
432 // when zooming in a lot, fnear can become smaller than ffar. This should not happen
433 if ( fnear > ffar )
434 std::swap( fnear, ffar );
435
436 // set near/far plane - with some tolerance in front/behind expected near/far planes
437 float newFar = ffar * 2;
438 float newNear = fnear / 2;
439 if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
440 {
441 camera->setFarPlane( newFar );
442 camera->setNearPlane( newNear );
443 return true;
444 }
445
446 return false;
447}
448
449void Qgs3DMapScene::onFrameTriggered( float dt )
450{
451 QgsEventTracing::addEvent( QgsEventTracing::EventType::Instant, QStringLiteral( "3D" ), QStringLiteral( "Frame begins" ) );
452
453 mCameraController->frameTriggered( dt );
454
455 updateScene();
456
457 // lock changing the FPS counter to 5 fps
458 static int frameCount = 0;
459 static float accumulatedTime = 0.0f;
460
461 if ( !mMap.isFpsCounterEnabled() )
462 {
463 frameCount = 0;
464 accumulatedTime = 0;
465 return;
466 }
467
468 frameCount++;
469 accumulatedTime += dt;
470 if ( accumulatedTime >= 0.2f )
471 {
472 float fps = ( float ) frameCount / accumulatedTime;
473 frameCount = 0;
474 accumulatedTime = 0.0f;
475 emit fpsCountChanged( fps );
476 }
477}
478
479void Qgs3DMapScene::createTerrain()
480{
481 if ( mTerrain )
482 {
483 mSceneEntities.removeOne( mTerrain );
484
485 delete mTerrain;
486 mTerrain = nullptr;
487 }
488
489 if ( mGlobe )
490 {
491 mSceneEntities.removeOne( mGlobe );
492
493 delete mGlobe;
494 mGlobe = nullptr;
495 }
496
497 if ( !mTerrainUpdateScheduled )
498 {
499 // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
500 QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
501 mTerrainUpdateScheduled = true;
502 setSceneState( Updating );
503 }
504 else
505 {
507 }
508}
509
510void Qgs3DMapScene::createTerrainDeferred()
511{
512 QgsChunkedEntity *terrainOrGlobe = nullptr;
513
515 {
516 mGlobe = new QgsGlobeEntity( &mMap );
517 terrainOrGlobe = mGlobe;
518 }
519 else if ( mMap.sceneMode() == Qgis::SceneMode::Local && mMap.terrainRenderingEnabled() && mMap.terrainGenerator() )
520 {
521 double tile0width = mMap.terrainGenerator()->rootChunkExtent().width();
522 int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.terrainSettings()->mapTileResolution(), mMap.terrainSettings()->maximumGroundError() );
523 const QgsBox3D rootBox3D = mMap.terrainGenerator()->rootChunkBox3D( mMap );
524 float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
525 const QgsBox3D clippingBox3D( mMap.extent(), rootBox3D.zMinimum(), rootBox3D.zMaximum() );
526 mMap.terrainGenerator()->setupQuadtree( rootBox3D, rootError, maxZoomLevel, clippingBox3D );
527
528 mTerrain = new QgsTerrainEntity( &mMap );
529 terrainOrGlobe = mTerrain;
530 }
531
532 if ( terrainOrGlobe )
533 {
534 terrainOrGlobe->setParent( this );
535 terrainOrGlobe->setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
536
537 mSceneEntities << terrainOrGlobe;
538
539 connect( terrainOrGlobe, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
540 connect( terrainOrGlobe, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity *entity ) {
541 // let's make sure that any entity we're about to show has the right scene origin set
542 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
543 for ( QgsGeoTransform *transform : transforms )
544 {
545 transform->setOrigin( mMap.origin() );
546 }
547
548 // enable clipping on the terrain if necessary
549 handleClippingOnEntity( entity );
550 } );
551 }
552
553 // make sure that renderers for layers are re-created as well
554 const QList<QgsMapLayer *> layers = mMap.layers();
555 for ( QgsMapLayer *layer : layers )
556 {
557 // remove old entity - if any
558 removeLayerEntity( layer );
559
560 // add new entity - if any 3D renderer
561 addLayerEntity( layer );
562 }
563
565 onCameraChanged(); // force update of the new terrain
566 mTerrainUpdateScheduled = false;
567}
568
569void Qgs3DMapScene::onBackgroundColorChanged()
570{
571 mEngine->setClearColor( mMap.backgroundColor() );
572}
573
574void Qgs3DMapScene::updateLights()
575{
576 for ( Qt3DCore::QEntity *entity : std::as_const( mLightEntities ) )
577 entity->deleteLater();
578 mLightEntities.clear();
579
580 const QList<QgsLightSource *> newLights = mMap.lightSources();
581 for ( const QgsLightSource *source : newLights )
582 {
583 mLightEntities.append( source->createEntity( mMap, this ) );
584 }
585
586 onShadowSettingsChanged();
587}
588
589void Qgs3DMapScene::updateCameraLens()
590{
591 mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
592 mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
593 onCameraChanged();
594}
595
596void Qgs3DMapScene::onLayerRenderer3DChanged()
597{
598 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
599 Q_ASSERT( layer );
600
601 // remove old entity - if any
602 removeLayerEntity( layer );
603
604 // add new entity - if any 3D renderer
605 addLayerEntity( layer );
606}
607
608void Qgs3DMapScene::onLayersChanged()
609{
610 QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
611 QList<QgsMapLayer *> layersAdded;
612 const QList<QgsMapLayer *> layers = mMap.layers();
613 for ( QgsMapLayer *layer : layers )
614 {
615 if ( !layersBefore.contains( layer ) )
616 {
617 layersAdded << layer;
618 }
619 else
620 {
621 layersBefore.remove( layer );
622 }
623 }
624
625 // what is left in layersBefore are layers that have been removed
626 for ( QgsMapLayer *layer : std::as_const( layersBefore ) )
627 {
628 removeLayerEntity( layer );
629 }
630
631 for ( QgsMapLayer *layer : std::as_const( layersAdded ) )
632 {
633 addLayerEntity( layer );
634 }
635}
636
638{
639 const QList<QgsMapLayer *> layers = mLayerEntities.keys();
640 for ( QgsMapLayer *layer : layers )
641 {
642 if ( QgsMapLayerTemporalProperties *temporalProperties = layer->temporalProperties() )
643 {
644 if ( temporalProperties->isActive() )
645 {
646 removeLayerEntity( layer );
647 addLayerEntity( layer );
648 }
649 }
650 }
651}
652
653void Qgs3DMapScene::addSceneEntity( Qgs3DMapSceneEntity *sceneNewEntity )
654{
655 Q_ASSERT( sceneNewEntity );
656
657 mSceneEntities.append( sceneNewEntity );
658
659 sceneNewEntity->setParent( this );
660
661 finalizeNewEntity( sceneNewEntity );
662
663 connect( sceneNewEntity, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity *entity ) {
664 finalizeNewEntity( entity );
665 // this ensures to update the near/far planes with the exact bounding box of the new entity.
666 updateCameraNearFarPlanes();
667 } );
668
669 connect( sceneNewEntity, &Qgs3DMapSceneEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
670
671 onCameraChanged(); // needed for chunked entities
672}
673
674void Qgs3DMapScene::removeSceneEntity( Qgs3DMapSceneEntity *sceneEntity )
675{
676 Q_ASSERT( sceneEntity );
677
678 mSceneEntities.removeOne( sceneEntity );
679
680 sceneEntity->deleteLater();
681}
682
683
684void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
685{
686 QgsAbstract3DRenderer *renderer = layer->renderer3D();
687 if ( renderer )
688 {
689 // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
690 // It has happened before that renderer pointed to a different layer (probably after copying a style).
691 // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
692 // the vector layer 3D renderer classes are not available.
693 if ( layer->type() == Qgis::LayerType::Vector && ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
694 {
695 static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
696 if ( renderer->type() == QLatin1String( "vector" ) )
697 {
698 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
699 if ( vlayer->geometryType() == Qgis::GeometryType::Point )
700 {
701 const QgsPoint3DSymbol *pointSymbol = static_cast<const QgsPoint3DSymbol *>( static_cast<QgsVectorLayer3DRenderer *>( renderer )->symbol() );
702 if ( pointSymbol->shape() == Qgis::Point3DShape::Model )
703 {
704 mModelVectorLayers.append( layer );
705 }
706 }
707 }
708 else if ( renderer->type() == QLatin1String( "rulebased" ) )
709 {
710 const QgsRuleBased3DRenderer::RuleList rules = static_cast<QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
711 for ( auto rule : rules )
712 {
713 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( rule->symbol() );
714 if ( pointSymbol && pointSymbol->shape() == Qgis::Point3DShape::Model )
715 {
716 mModelVectorLayers.append( layer );
717 break;
718 }
719 }
720 }
721 }
722 else if ( layer->type() == Qgis::LayerType::Mesh && renderer->type() == QLatin1String( "mesh" ) )
723 {
724 QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
725 meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
726
727 // Before entity creation, set the maximum texture size
728 // Not very clean, but for now, only place found in the workflow to do that simple
729 QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
730 sym->setMaximumTextureSize( maximumTextureSize() );
731 meshRenderer->setSymbol( sym );
732 }
733 else if ( layer->type() == Qgis::LayerType::PointCloud && renderer->type() == QLatin1String( "pointcloud" ) )
734 {
735 QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
736 pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
737 }
738 else if ( layer->type() == Qgis::LayerType::TiledScene && renderer->type() == QLatin1String( "tiledscene" ) )
739 {
740 QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
741 tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
742 }
743
744 Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
745 if ( newEntity )
746 {
747 mLayerEntities.insert( layer, newEntity );
748
749 if ( Qgs3DMapSceneEntity *sceneNewEntity = qobject_cast<Qgs3DMapSceneEntity *>( newEntity ) )
750 {
751 // also sets this scene as the entity's parent and finalizes it
752 addSceneEntity( sceneNewEntity );
753 }
754 else
755 {
756 newEntity->setParent( this );
757 finalizeNewEntity( newEntity );
758 }
759 }
760 }
761
762 connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
763
764 if ( layer->type() == Qgis::LayerType::Vector )
765 {
766 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
767 connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
768 connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
769 }
770
771 if ( layer->type() == Qgis::LayerType::Mesh )
772 {
773 connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
774 }
775
776 if ( layer->type() == Qgis::LayerType::PointCloud )
777 {
778 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
779 connect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
780 connect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
781 }
782}
783
784void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
785{
786 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
787
788 if ( Qgs3DMapSceneEntity *sceneEntity = qobject_cast<Qgs3DMapSceneEntity *>( entity ) )
789 {
790 // also schedules the entity for deletion
791 removeSceneEntity( sceneEntity );
792 }
793 else
794 {
795 if ( entity )
796 entity->deleteLater();
797 }
798
799 disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
800
801 if ( layer->type() == Qgis::LayerType::Vector )
802 {
803 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
804 disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
805 disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
806 mModelVectorLayers.removeAll( layer );
807 }
808
809 if ( layer->type() == Qgis::LayerType::Mesh )
810 {
811 disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
812 }
813
814 if ( layer->type() == Qgis::LayerType::PointCloud )
815 {
816 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
817 disconnect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
818 disconnect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
819 disconnect( pclayer, &QgsPointCloudLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
820 }
821}
822
823void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
824{
825 // let's make sure that any entity we're about to show has the right scene origin set
826 const QList<QgsGeoTransform *> transforms = newEntity->findChildren<QgsGeoTransform *>();
827 for ( QgsGeoTransform *transform : transforms )
828 {
829 transform->setOrigin( mMap.origin() );
830 }
831
832 // set clip planes on the new entity if necessary
833 handleClippingOnEntity( newEntity );
834
835 // this is probably not the best place for material-specific configuration,
836 // maybe this could be more generalized when other materials need some specific treatment
837 const QList<QgsLineMaterial *> childLineMaterials = newEntity->findChildren<QgsLineMaterial *>();
838 for ( QgsLineMaterial *lm : childLineMaterials )
839 {
840 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, lm, [lm, this] {
841 lm->setViewportSize( mEngine->size() );
842 } );
843
844 lm->setViewportSize( mEngine->size() );
845 }
846 // configure billboard's viewport when the viewport is changed.
847 const QList<QgsPoint3DBillboardMaterial *> childBillboardMaterials = newEntity->findChildren<QgsPoint3DBillboardMaterial *>();
848 for ( QgsPoint3DBillboardMaterial *bm : childBillboardMaterials )
849 {
850 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, bm, [bm, this] {
851 bm->setViewportSize( mEngine->size() );
852 } );
853
854 bm->setViewportSize( mEngine->size() );
855 }
856
857 // Finalize adding the 3D transparent objects by adding the layer components to the entities
858 QgsFrameGraph *frameGraph = mEngine->frameGraph();
859 Qt3DRender::QLayer *transparentLayer = frameGraph->forwardRenderView().transparentObjectLayer();
860 const QList<Qt3DRender::QMaterial *> childMaterials = newEntity->findChildren<Qt3DRender::QMaterial *>();
861 for ( Qt3DRender::QMaterial *material : childMaterials )
862 {
863 // This handles the phong material without data defined properties.
864 if ( Qt3DExtras::QDiffuseSpecularMaterial *ph = qobject_cast<Qt3DExtras::QDiffuseSpecularMaterial *>( material ) )
865 {
866 if ( ph->diffuse().value<QColor>().alphaF() != 1.0f )
867 {
868 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( ph->parent() );
869 if ( entity && !entity->components().contains( transparentLayer ) )
870 {
871 entity->addComponent( transparentLayer );
872 }
873 }
874 }
875 else
876 {
877 // This handles the phong material with data defined properties, the textured case and point (instanced) symbols.
878 Qt3DRender::QEffect *effect = material->effect();
879 if ( effect )
880 {
881 const QVector<Qt3DRender::QParameter *> parameters = effect->parameters();
882 for ( const Qt3DRender::QParameter *parameter : parameters )
883 {
884 if ( parameter->name() == "opacity" && parameter->value() != 1.0f )
885 {
886 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( material->parent() );
887 if ( entity && !entity->components().contains( transparentLayer ) )
888 {
889 entity->addComponent( transparentLayer );
890 }
891 break;
892 }
893 }
894 }
895 }
896 }
897}
898
899int Qgs3DMapScene::maximumTextureSize() const
900{
901 QSurface *surface = mEngine->surface();
902 QOpenGLContext context;
903 context.create();
904 bool success = context.makeCurrent( surface );
905
906 if ( success )
907 {
908 QOpenGLFunctions openglFunctions = QOpenGLFunctions( &context );
909
910 GLint size;
911 openglFunctions.initializeOpenGLFunctions();
912 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
913 return int( size );
914 }
915 else
916 {
917 return 4096; //we can't have a context to defined the max texture size, we use this reasonable value
918 }
919}
920
921void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
922{
923 mEntityCameraViewCenter = new Qt3DCore::QEntity;
924
925 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
926 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
927 connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera] {
928 trCameraViewCenter->setTranslation( camera->viewCenter() );
929 } );
930
931 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
932 materialCameraViewCenter->setAmbient( Qt::red );
933 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
934
935 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
936 rendererCameraViewCenter->setRadius( 10 );
937 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
938
939 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
940 mEntityCameraViewCenter->setParent( this );
941
942 connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this] {
943 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
944 } );
945}
946
947void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
948{
949 if ( mSceneState == state )
950 return;
951 mSceneState = state;
952 emit sceneStateChanged();
953}
954
955void Qgs3DMapScene::updateSceneState()
956{
957 if ( mTerrainUpdateScheduled )
958 {
959 setSceneState( Updating );
960 return;
961 }
962
963 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
964 {
965 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
966 {
967 setSceneState( Updating );
968 return;
969 }
970 }
971
972 setSceneState( Ready );
973}
974
975void Qgs3DMapScene::onSkyboxSettingsChanged()
976{
977 QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
978 if ( mSkybox )
979 {
980 mSkybox->deleteLater();
981 mSkybox = nullptr;
982 }
983
984 mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
985
986 if ( mMap.isSkyboxEnabled() )
987 {
988 QMap<QString, QString> faces;
989 switch ( skyboxSettings.skyboxType() )
990 {
992 faces = skyboxSettings.cubeMapFacesPaths();
993 mSkybox = new QgsCubeFacesSkyboxEntity(
994 faces[QStringLiteral( "posX" )], faces[QStringLiteral( "posY" )], faces[QStringLiteral( "posZ" )],
995 faces[QStringLiteral( "negX" )], faces[QStringLiteral( "negY" )], faces[QStringLiteral( "negZ" )],
996 this
997 );
998 break;
1000 mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
1001 break;
1002 }
1003 }
1004}
1005
1006void Qgs3DMapScene::onShadowSettingsChanged()
1007{
1008 QgsFrameGraph *frameGraph = mEngine->frameGraph();
1009 frameGraph->updateShadowSettings( mMap.shadowSettings(), mMap.lightSources() );
1010}
1011
1012void Qgs3DMapScene::onAmbientOcclusionSettingsChanged()
1013{
1014 QgsFrameGraph *frameGraph = mEngine->frameGraph();
1015 QgsAmbientOcclusionSettings ambientOcclusionSettings = mMap.ambientOcclusionSettings();
1016 frameGraph->setAmbientOcclusionEnabled( ambientOcclusionSettings.isEnabled() );
1017 frameGraph->setAmbientOcclusionRadius( ambientOcclusionSettings.radius() );
1018 frameGraph->setAmbientOcclusionIntensity( ambientOcclusionSettings.intensity() );
1019 frameGraph->setAmbientOcclusionThreshold( ambientOcclusionSettings.threshold() );
1020}
1021
1022void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
1023{
1024 mEngine->frameGraph()->updateDebugShadowMapSettings( mMap );
1025}
1026
1027void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1028{
1029 mEngine->frameGraph()->updateDebugDepthMapSettings( mMap );
1030}
1031
1032void Qgs3DMapScene::onDebugOverlayEnabledChanged()
1033{
1035 mEngine->renderSettings()->setRenderPolicy( mMap.isDebugOverlayEnabled() ? Qt3DRender::QRenderSettings::Always : Qt3DRender::QRenderSettings::OnDemand );
1036}
1037
1038void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1039{
1040 bool edlEnabled = mMap.eyeDomeLightingEnabled();
1041 double edlStrength = mMap.eyeDomeLightingStrength();
1042 double edlDistance = mMap.eyeDomeLightingDistance();
1043 mEngine->frameGraph()->setupEyeDomeLighting( edlEnabled, edlStrength, edlDistance );
1044}
1045
1046void Qgs3DMapScene::onCameraMovementSpeedChanged()
1047{
1048 mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1049}
1050
1051void Qgs3DMapScene::onCameraNavigationModeChanged()
1052{
1053 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
1054}
1055
1057{
1058 QVector<QString> notParsedLayers;
1059 Qgs3DSceneExporter exporter;
1060
1061 exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1062 exporter.setSmoothEdges( exportSettings.smoothEdges() );
1063 exporter.setExportNormals( exportSettings.exportNormals() );
1064 exporter.setExportTextures( exportSettings.exportTextures() );
1065 exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1066 exporter.setScale( exportSettings.scale() );
1067
1068 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1069 {
1070 QgsMapLayer *layer = it.key();
1071 Qt3DCore::QEntity *rootEntity = it.value();
1072 Qgis::LayerType layerType = layer->type();
1073 switch ( layerType )
1074 {
1076 if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1077 notParsedLayers.push_back( layer->name() );
1078 break;
1087 notParsedLayers.push_back( layer->name() );
1088 break;
1089 }
1090 }
1091
1092 if ( mTerrain )
1093 exporter.parseTerrain( mTerrain, "Terrain" );
1094
1095 const bool sceneSaved = exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1096 if ( !sceneSaved )
1097 {
1098 return false;
1099 }
1100
1101 if ( !notParsedLayers.empty() )
1102 {
1103 QString message = tr( "The following layers were not exported:" ) + "\n";
1104 for ( const QString &layerName : notParsedLayers )
1105 message += layerName + "\n";
1106 QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText );
1107 }
1108
1109 return true;
1110}
1111
1112QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1113{
1114 QVector<const QgsChunkNode *> chunks;
1115 if ( !mLayerEntities.contains( layer ) )
1116 return chunks;
1117 if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[layer] ) )
1118 {
1119 const QList<QgsChunkNode *> activeNodes = c->activeNodes();
1120 for ( QgsChunkNode *n : activeNodes )
1121 chunks.push_back( n );
1122 }
1123 return chunks;
1124}
1125
1127{
1128 return mMap.extent();
1129}
1130
1131QgsDoubleRange Qgs3DMapScene::elevationRange( const bool ignoreTerrain ) const
1132{
1133 double zMin = std::numeric_limits<double>::max();
1134 double zMax = std::numeric_limits<double>::lowest();
1135 if ( mMap.terrainRenderingEnabled() && mTerrain && !ignoreTerrain )
1136 {
1137 const QgsBox3D box3D = mTerrain->rootNode()->box3D();
1138 zMin = std::min( zMin, box3D.zMinimum() );
1139 zMax = std::max( zMax, box3D.zMaximum() );
1140 }
1141
1142 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); it++ )
1143 {
1144 QgsMapLayer *layer = it.key();
1145 switch ( layer->type() )
1146 {
1148 {
1149 QgsPointCloudLayer *pcl = qobject_cast<QgsPointCloudLayer *>( layer );
1150 QgsDoubleRange zRange = pcl->elevationProperties()->calculateZRange( pcl );
1151 zMin = std::min( zMin, zRange.lower() );
1152 zMax = std::max( zMax, zRange.upper() );
1153 break;
1154 }
1156 {
1157 QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( layer );
1158 QgsAbstract3DRenderer *renderer3D = meshLayer->renderer3D();
1159 if ( renderer3D )
1160 {
1161 QgsMeshLayer3DRenderer *meshLayerRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer3D );
1162 const int verticalGroupDatasetIndex = meshLayerRenderer->symbol()->verticalDatasetGroupIndex();
1163 const QgsMeshDatasetGroupMetadata verticalGroupMetadata = meshLayer->datasetGroupMetadata( verticalGroupDatasetIndex );
1164 const double verticalScale = meshLayerRenderer->symbol()->verticalScale();
1165 zMin = std::min( zMin, verticalGroupMetadata.minimum() * verticalScale );
1166 zMax = std::max( zMax, verticalGroupMetadata.maximum() * verticalScale );
1167 }
1168 break;
1169 }
1171 {
1172 QgsTiledSceneLayer *sceneLayer = qobject_cast<QgsTiledSceneLayer *>( layer );
1173 const QgsDoubleRange zRange = sceneLayer->elevationProperties()->calculateZRange( sceneLayer );
1174 if ( !zRange.isInfinite() && !zRange.isEmpty() )
1175 {
1176 zMin = std::min( zMin, zRange.lower() );
1177 zMax = std::max( zMax, zRange.upper() );
1178 }
1179 break;
1180 }
1187 break;
1188 }
1189 }
1190 const QgsDoubleRange zRange( std::min( zMin, std::numeric_limits<double>::max() ), std::max( zMax, std::numeric_limits<double>::lowest() ) );
1191 return zRange.isEmpty() ? QgsDoubleRange() : zRange;
1192}
1193
1194QMap<QString, Qgs3DMapScene *> Qgs3DMapScene::openScenes()
1195{
1196 return sOpenScenesFunction();
1197}
1198
1199void Qgs3DMapScene::addCameraRotationCenterEntity( QgsCameraController *controller )
1200{
1201 mEntityRotationCenter = new Qt3DCore::QEntity;
1202
1203 Qt3DCore::QTransform *trRotationCenter = new Qt3DCore::QTransform;
1204 mEntityRotationCenter->addComponent( trRotationCenter );
1205 Qt3DExtras::QPhongMaterial *materialRotationCenter = new Qt3DExtras::QPhongMaterial;
1206 materialRotationCenter->setAmbient( Qt::blue );
1207 mEntityRotationCenter->addComponent( materialRotationCenter );
1208 Qt3DExtras::QSphereMesh *rendererRotationCenter = new Qt3DExtras::QSphereMesh;
1209 rendererRotationCenter->setRadius( 10 );
1210 mEntityRotationCenter->addComponent( rendererRotationCenter );
1211 mEntityRotationCenter->setEnabled( false );
1212 mEntityRotationCenter->setParent( this );
1213
1214 connect( controller, &QgsCameraController::cameraRotationCenterChanged, this, [trRotationCenter]( QVector3D center ) {
1215 trRotationCenter->setTranslation( center );
1216 } );
1217
1218 connect( &mMap, &Qgs3DMapSettings::showCameraRotationCenterChanged, this, [this] {
1219 mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() );
1220 } );
1221}
1222
1223void Qgs3DMapScene::on3DAxisSettingsChanged()
1224{
1225 if ( m3DAxis )
1226 {
1227 m3DAxis->onAxisSettingsChanged();
1228 }
1229 else
1230 {
1231 if ( QgsWindow3DEngine *engine = dynamic_cast<QgsWindow3DEngine *>( mEngine ) )
1232 {
1233 m3DAxis = new Qgs3DAxis( static_cast<Qgs3DMapCanvas *>( engine->window() ), engine->root(), this, mCameraController, &mMap );
1234 }
1235 }
1236}
1237
1238void Qgs3DMapScene::onOriginChanged()
1239{
1240 const QList<QgsGeoTransform *> geoTransforms = findChildren<QgsGeoTransform *>();
1241 for ( QgsGeoTransform *transform : geoTransforms )
1242 {
1243 transform->setOrigin( mMap.origin() );
1244 }
1245
1246 const QList<QgsGeoTransform *> rubberBandGeoTransforms = mEngine->frameGraph()->rubberBandsRootEntity()->findChildren<QgsGeoTransform *>();
1247 for ( QgsGeoTransform *transform : rubberBandGeoTransforms )
1248 {
1249 transform->setOrigin( mMap.origin() );
1250 }
1251
1252 const QgsVector3D oldOrigin = mCameraController->origin();
1253 mCameraController->setOrigin( mMap.origin() );
1254
1255 if ( !mClipPlanesEquations.isEmpty() )
1256 {
1257 // how the math works - for a plane defined as (a,b,c,d), only "d" changes when
1258 // moving the origin - the plane normal vector (a,b,c) stays the same.
1259 // - line equation for old shift: a * (x - x0) + b * (y - y0) + c * (z - z0) + d0 = 0
1260 // - line equation for new shift: a * (x - x1) + b * (y - y1) + c * (z - z1) + d1 = 0
1261 // - we solve for d1:
1262 // d1 = a * (x1 - x0) + b * (y1 - y0) + c * (z1 - z0) + d0
1263
1264 QList<QVector4D> newPlanes;
1265 QgsVector3D originShift = mMap.origin() - oldOrigin;
1266 for ( QVector4D plane : std::as_const( mClipPlanesEquations ) )
1267 {
1268 plane.setW( originShift.x() * plane.x() + originShift.y() * plane.y() + originShift.z() * plane.z() + plane.w() );
1269 newPlanes.append( plane );
1270 }
1271 enableClipping( newPlanes );
1272 }
1273}
1274
1275void Qgs3DMapScene::handleClippingOnEntity( QEntity *entity ) const
1276{
1277 if ( mClipPlanesEquations.isEmpty() ) // no clip plane equations, disable clipping
1278 {
1279 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1280 {
1281 material->disableClipping();
1282 }
1283 }
1284 else // enable clipping
1285 {
1286 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1287 {
1288 material->enableClipping( mClipPlanesEquations );
1289 }
1290 }
1291
1292 // recursive call
1293 // enable or disable clipping on the children accordingly
1294 for ( QObject *child : entity->children() )
1295 {
1296 Qt3DCore::QEntity *childEntity = qobject_cast<Qt3DCore::QEntity *>( child );
1297 if ( childEntity )
1298 {
1299 handleClippingOnEntity( childEntity );
1300 }
1301 }
1302}
1303
1304void Qgs3DMapScene::handleClippingOnAllEntities() const
1305{
1306 // Need to loop mLayerEntities instead of mSceneEntities to handle entities
1307 // which do no inherit from Qgs3DMapSceneEntity. For example, mesh entities.
1308 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1309 {
1310 handleClippingOnEntity( it.value() );
1311 }
1312 if ( mTerrain )
1313 {
1314 handleClippingOnEntity( mTerrain );
1315 }
1316 if ( mGlobe )
1317 {
1318 handleClippingOnEntity( mGlobe );
1319 }
1320}
1321
1322void Qgs3DMapScene::enableClipping( const QList<QVector4D> &clipPlaneEquations )
1323{
1324 if ( clipPlaneEquations.size() > mMaxClipPlanes )
1325 {
1326 QgsDebugMsgLevel( QStringLiteral( "Qgs3DMapScene::enableClipping: it is not possible to use more than %1 clipping planes." ).arg( mMaxClipPlanes ), 2 );
1327 }
1328 mClipPlanesEquations = clipPlaneEquations.mid( 0, mMaxClipPlanes );
1329
1330 // enable the clip planes on the framegraph
1331 mEngine->frameGraph()->addClipPlanes( clipPlaneEquations.size() );
1332
1333 // Enable the clip planes for the material of each entity.
1334 handleClippingOnAllEntities();
1335}
1336
1338{
1339 mClipPlanesEquations.clear();
1340
1341 // disable the clip planes on the framegraph
1342 mEngine->frameGraph()->removeClipPlanes();
1343
1344 // Disable the clip planes for the material of each entity.
1345 handleClippingOnAllEntities();
1346}
1347
1348void Qgs3DMapScene::onStopUpdatesChanged()
1349{
1350 mSceneUpdatesEnabled = !mMap.stopUpdates();
1351}
LayerType
Types of layers that can be added to a map.
Definition qgis.h:169
@ 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.
@ Globe
Scene is represented as a globe using a geocentric CRS.
@ Local
Local scene based on a projected CRS.
Display 3D ortho axis in the main 3D view.
Definition qgs3daxis.h:52
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
Convenience wrapper to simplify the creation of a 3D window ready to be used with QGIS.
Manages the various settings the user can choose from when exporting a 3D scene.
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 viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
QList< QVector4D > clipPlaneEquations() const
Returns list of clipping planes if clipping is enabled, otherwise an empty list.
static std::function< QMap< QString, Qgs3DMapScene * >()> sOpenScenesFunction
Static function for returning open 3D map scenes.
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)
void disableClipping()
Disables OpenGL clipping.
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...
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.
bool exportScene(const Qgs3DMapExportSettings &exportSettings)
Exports the scene according to the scene export settings Returns false if the operation failed.
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.
QgsDoubleRange elevationRange(bool ignoreTerrain=false) const
Returns the scene's elevation range.
QgsRectangle sceneExtent() const
Returns the scene extent in the map's CRS.
void sceneStateChanged()
Emitted when the scene's state has changed.
void removeSceneEntity(Qgs3DMapSceneEntity *entity)
Removes a 3D scene entity for the scene.
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.
void enableClipping(const QList< QVector4D > &clipPlaneEquations)
Enables OpenGL clipping based on the planes equations defined in clipPlaneEquations.
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.
void addSceneEntity(Qgs3DMapSceneEntity *entity)
Adds a 3D map scene entity to the scene.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
Definition of the world.
void extentChanged()
Emitted when the 3d view's 2d extent has changed.
bool isDebugOverlayEnabled() const
Returns whether debug overlay is enabled.
void originChanged()
Emitted when the world's origin point has been shifted.
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 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.
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.
const QgsAbstractTerrainSettings * terrainSettings() const
Returns the terrain settings.
void cameraNavigationModeChanged()
Emitted when the camera navigation mode was changed.
void shadowSettingsChanged()
Emitted when shadow rendering settings are changed.
bool stopUpdates() const
Returns whether the scene updates on camera movement.
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.
Qgis::SceneMode sceneMode() const
Returns mode of the 3D scene - whether it is represented as a globe (when using Geocentric CRS such a...
void setOrigin(const QgsVector3D &origin)
Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
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.
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.
void stopUpdatesChanged()
Emitted when the flag whether to keep updating scene has changed.
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.
void terrainSettingsChanged()
Emitted when the terrain settings are changed.
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)
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.
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 scenes.
bool save(const QString &sceneName, const QString &sceneFolderPath, int precision=6)
Saves the scene to a .obj file Returns false if the operation failed.
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 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.
static int openGlMaxClipPlanes(QSurface *surface)
Gets the maximum number of clip planes that can be used.
Axis-aligned bounding box - in world coords.
Definition qgsaabb.h:35
Base class for 3D engine implementation.
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 participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
virtual Qt3DCore::QEntity * createEntity(Qgs3DMapSettings *map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
double maximumGroundError() const
Returns the maximum ground error of terrain tiles in world units.
int mapTileResolution() const
Returns the resolution (in pixels) of the texture of a terrain tile.
Base class for 3D renderers that are based on vector layers.
Contains the configuration of ambient occlusion rendering.
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 ...
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:259
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:252
Object that controls camera movement based on user input.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
const QgsVector3D origin() const
Returns the origin of the scene in map coordinates.
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 resetGlobe(float distance, double lat=0, double lon=0)
Resets view of the globe to look at a particular location given as latitude and longitude (in degrees...
void setOrigin(const QgsVector3D &origin)
Reacts to the shift of origin of the scene, updating camera pose and any other member variables so th...
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
A skybox constructed from 6 cube faces.
QgsRange which stores a range of double values.
Definition qgsrange.h:233
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:287
Qt3DRender::QLayer * transparentObjectLayer()
Returns a layer object used to indicate that the object is transparent.
Container class that holds different objects related to frame graphs of 3D scenes.
void updateShadowSettings(const QgsShadowSettings &shadowSettings, const QList< QgsLightSource * > &lightSources)
Updates shadow bias, light and texture size according to shadowSettings and lightSources.
void addClipPlanes(int nrClipPlanes)
Setups nrClipPlanes clip planes in the forward pass to enable OpenGL clipping.
void updateDebugShadowMapSettings(const Qgs3DMapSettings &settings)
Updates settings for shadows debug map.
void removeClipPlanes()
Disables OpenGL clipping.
void setAmbientOcclusionIntensity(float intensity)
Sets the ambient occlusion intensity.
Qt3DCore::QEntity * rubberBandsRootEntity()
Returns entity for all rubber bands (to show them always on top)
void setDebugOverlayEnabled(bool enabled)
Sets whether debug overlay is enabled.
void updateDebugDepthMapSettings(const Qgs3DMapSettings &settings)
Updates settings for depth debug map.
QgsForwardRenderView & forwardRenderView()
Returns forward renderview.
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.
Base class for light sources in 3d scenes.
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:77
QString name
Definition qgsmaplayer.h:81
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:87
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.
Base class for all materials used within QGIS 3D views.
Definition qgsmaterial.h:39
3D symbol that draws mesh geometry as planar triangles.
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.
A collection of dataset group metadata such as whether the data is vector or scalar,...
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.
Material of the billboard rendering for points in 3D map view.
void setViewportSize(const QSizeF size)
Set the size of the view port.
3D symbol that draws point geometries as 3D objects using one of the predefined shapes.
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.
Represents 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
double yMinimum
double xMaximum
double yMaximum
QgsPointXY center
Rule-based 3D renderer.
QList< QgsRuleBased3DRenderer::Rule * > RuleList
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 QgsBox3D rootChunkBox3D(const Qgs3DMapSettings &map) const
Returns 3D box (in map coordinates) of the root chunk.
virtual float rootChunkError(const Qgs3DMapSettings &map) const
Returns error of the root chunk in world coordinates.
virtual QgsRectangle rootChunkExtent() const =0
extent of the terrain's root chunk in terrain's CRS
3D renderer that renders content of a tiled scene layer.
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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:51
QString toString(int precision=17) const
Returns a string representation of the 3D vector.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:47
3D renderer that renders all features of a vector layer with the same 3D symbol.
Represents a vector layer which manages a vector based dataset.
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.
On-screen 3D engine: it creates an OpenGL window (QWindow) and displays rendered 3D scenes there.
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:6298
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41