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