QGIS API Documentation 3.27.0-Master (0a97e3138f)
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/QObjectPicker>
21#include <Qt3DRender/QPickEvent>
22#include <Qt3DRender/QPickingSettings>
23#include <Qt3DRender/QPickTriangleEvent>
24#include <Qt3DRender/QRenderSettings>
25#include <Qt3DRender/QSceneLoader>
26#include <Qt3DExtras/QForwardRenderer>
27#include <Qt3DExtras/QPhongMaterial>
28#include <Qt3DExtras/QPhongAlphaMaterial>
29#include <Qt3DExtras/QDiffuseSpecularMaterial>
30#include <Qt3DExtras/QSphereMesh>
31#include <Qt3DLogic/QFrameAction>
32#include <Qt3DRender/QEffect>
33#include <Qt3DRender/QTechnique>
34#include <Qt3DRender/QRenderPass>
35#include <Qt3DRender/QRenderState>
36#include <Qt3DRender/QCullFace>
37#include <Qt3DRender/QDepthTest>
38#include <QSurface>
39#include <QUrl>
40#include <QtMath>
41
42#include <QOpenGLContext>
43#include <QOpenGLFunctions>
44#include <QTimer>
45
46#include "qgs3daxis.h"
47#include "qgslogger.h"
48#include "qgsapplication.h"
49#include "qgsaabb.h"
50#include "qgsabstract3dengine.h"
52#include "qgs3dmapsettings.h"
53#include "qgs3dutils.h"
55#include "qgscameracontroller.h"
56#include "qgschunkedentity_p.h"
57#include "qgschunknode_p.h"
58#include "qgseventtracing.h"
59#include "qgsmeshlayer.h"
61#include "qgspoint3dsymbol.h"
63#include "qgspointcloudlayer.h"
65#include "qgssourcecache.h"
66#include "qgsterrainentity_p.h"
67#include "qgsterraingenerator.h"
69#include "qgsvectorlayer.h"
73
74#include "qgslinematerial_p.h"
75#include "qgs3dsceneexporter.h"
78#include "qgsmessageoutput.h"
79
80#include "qgsskyboxentity.h"
81#include "qgsskyboxsettings.h"
82
83#include "qgswindow3dengine.h"
85#include "qgspointcloudlayer.h"
87
89 : mMap( map )
90 , mEngine( engine )
91{
92
93 connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
94 onBackgroundColorChanged();
95
96 // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
97 // even if there's no change. Switching to "on demand" should only re-render when something has changed
98 // and we save quite a lot of resources
99 mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
100
101 // we want precise picking of terrain (also bounding volume picking does not seem to work - not sure why)
102 mEngine->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
103
104 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
105
106 // Camera
107 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
108 mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
109
110 mFrameAction = new Qt3DLogic::QFrameAction();
111 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
112 this, &Qgs3DMapScene::onFrameTriggered );
113 addComponent( mFrameAction ); // takes ownership
114
115 // Camera controlling
116 mCameraController = new QgsCameraController( this ); // attaches to the scene
117 mCameraController->setViewport( viewportRect );
118 mCameraController->setCamera( mEngine->camera() );
119 mCameraController->resetView( 1000 );
120
121 addCameraViewCenterEntity( mEngine->camera() );
122 addCameraRotationCenterEntity( mCameraController );
123 updateLights();
124
125 // create terrain entity
126
127 createTerrainDeferred();
128 connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
129 connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain );
130 connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
131 connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
132 connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
133 connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
134 connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
135 connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
136 connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
137 connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
138 connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged );
139 connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
140 connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
141 connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
142 connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
143 connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
144 connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
145 connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
147 connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
148 connect( &map, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapScene::onCameraNavigationModeChanged );
149 connect( &map, &Qgs3DMapSettings::debugOverlayEnabledChanged, this, &Qgs3DMapScene::onDebugOverlayEnabledChanged );
150
151 connect( &map, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapScene::on3DAxisSettingsChanged );
152
153 connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
154 {
155 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
156 for ( QgsMapLayer *layer : modelVectorLayers )
157 {
158 QgsAbstract3DRenderer *renderer = layer->renderer3D();
159 if ( renderer )
160 {
161 if ( renderer->type() == QLatin1String( "vector" ) )
162 {
163 const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
164 if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
165 {
166 removeLayerEntity( layer );
167 addLayerEntity( layer );
168 }
169 }
170 else if ( renderer->type() == QLatin1String( "rulebased" ) )
171 {
172 const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
173 for ( auto rule : rules )
174 {
175 const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
176 if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
177 {
178 removeLayerEntity( layer );
179 addLayerEntity( layer );
180 break;
181 }
182 }
183 }
184 }
185 }
186 } );
187
188 // create entities of renderers
189
190 onRenderersChanged();
191
192 // listen to changes of layers in order to add/remove 3D renderer entities
193 connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
194
195
196#if 0
197 ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory );
198 testChunkEntity->setEnabled( false );
199 testChunkEntity->setParent( this );
200 chunkEntities << testChunkEntity;
201#endif
202
203 connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
204 connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged );
205
206#if 0
207 // experiments with loading of existing 3D models.
208
209 // scene loader only gets loaded only when added to a scene...
210 // it loads everything: geometries, materials, transforms, lights, cameras (if any)
211 Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity;
212 Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader;
213 loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
214 loaderEntity->addComponent( loader );
215 loaderEntity->setParent( this );
216
217 // mesh loads just geometry as one geometry...
218 // so if there are different materials (e.g. colors) used in the model, this information is lost
219 Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity;
220 Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
221 mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
222 meshEntity->addComponent( mesh );
223 Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
224 material->setAmbient( Qt::red );
225 meshEntity->addComponent( material );
226 Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform;
227 meshTransform->setScale( 1 );
228 meshEntity->addComponent( meshTransform );
229 meshEntity->setParent( this );
230#endif
231 onSkyboxSettingsChanged();
232
233 // force initial update of chunked entities
234 onCameraChanged();
235 // force initial update of eye dome shading
236 onEyeDomeShadingSettingsChanged();
237 // force initial update of debugging setting of preview quads
238 onDebugShadowMapSettingsChanged();
239 onDebugDepthMapSettingsChanged();
240
241 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
242 onCameraMovementSpeedChanged();
243
244 on3DAxisSettingsChanged();
245}
246
248{
249 QgsRectangle extent = sceneExtent();
250 float side = std::max( extent.width(), extent.height() );
251 float a = side / 2.0f / std::sin( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2.0f );
252 // Note: the 1.5 multiplication is to move the view upwards to look better
253 mCameraController->resetView( 1.5 * std::sqrt( a * a - side * side ) ); // assuming FOV being 45 degrees
254}
255
257{
258 QgsPointXY center = extent.center();
259 QgsVector3D centerWorld = mMap.mapToWorldCoordinates( QVector3D( center.x(), center.y(), 0 ) );
260 QgsVector3D p1 = mMap.mapToWorldCoordinates( QVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
261 QgsVector3D p2 = mMap.mapToWorldCoordinates( QVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
262
263 float xSide = std::abs( p1.x() - p2.x() );
264 float ySide = std::abs( p1.z() - p2.z() );
265 if ( xSide < ySide )
266 {
267 float fov = 2 * std::atan( std::tan( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2 ) * cameraController()->camera()->aspectRatio() );
268 float r = xSide / 2.0f / std::tan( fov / 2.0f );
269 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
270 }
271 else
272 {
273 float fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
274 float r = ySide / 2.0f / std::tan( fov / 2.0f );
275 mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
276 }
277}
278
280{
281 Qt3DRender::QCamera *camera = mCameraController->camera();
282 const QRect viewport = mCameraController->viewport();
283 QVector<QgsPointXY> extent;
284 QVector<int> pointsOrder = { 0, 1, 3, 2 };
285 for ( int i : pointsOrder )
286 {
287 const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : viewport.width(), ( ( i >> 1 ) & 1 ) ? 0 : viewport.height() );
288 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, viewport.size(), camera );
289 QVector3D dir = ray.direction();
290 if ( dir.y() == 0.0 )
291 dir.setY( 0.000001 );
292 double t = - ray.origin().y() / dir.y();
293 if ( t < 0 )
294 {
295 // If the projected point is on the back of the camera we choose the farthest point in the front
296 t = camera->farPlane();
297 }
298 else
299 {
300 // If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
301 t = std::min<float>( t, camera->farPlane() );
302 }
303 QVector3D planePoint = ray.origin() + t * dir;
304 QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
305 extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
306 }
307 return extent;
308}
309
311{
312 return mTerrain ? mTerrain->pendingJobsCount() : 0;
313}
314
316{
317 int count = 0;
318 for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
319 count += entity->pendingJobsCount();
320 return count;
321}
322
324{
325 if ( mPickHandlers.isEmpty() )
326 {
327 // we need to add object pickers
328 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
329 {
330 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
331 chunkedEntity->setPickingEnabled( true );
332 }
333 }
334
335 mPickHandlers.append( pickHandler );
336}
337
339{
340 mPickHandlers.removeOne( pickHandler );
341
342 if ( mPickHandlers.isEmpty() )
343 {
344 // we need to remove pickers
345 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
346 {
347 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
348 chunkedEntity->setPickingEnabled( false );
349 }
350 }
351}
352
353void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid )
354{
355 QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
356 if ( !layer )
357 return;
358
359 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
360 if ( !vlayer )
361 return;
362
363 for ( Qgs3DMapScenePickHandler *pickHandler : std::as_const( mPickHandlers ) )
364 {
365 pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
366 }
367}
368
369float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
370{
371 Qt3DRender::QCamera *camera = mCameraController->camera();
372 float fov = camera->fieldOfView();
373 QRect rect = mCameraController->viewport();
374 float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
375
376 // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
377 // with explanation of the math.
378 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
379 float err = frustumWidthAtDistance * epsilon / screenSizePx;
380 return err;
381}
382
383QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
384{
385 Qt3DRender::QCamera *camera = cameraController->camera();
386 QgsChunkedEntity::SceneState state;
387 state.cameraFov = camera->fieldOfView();
388 state.cameraPos = camera->position();
389 QRect rect = cameraController->viewport();
390 state.screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct?
391 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
392 return state;
393}
394
395void Qgs3DMapScene::onCameraChanged()
396{
397 if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
398 {
399 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
400 const float viewWidthFromCenter = mCameraController->distance();
401 const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
402 mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
403 }
404
405 updateScene();
406 bool changedCameraPlanes = updateCameraNearFarPlanes();
407
408 if ( changedCameraPlanes )
409 {
410 // repeat update of entities - because we have updated camera's near/far planes,
411 // the active nodes may have changed as well
412 updateScene();
413 updateCameraNearFarPlanes();
414 }
415
416 onShadowSettingsChanged();
417
418 QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
419 emit viewed2DExtentFrom3DChanged( extent2D );
420}
421
422void removeQLayerComponentsFromHierarchy( Qt3DCore::QEntity *entity )
423{
424 QVector<Qt3DCore::QComponent *> toBeRemovedComponents;
425 for ( Qt3DCore::QComponent *component : entity->components() )
426 {
427 Qt3DRender::QLayer *layer = qobject_cast<Qt3DRender::QLayer *>( component );
428 if ( layer != nullptr )
429 toBeRemovedComponents.push_back( layer );
430 }
431 for ( Qt3DCore::QComponent *component : toBeRemovedComponents )
432 entity->removeComponent( component );
433 for ( Qt3DCore::QEntity *obj : entity->findChildren<Qt3DCore::QEntity *>() )
434 {
435 if ( obj != nullptr )
437 }
438}
439
440void addQLayerComponentsToHierarchy( Qt3DCore::QEntity *entity, const QVector<Qt3DRender::QLayer *> &layers )
441{
442 for ( Qt3DRender::QLayer *layer : layers )
443 entity->addComponent( layer );
444 for ( Qt3DCore::QEntity *child : entity->findChildren<Qt3DCore::QEntity *>() )
445 {
446 if ( child != nullptr )
447 addQLayerComponentsToHierarchy( child, layers );
448 }
449}
450
451void Qgs3DMapScene::updateScene()
452{
453 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );
454 for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
455 {
456 if ( entity->isEnabled() )
457 entity->update( _sceneState( mCameraController ) );
458 }
459 updateSceneState();
460}
461
462static void _updateNearFarPlane( const QList<QgsChunkNode *> &activeNodes, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar )
463{
464 for ( QgsChunkNode *node : activeNodes )
465 {
466 // project each corner of bbox to camera coordinates
467 // and determine closest and farthest point.
468 QgsAABB bbox = node->bbox();
469 for ( int i = 0; i < 8; ++i )
470 {
471 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
472 ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
473 ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
474
475 QVector4D pc = viewMatrix * p;
476
477
478 float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
479 fnear = std::min( fnear, dst );
480 ffar = std::max( ffar, dst );
481 }
482 }
483}
484
485bool Qgs3DMapScene::updateCameraNearFarPlanes()
486{
487 // Update near and far plane from the terrain.
488 // this needs to be done with great care as we have kind of circular dependency here:
489 // active nodes are culled based on the current frustum (which involves near + far plane)
490 // and then based on active nodes we set near and far plane.
491 //
492 // All of this is just heuristics assuming that all other stuff is being rendered somewhere
493 // around the area where the terrain is.
494 //
495 // Near/far plane is setup in order to make best use of the depth buffer to avoid:
496 // 1. precision errors - if the range is too great
497 // 2. unwanted clipping of scene - if the range is too small
498
499 Qt3DRender::QCamera *camera = cameraController()->camera();
500 QMatrix4x4 viewMatrix = camera->viewMatrix();
501 float fnear = 1e9;
502 float ffar = 0;
503 QList<QgsChunkNode *> activeNodes;
504 if ( mTerrain )
505 activeNodes = mTerrain->activeNodes();
506
507 // it could be that there are no active nodes - they could be all culled or because root node
508 // is not yet loaded - we still need at least something to understand bounds of our scene
509 // so lets use the root node
510 if ( mTerrain && activeNodes.isEmpty() )
511 activeNodes << mTerrain->rootNode();
512
513 _updateNearFarPlane( activeNodes, viewMatrix, fnear, ffar );
514
515 // Also involve all the other chunked entities to make sure that they will not get
516 // clipped by the near or far plane
517 for ( QgsChunkedEntity *e : std::as_const( mChunkEntities ) )
518 {
519 if ( e != mTerrain )
520 {
521 QList<QgsChunkNode *> activeEntityNodes = e->activeNodes();
522 if ( activeEntityNodes.empty() )
523 activeEntityNodes << e->rootNode();
524 _updateNearFarPlane( activeEntityNodes, viewMatrix, fnear, ffar );
525 }
526 }
527
528 if ( fnear < 1 )
529 fnear = 1; // does not really make sense to use negative far plane (behind camera)
530
531 // when zooming in a lot, fnear can become smaller than ffar. This should not happen
532 if ( fnear > ffar )
533 std::swap( fnear, ffar );
534
535 if ( fnear == 1e9 && ffar == 0 )
536 {
537 // the update didn't work out... this should not happen
538 // well at least temporarily use some conservative starting values
539 qWarning() << "oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
540 fnear = 1;
541 ffar = 1e9;
542 }
543
544 // set near/far plane - with some tolerance in front/behind expected near/far planes
545 float newFar = ffar * 2;
546 float newNear = fnear / 2;
547 if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
548 {
549 camera->setFarPlane( newFar );
550 camera->setNearPlane( newNear );
551 return true;
552 }
553
554 return false;
555}
556
557void Qgs3DMapScene::onFrameTriggered( float dt )
558{
559 mCameraController->frameTriggered( dt );
560
561 for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
562 {
563 if ( entity->isEnabled() && entity->needsUpdate() )
564 {
565 QgsDebugMsgLevel( QStringLiteral( "need for update" ), 2 );
566 entity->update( _sceneState( mCameraController ) );
567 }
568 }
569
570 updateSceneState();
571
572 // lock changing the FPS counter to 5 fps
573 static int frameCount = 0;
574 static float accumulatedTime = 0.0f;
575
576 if ( !mMap.isFpsCounterEnabled() )
577 {
578 frameCount = 0;
579 accumulatedTime = 0;
580 return;
581 }
582
583 frameCount++;
584 accumulatedTime += dt;
585 if ( accumulatedTime >= 0.2f )
586 {
587 float fps = ( float )frameCount / accumulatedTime;
588 frameCount = 0;
589 accumulatedTime = 0.0f;
590 emit fpsCountChanged( fps );
591 }
592}
593
594void Qgs3DMapScene::createTerrain()
595{
596 if ( mTerrain )
597 {
598 mChunkEntities.removeOne( mTerrain );
599
600 mTerrain->deleteLater();
601 mTerrain = nullptr;
602 }
603
604 if ( !mTerrainUpdateScheduled )
605 {
606 // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
607 QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
608 mTerrainUpdateScheduled = true;
609 setSceneState( Updating );
610 }
611 else
612 {
614 }
615}
616
617void Qgs3DMapScene::createTerrainDeferred()
618{
619 if ( mMap.terrainRenderingEnabled() && mMap.terrainGenerator() )
620 {
621 double tile0width = mMap.terrainGenerator()->extent().width();
622 int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.mapTileResolution(), mMap.maxTerrainGroundError() );
623 QgsAABB rootBbox = mMap.terrainGenerator()->rootChunkBbox( mMap );
624 float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
625 mMap.terrainGenerator()->setupQuadtree( rootBbox, rootError, maxZoomLevel );
626
627 mTerrain = new QgsTerrainEntity( mMap );
628 mTerrain->setParent( this );
629 mTerrain->setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
630
631 mCameraController->setTerrainEntity( mTerrain );
632
633 mChunkEntities << mTerrain;
634
635 connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
636 connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
637 }
638 else
639 {
640 mTerrain = nullptr;
641 mCameraController->setTerrainEntity( mTerrain );
642 }
643
644 // make sure that renderers for layers are re-created as well
645 const QList<QgsMapLayer *> layers = mMap.layers();
646 for ( QgsMapLayer *layer : layers )
647 {
648 // remove old entity - if any
649 removeLayerEntity( layer );
650
651 // add new entity - if any 3D renderer
652 addLayerEntity( layer );
653 }
654
656 onCameraChanged(); // force update of the new terrain
657 mTerrainUpdateScheduled = false;
658}
659
660void Qgs3DMapScene::onBackgroundColorChanged()
661{
662 mEngine->setClearColor( mMap.backgroundColor() );
663}
664
665void Qgs3DMapScene::updateLights()
666{
667 for ( Qt3DCore::QEntity *entity : std::as_const( mLightEntities ) )
668 entity->deleteLater();
669 mLightEntities.clear();
670
671 const QList< QgsLightSource * > newLights = mMap.lightSources();
672 for ( const QgsLightSource *source : newLights )
673 {
674 mLightEntities.append( source->createEntity( mMap, this ) );
675 }
676
677 onShadowSettingsChanged();
678}
679
680void Qgs3DMapScene::updateCameraLens()
681{
682 mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
683 mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
684 onCameraChanged();
685}
686
687void Qgs3DMapScene::onRenderersChanged()
688{
689 // remove entities (if any)
690 qDeleteAll( mRenderersEntities );
691 mRenderersEntities.clear();
692
693 // re-add entities from new set of renderers
694 const QList<QgsAbstract3DRenderer *> renderers = mMap.renderers();
695 for ( const QgsAbstract3DRenderer *renderer : renderers )
696 {
697 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
698 if ( newEntity )
699 {
700 newEntity->setParent( this );
701 finalizeNewEntity( newEntity );
702 mRenderersEntities[renderer] = newEntity;
703 }
704 }
705}
706
707void Qgs3DMapScene::onLayerRenderer3DChanged()
708{
709 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
710 Q_ASSERT( layer );
711
712 // remove old entity - if any
713 removeLayerEntity( layer );
714
715 // add new entity - if any 3D renderer
716 addLayerEntity( layer );
717}
718
719void Qgs3DMapScene::onLayersChanged()
720{
721 QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
722 QList<QgsMapLayer *> layersAdded;
723 const QList<QgsMapLayer *> layers = mMap.layers();
724 for ( QgsMapLayer *layer : layers )
725 {
726 if ( !layersBefore.contains( layer ) )
727 {
728 layersAdded << layer;
729 }
730 else
731 {
732 layersBefore.remove( layer );
733 }
734 }
735
736 // what is left in layersBefore are layers that have been removed
737 for ( QgsMapLayer *layer : std::as_const( layersBefore ) )
738 {
739 removeLayerEntity( layer );
740 }
741
742 for ( QgsMapLayer *layer : std::as_const( layersAdded ) )
743 {
744 addLayerEntity( layer );
745 }
746}
747
749{
750 for ( auto layer : mLayerEntities.keys() )
751 {
752 if ( layer->temporalProperties()->isActive() )
753 {
754 removeLayerEntity( layer );
755 addLayerEntity( layer );
756 }
757 }
758}
759
760void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
761{
762 bool needsSceneUpdate = false;
763 QgsAbstract3DRenderer *renderer = layer->renderer3D();
764 if ( renderer )
765 {
766 // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
767 // It has happened before that renderer pointed to a different layer (probably after copying a style).
768 // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
769 // the vector layer 3D renderer classes are not available.
770 if ( layer->type() == QgsMapLayerType::VectorLayer &&
771 ( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
772 {
773 static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
774 if ( renderer->type() == QLatin1String( "vector" ) )
775 {
776 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
777 if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry )
778 {
779 const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
780 if ( pointSymbol->shape() == QgsPoint3DSymbol::Model )
781 {
782 mModelVectorLayers.append( layer );
783 }
784 }
785 }
786 else if ( renderer->type() == QLatin1String( "rulebased" ) )
787 {
788 const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
789 for ( auto rule : rules )
790 {
791 const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
792 if ( pointSymbol && pointSymbol->shape() == QgsPoint3DSymbol::Model )
793 {
794 mModelVectorLayers.append( layer );
795 break;
796 }
797 }
798 }
799 }
800 else if ( layer->type() == QgsMapLayerType::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
801 {
802 QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
803 meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
804
805 // Before entity creation, set the maximum texture size
806 // Not very clean, but for now, only place found in the workflow to do that simple
807 QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
808 sym->setMaximumTextureSize( maximumTextureSize() );
809 meshRenderer->setSymbol( sym );
810 }
811 else if ( layer->type() == QgsMapLayerType::PointCloudLayer && renderer->type() == QLatin1String( "pointcloud" ) )
812 {
813 QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
814 pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
815 }
816
817 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
818 if ( newEntity )
819 {
820 newEntity->setParent( this );
821 mLayerEntities.insert( layer, newEntity );
822
823 finalizeNewEntity( newEntity );
824
825 if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
826 {
827 mChunkEntities.append( chunkedNewEntity );
828 needsSceneUpdate = true;
829
830 chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
831 connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject, this, &Qgs3DMapScene::onLayerEntityPickedObject );
832
833 connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
834 {
835 finalizeNewEntity( entity );
836 } );
837
838 connect( chunkedNewEntity, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
839 }
840 }
841 }
842
843 if ( needsSceneUpdate )
844 onCameraChanged(); // needed for chunked entities
845
846 connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
847
848 if ( layer->type() == QgsMapLayerType::VectorLayer )
849 {
850 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
851 connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
852 connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
853 }
854
855 if ( layer->type() == QgsMapLayerType::MeshLayer )
856 {
857 connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
858 }
859
860 if ( layer->type() == QgsMapLayerType::PointCloudLayer )
861 {
862 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
863 connect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
864 connect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
865 }
866}
867
868void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
869{
870 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
871
872 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
873 {
874 mChunkEntities.removeOne( chunkedEntity );
875 }
876
877 if ( entity )
878 entity->deleteLater();
879
880 disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
881
882 if ( layer->type() == QgsMapLayerType::VectorLayer )
883 {
884 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
885 disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
886 disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
887 mModelVectorLayers.removeAll( layer );
888 }
889
890 if ( layer->type() == QgsMapLayerType::MeshLayer )
891 {
892 disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
893 }
894
895 if ( layer->type() == QgsMapLayerType::PointCloudLayer )
896 {
897 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
898 disconnect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
899 disconnect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
900 }
901}
902
903void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
904{
905 // this is probably not the best place for material-specific configuration,
906 // maybe this could be more generalized when other materials need some specific treatment
907 for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
908 {
909 connect( mCameraController, &QgsCameraController::viewportChanged, lm, [lm, this]
910 {
911 lm->setViewportSize( mCameraController->viewport().size() );
912 } );
913
914 lm->setViewportSize( cameraController()->viewport().size() );
915 }
916 // configure billboard's viewport when the viewport is changed.
917 for ( QgsPoint3DBillboardMaterial *bm : newEntity->findChildren<QgsPoint3DBillboardMaterial *>() )
918 {
919 connect( mCameraController, &QgsCameraController::viewportChanged, bm, [bm, this]
920 {
921 bm->setViewportSize( mCameraController->viewport().size() );
922 } );
923
924 bm->setViewportSize( mCameraController->viewport().size() );
925 }
926
927 // Finalize adding the 3D transparent objects by adding the layer components to the entities
928 QgsShadowRenderingFrameGraph *frameGraph = mEngine->frameGraph();
929 Qt3DRender::QLayer *transparentLayer = frameGraph->transparentObjectLayer();
930 for ( Qt3DRender::QMaterial *material : newEntity->findChildren<Qt3DRender::QMaterial *>() )
931 {
932 // This handles the phong material without data defined properties.
933 if ( Qt3DExtras::QDiffuseSpecularMaterial *ph = qobject_cast<Qt3DExtras::QDiffuseSpecularMaterial *>( material ) )
934 {
935 if ( ph->diffuse().value<QColor>().alphaF() != 1.0f )
936 {
937 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( ph->parent() );
938 if ( entity && !entity->components().contains( transparentLayer ) )
939 {
940 entity->addComponent( transparentLayer );
941 }
942 }
943 }
944 else
945 {
946 // This handles the phong material with data defined properties, the textured case and point (instanced) symbols.
947 Qt3DRender::QEffect *effect = material->effect();
948 if ( effect )
949 {
950 for ( const auto *parameter : effect->parameters() )
951 {
952 if ( parameter->name() == "opacity" && parameter->value() != 1.0f )
953 {
954 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( material->parent() );
955 if ( entity && !entity->components().contains( transparentLayer ) )
956 {
957 entity->addComponent( transparentLayer );
958 }
959 break;
960 }
961 }
962 }
963 }
964 }
965}
966
967int Qgs3DMapScene::maximumTextureSize() const
968{
969 QSurface *surface = mEngine->surface();
970 QOpenGLContext context;
971 context.create();
972 bool success = context.makeCurrent( surface );
973
974 if ( success )
975 {
976 QOpenGLFunctions openglFunctions = QOpenGLFunctions( &context );
977
978 GLint size;
979 openglFunctions.initializeOpenGLFunctions();
980 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
981 return int( size );
982 }
983 else
984 {
985 return 4096; //we can't have a context to defined the max texture size, we use this reasonable value
986 }
987
988}
989
990void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
991{
992 mEntityCameraViewCenter = new Qt3DCore::QEntity;
993
994 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
995 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
996 connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera]
997 {
998 trCameraViewCenter->setTranslation( camera->viewCenter() );
999 } );
1000
1001 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
1002 materialCameraViewCenter->setAmbient( Qt::red );
1003 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
1004
1005 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
1006 rendererCameraViewCenter->setRadius( 10 );
1007 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
1008
1009 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
1010 mEntityCameraViewCenter->setParent( this );
1011
1012 connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this]
1013 {
1014 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
1015 } );
1016}
1017
1018void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
1019{
1020 if ( mSceneState == state )
1021 return;
1022 mSceneState = state;
1023 emit sceneStateChanged();
1024}
1025
1026void Qgs3DMapScene::updateSceneState()
1027{
1028 if ( mTerrainUpdateScheduled )
1029 {
1030 setSceneState( Updating );
1031 return;
1032 }
1033
1034 for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
1035 {
1036 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
1037 {
1038 setSceneState( Updating );
1039 return;
1040 }
1041 }
1042
1043 setSceneState( Ready );
1044}
1045
1046void Qgs3DMapScene::onSkyboxSettingsChanged()
1047{
1048 QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
1049 if ( mSkybox != nullptr )
1050 {
1051 mSkybox->deleteLater();
1052 mSkybox = nullptr;
1053 }
1054
1055 mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
1056
1057 if ( mMap.isSkyboxEnabled() )
1058 {
1059 QMap<QString, QString> faces;
1060 switch ( skyboxSettings.skyboxType() )
1061 {
1063 faces = skyboxSettings.cubeMapFacesPaths();
1064 mSkybox = new QgsCubeFacesSkyboxEntity(
1065 faces[QStringLiteral( "posX" )], faces[QStringLiteral( "posY" )], faces[QStringLiteral( "posZ" )],
1066 faces[QStringLiteral( "negX" )], faces[QStringLiteral( "negY" )], faces[QStringLiteral( "negZ" )],
1067 this
1068 );
1069 break;
1071 mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
1072 break;
1073 }
1074 }
1075}
1076
1077void Qgs3DMapScene::onShadowSettingsChanged()
1078{
1079 QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1080
1081 const QList< QgsLightSource * > lightSources = mMap.lightSources();
1082 QList< QgsDirectionalLightSettings * > directionalLightSources;
1083 for ( QgsLightSource *source : lightSources )
1084 {
1085 if ( source->type() == Qgis::LightSourceType::Directional )
1086 {
1087 directionalLightSources << qgis::down_cast< QgsDirectionalLightSettings * >( source );
1088 }
1089 }
1090
1091 QgsShadowSettings shadowSettings = mMap.shadowSettings();
1092 int selectedLight = shadowSettings.selectedDirectionalLight();
1093 if ( shadowSettings.renderShadows() && selectedLight >= 0 && selectedLight < directionalLightSources.count() )
1094 {
1095 shadowRenderingFrameGraph->setShadowRenderingEnabled( true );
1096 shadowRenderingFrameGraph->setShadowBias( shadowSettings.shadowBias() );
1097 shadowRenderingFrameGraph->setShadowMapResolution( shadowSettings.shadowMapResolution() );
1098 QgsDirectionalLightSettings light = *directionalLightSources.at( selectedLight );
1099 shadowRenderingFrameGraph->setupDirectionalLight( light, shadowSettings.maximumShadowRenderingDistance() );
1100 }
1101 else
1102 shadowRenderingFrameGraph->setShadowRenderingEnabled( false );
1103}
1104
1105void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
1106{
1107 QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1108 shadowRenderingFrameGraph->setupShadowMapDebugging( mMap.debugShadowMapEnabled(), mMap.debugShadowMapCorner(), mMap.debugShadowMapSize() );
1109}
1110
1111void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1112{
1113 QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1114 shadowRenderingFrameGraph->setupDepthMapDebugging( mMap.debugDepthMapEnabled(), mMap.debugDepthMapCorner(), mMap.debugDepthMapSize() );
1115}
1116
1117void Qgs3DMapScene::onDebugOverlayEnabledChanged()
1118{
1119 QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1120 shadowRenderingFrameGraph->setDebugOverlayEnabled( mMap.isDebugOverlayEnabled() );
1121}
1122
1123void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1124{
1125 QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
1126
1127 bool edlEnabled = mMap.eyeDomeLightingEnabled();
1128 double edlStrength = mMap.eyeDomeLightingStrength();
1129 double edlDistance = mMap.eyeDomeLightingDistance();
1130 shadowRenderingFrameGraph->setupEyeDomeLighting( edlEnabled, edlStrength, edlDistance );
1131}
1132
1133void Qgs3DMapScene::onCameraMovementSpeedChanged()
1134{
1135 mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1136}
1137
1138void Qgs3DMapScene::onCameraNavigationModeChanged()
1139{
1140 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
1141}
1142
1144{
1145 QVector<QString> notParsedLayers;
1146 Qgs3DSceneExporter exporter;
1147
1148 exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1149 exporter.setSmoothEdges( exportSettings.smoothEdges() );
1150 exporter.setExportNormals( exportSettings.exportNormals() );
1151 exporter.setExportTextures( exportSettings.exportTextures() );
1152 exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1153 exporter.setScale( exportSettings.scale() );
1154
1155 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1156 {
1157 QgsMapLayer *layer = it.key();
1158 Qt3DCore::QEntity *rootEntity = it.value();
1159 QgsMapLayerType layerType = layer->type();
1160 switch ( layerType )
1161 {
1163 if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1164 notParsedLayers.push_back( layer->name() );
1165 break;
1173 notParsedLayers.push_back( layer->name() );
1174 break;
1175 }
1176 }
1177
1178 if ( mTerrain )
1179 exporter.parseTerrain( mTerrain, "Terrain" );
1180
1181 exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1182
1183 if ( !notParsedLayers.empty() )
1184 {
1185 QString message = tr( "The following layers were not exported:" ) + "\n";
1186 for ( const QString &layerName : notParsedLayers )
1187 message += layerName + "\n";
1188 QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, QgsMessageOutput::MessageText );
1189 }
1190}
1191
1192QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1193{
1194 QVector<const QgsChunkNode *> chunks;
1195 if ( !mLayerEntities.contains( layer ) ) return chunks;
1196 if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[ layer ] ) )
1197 {
1198 for ( QgsChunkNode *n : c->activeNodes() )
1199 chunks.push_back( n );
1200 }
1201 return chunks;
1202}
1203
1205{
1206 QgsRectangle extent;
1207 extent.setMinimal();
1208
1209 for ( QgsMapLayer *layer : mLayerEntities.keys() )
1210 {
1211 Qt3DCore::QEntity *layerEntity = mLayerEntities[ layer ];
1212 QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( layerEntity );
1213 if ( !c )
1214 continue;
1215 QgsChunkNode *chunkNode = c->rootNode();
1216 QgsAABB bbox = chunkNode->bbox();
1217 QgsRectangle layerExtent = Qgs3DUtils::worldToLayerExtent( bbox, layer->crs(), mMap.origin(), mMap.crs(), mMap.transformContext() );
1218 extent.combineExtentWith( layerExtent );
1219 }
1220
1221 if ( mMap.terrainRenderingEnabled() )
1222 {
1223 if ( QgsTerrainGenerator *terrainGenerator = mMap.terrainGenerator() )
1224 {
1225 QgsRectangle terrainExtent = terrainGenerator->extent();
1226 QgsCoordinateTransform terrainToMapTransform( terrainGenerator->crs(), mMap.crs(), QgsProject::instance() );
1227 terrainToMapTransform.setBallparkTransformsAreAppropriate( true );
1228 terrainExtent = terrainToMapTransform.transformBoundingBox( terrainExtent );
1229 extent.combineExtentWith( terrainExtent );
1230 }
1231 }
1232
1233 return extent;
1234}
1235
1236void Qgs3DMapScene::addCameraRotationCenterEntity( QgsCameraController *controller )
1237{
1238 mEntityRotationCenter = new Qt3DCore::QEntity;
1239
1240 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
1241 mEntityRotationCenter->addComponent( trCameraViewCenter );
1242 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
1243 materialCameraViewCenter->setAmbient( Qt::blue );
1244 mEntityRotationCenter->addComponent( materialCameraViewCenter );
1245 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
1246 rendererCameraViewCenter->setRadius( 10 );
1247 mEntityRotationCenter->addComponent( rendererCameraViewCenter );
1248 mEntityRotationCenter->setEnabled( true );
1249 mEntityRotationCenter->setParent( this );
1250
1251 connect( controller, &QgsCameraController::cameraRotationCenterChanged, this, [trCameraViewCenter]( QVector3D center )
1252 {
1253 trCameraViewCenter->setTranslation( center );
1254 } );
1255
1256 mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() );
1257
1258 connect( &mMap, &Qgs3DMapSettings::showCameraRotationCenterChanged, this, [this]
1259 {
1260 mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() );
1261 } );
1262}
1263
1264void Qgs3DMapScene::on3DAxisSettingsChanged()
1265{
1266 if ( m3DAxis )
1267 {
1268 m3DAxis->onAxisSettingsChanged();
1269 }
1270 else
1271 {
1272 if ( QgsWindow3DEngine *engine = dynamic_cast<QgsWindow3DEngine *>( mEngine ) )
1273 {
1274 m3DAxis = new Qgs3DAxis( static_cast<Qt3DExtras::Qt3DWindow *>( engine->window() ),
1275 engine->root(),
1276 this,
1277 mCameraController,
1278 &mMap );
1279 }
1280 }
1281}
@ Directional
Directional light source.
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
Definition: qgs3daxis.cpp:998
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.
void unregisterPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object pick...
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)
void registerPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Registers an object that will get results of pick events on 3D entities. Does not take ownership of t...
Qgs3DMapScene(Qgs3DMapSettings &map, QgsAbstract3DEngine *engine)
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
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.
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.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:78
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 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 renderersChanged()
Emitted when the list of map's extra renderers have been modified.
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.
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.
QList< QgsAbstract3DRenderer * > renderers() const
Returns list of extra 3D renderers.
void projectionTypeChanged()
Emitted when the camera lens projection type changes.
float fieldOfView() const
Returns the camera lens' field of view.
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 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.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
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.
QgsCameraController::NavigationMode cameraNavigationMode() const
Returns the navigation mode used by the camera.
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.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
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 QgsRectangle worldToLayerExtent(const QgsAABB &bbox, const QgsCoordinateReferenceSystem &layerCrs, const QgsVector3D &mapOrigin, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &context)
Converts axis aligned bounding box in 3D world coordinates to extent in map layer CRS.
Definition: qgs3dutils.cpp:582
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...
Definition: qgs3dutils.cpp:227
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.
Definition: qgs3dutils.cpp:681
3
Definition: qgsaabb.h:34
float yMax
Definition: qgsaabb.h:85
float xMax
Definition: qgsaabb.h:84
float xMin
Definition: qgsaabb.h:81
float zMax
Definition: qgsaabb.h:86
float yMin
Definition: qgsaabb.h:82
float zMin
Definition: qgsaabb.h:83
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.
static QgsSourceCache * sourceCache()
Returns the application's source cache, used for caching embedded and remote source strings as local ...
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates.
void setCameraNavigationMode(QgsCameraController::NavigationMode navigationMode)
Sets the navigation mode used by the camera controller.
Qt3DRender::QCamera * camera
float distance() const
Returns distance of the camera from the point it is looking at.
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene.
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 setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
void viewportChanged()
Emitted when viewport rectangle has been updated.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
A skybox constructed from a 6 cube faces.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
void rendererChanged()
Signal emitted when renderer is changed.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1502
void layerModified()
Emitted when modifications has been done on layer.
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.
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.
Definition: qgsmeshlayer.h:99
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
A skybox constructed from a panoramic image.
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.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:478
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
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 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 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 setDebugOverlayEnabled(bool enabled)
Sets whether debug overlay is 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.
bool isActive() const
Returns true if the temporal property is active.
virtual QgsRectangle extent() const =0
extent of the terrain in terrain's CRS
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.
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 QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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:2421
void addQLayerComponentsToHierarchy(Qt3DCore::QEntity *entity, const QVector< Qt3DRender::QLayer * > &layers)
QgsChunkedEntity::SceneState _sceneState(QgsCameraController *cameraController)
void removeQLayerComponentsFromHierarchy(Qt3DCore::QEntity *entity)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39