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