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