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