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