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