QGIS API Documentation 4.1.0-Master (31622b25bb0)
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 <limits>
19
20#include "qgs3daxis.h"
22#include "qgs3dmapsettings.h"
23#include "qgs3dsceneexporter.h"
24#include "qgs3dutils.h"
25#include "qgsaabb.h"
26#include "qgsabstract3dengine.h"
30#include "qgsannotationlayer.h"
32#include "qgsapplication.h"
33#include "qgscameracontroller.h"
35#include "qgschunkedentity.h"
36#include "qgschunknode.h"
37#include "qgseventtracing.h"
40#include "qgsframegraph.h"
41#include "qgsgeotransform.h"
45#include "qgslightsource.h"
46#include "qgslinematerial_p.h"
47#include "qgslogger.h"
50#include "qgsmaterial.h"
51#include "qgsmeshlayer.h"
53#include "qgsmessageoutput.h"
57#include "qgspoint3dsymbol.h"
58#include "qgspointcloudlayer.h"
62#include "qgsshadowrenderview.h"
63#include "qgsskyboxentity.h"
64#include "qgsskyboxsettings.h"
65#include "qgssourcecache.h"
66#include "qgsterrainentity.h"
67#include "qgsterraingenerator.h"
68#include "qgstiledscenelayer.h"
70#include "qgsvectorlayer.h"
72#include "qgswindow3dengine.h"
73
74#include <QOpenGLContext>
75#include <QOpenGLFunctions>
76#include <QString>
77#include <QSurface>
78#include <QTimer>
79#include <QUrl>
80#include <Qt3DExtras/QDiffuseSpecularMaterial>
81#include <Qt3DExtras/QForwardRenderer>
82#include <Qt3DExtras/QPhongMaterial>
83#include <Qt3DExtras/QSphereMesh>
84#include <Qt3DLogic/QFrameAction>
85#include <Qt3DRender/QCamera>
86#include <Qt3DRender/QCullFace>
87#include <Qt3DRender/QDepthTest>
88#include <Qt3DRender/QEffect>
89#include <Qt3DRender/QMaterial>
90#include <Qt3DRender/QMesh>
91#include <Qt3DRender/QRenderPass>
92#include <Qt3DRender/QRenderSettings>
93#include <Qt3DRender/QRenderState>
94#include <Qt3DRender/QSceneLoader>
95#include <Qt3DRender/QTechnique>
96#include <QtMath>
97
98#include "moc_qgs3dmapscene.cpp"
99
100using namespace Qt::StringLiterals;
101
102std::function<QMap<QString, Qgs3DMapScene *>()> Qgs3DMapScene::sOpenScenesFunction = [] { return QMap<QString, Qgs3DMapScene *>(); };
103
105 : mMap( map )
106 , mEngine( engine )
107{
108 connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
109 onBackgroundColorChanged();
110
111 // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
112 // even if there's no change. Switching to "on demand" should only re-render when something has changed
113 // and we save quite a lot of resources
114 mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
115
116 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
117
118 // Get the maximum of clip planes available
119 mMaxClipPlanes = Qgs3DUtils::openGlMaxClipPlanes( mEngine->surface() );
120
121 // Camera
122 float aspectRatio = ( float ) viewportRect.width() / viewportRect.height();
123 mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
124
125 mFrameAction = new Qt3DLogic::QFrameAction();
126 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qgs3DMapScene::onFrameTriggered );
127 addComponent( mFrameAction ); // takes ownership
128
129 // Camera controlling
130 mCameraController = new QgsCameraController( this ); // attaches to the scene
131
132 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
133 mCameraController->resetGlobe( 10'000'000 );
134 else
135 mCameraController->resetView( 1000 );
136
137 addCameraViewCenterEntity( mEngine->camera() );
138 addCameraRotationCenterEntity( mCameraController );
139 updateLights();
140
141 // create terrain entity
142
143 createTerrainDeferred();
144 connect( &map, &Qgs3DMapSettings::extentChanged, this, &Qgs3DMapScene::createTerrain );
145 connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
146
147 connect( &map, &Qgs3DMapSettings::terrainSettingsChanged, this, &Qgs3DMapScene::createTerrain );
148
149 connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
150 connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
151 connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
152 connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
153 connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
154 connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
155 connect( &map, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapScene::onAmbientOcclusionSettingsChanged );
156 connect( &map, &Qgs3DMapSettings::bloomSettingsChanged, this, &Qgs3DMapScene::onBloomSettingsChanged );
157 connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
158 connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
159 connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
160 connect( &map, &Qgs3DMapSettings::msaaEnabledChanged, this, &Qgs3DMapScene::onMsaaEnabledChanged );
161 connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
163 connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
164 connect( &map, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapScene::onCameraNavigationModeChanged );
165 connect( &map, &Qgs3DMapSettings::debugOverlayEnabledChanged, this, &Qgs3DMapScene::onDebugOverlayEnabledChanged );
166 connect( &map, &Qgs3DMapSettings::stopUpdatesChanged, this, &Qgs3DMapScene::onStopUpdatesChanged );
167 connect( &map, &Qgs3DMapSettings::show2DMapOverlayChanged, this, &Qgs3DMapScene::onShowMapOverlayChanged );
168 connect( &map, &Qgs3DMapSettings::viewFrustumVisualizationEnabledChanged, this, &Qgs3DMapScene::onShowMapOverlayChanged );
169
170 connect( &map, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapScene::on3DAxisSettingsChanged );
171
172 connect( &map, &Qgs3DMapSettings::originChanged, this, &Qgs3DMapScene::onOriginChanged );
173
174 connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [this]( const QString &url ) {
175 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
176 for ( QgsMapLayer *layer : modelVectorLayers )
177 {
178 QgsAbstract3DRenderer *renderer = layer->renderer3D();
179 if ( renderer )
180 {
181 if ( renderer->type() == "vector"_L1 )
182 {
183 const QgsPoint3DSymbol *pointSymbol = static_cast<const QgsPoint3DSymbol *>( static_cast<QgsVectorLayer3DRenderer *>( renderer )->symbol() );
184 if ( pointSymbol && pointSymbol->shapeProperty( u"model"_s ).toString() == url )
185 {
186 removeLayerEntity( layer );
187 addLayerEntity( layer );
188 }
189 }
190 else if ( renderer->type() == "rulebased"_L1 )
191 {
192 const QgsRuleBased3DRenderer::RuleList rules = static_cast<QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
193 for ( const QgsRuleBased3DRenderer::Rule *rule : rules )
194 {
195 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( rule->symbol() );
196 if ( pointSymbol && pointSymbol->shapeProperty( u"model"_s ).toString() == url )
197 {
198 removeLayerEntity( layer );
199 addLayerEntity( layer );
200 break;
201 }
202 }
203 }
204 }
205 }
206 } );
207
208 // listen to changes of layers in order to add/remove 3D renderer entities
209 connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
210
211 connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
212 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, this, &Qgs3DMapScene::onCameraChanged );
213
214 connect( &map, &Qgs3DMapSettings::backgroundSettingsChanged, this, &Qgs3DMapScene::onBackgroundSettingsChanged );
215 onBackgroundSettingsChanged();
216
217 // force initial update of chunked entities
218 onCameraChanged();
219 // force initial update of eye dome shading
220 onEyeDomeShadingSettingsChanged();
221 // force initial update of debugging setting of preview quads
222 onDebugDepthMapSettingsChanged();
223 // force initial update of ambient occlusion settings
224 onAmbientOcclusionSettingsChanged();
225 // force initial update of MSAA setting
226 onMsaaEnabledChanged();
227 // initial state of bloom setting
228 onBloomSettingsChanged();
229
230 // timer used to refresh the map overlay every 250 ms while the camera is moving.
231 // schedule2DMapOverlayUpdate() is called to schedule the update.
232 // At the end of the delay, applyPendingOverlayUpdate() performs the update.
233 mOverlayUpdateTimer = new QTimer( this );
234 mOverlayUpdateTimer->setSingleShot( true );
235 mOverlayUpdateTimer->setInterval( 250 );
236 connect( mOverlayUpdateTimer, &QTimer::timeout, this, &Qgs3DMapScene::applyPendingOverlayUpdate );
237
238 // force initial update of map overlay entity
239 onShowMapOverlayChanged();
240
241 onCameraMovementSpeedChanged();
242
243 on3DAxisSettingsChanged();
244}
245
247{
248 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
249 {
250 mCameraController->resetGlobe( 10'000'000 );
251 return;
252 }
253
254 const QgsDoubleRange zRange = elevationRange();
255 const QgsRectangle extent = sceneExtent();
256 const double side = std::max( extent.width(), extent.height() );
257 double d = side / 2 / std::tan( cameraController()->camera()->fieldOfView() / 2 * M_PI / 180 );
258 d += zRange.isInfinite() ? 0. : zRange.upper();
259 mCameraController->resetView( static_cast<float>( d ) );
260}
261
263{
264 QgsPointXY center = extent.center();
265 const QgsVector3D origin = mMap.origin();
266
267 const QgsVector3D p1 = mMap.mapToWorldCoordinates( QgsVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
268 const QgsVector3D p2 = mMap.mapToWorldCoordinates( QgsVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
269
270 const double xSide = std::abs( p1.x() - p2.x() );
271 const double ySide = std::abs( p1.y() - p2.y() );
272 const double side = std::max( xSide, ySide );
273
274 const double fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
275 double distance = side / 2.0f / std::tan( fov / 2.0f );
276
277 // adjust by elevation
278 const QgsDoubleRange zRange = elevationRange();
279 if ( !zRange.isInfinite() )
280 distance += zRange.upper();
281
282 // subtract map origin so coordinates are relative to it
283 // clang-format off
284 mCameraController->setViewFromTop(
285 static_cast<float>( center.x() - origin.x() ),
286 static_cast<float>( center.y() - origin.y() ),
287 static_cast<float>( distance )
288 );
289 // clang-format on
290}
291
292QVector<QgsPointXY> Qgs3DMapScene::viewFrustum2DExtent() const
293{
294 Qt3DRender::QCamera *camera = mCameraController->camera();
295 QVector<QgsPointXY> extent;
296 QVector<int> pointsOrder = { 0, 1, 3, 2 };
297 for ( int i : pointsOrder )
298 {
299 const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : mEngine->size().width(), ( ( i >> 1 ) & 1 ) ? 0 : mEngine->size().height() );
300 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, mEngine->size(), camera );
301 QVector3D dir = ray.direction();
302 if ( dir.z() == 0.0 )
303 dir.setZ( 0.000001 );
304 double t = -ray.origin().z() / dir.z();
305 if ( t < 0 )
306 {
307 // If the projected point is on the back of the camera we choose the farthest point in the front
308 t = camera->farPlane();
309 }
310 else
311 {
312 // If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
313 t = std::min<float>( t, camera->farPlane() );
314 }
315 QVector3D planePoint = ray.origin() + t * dir;
316 QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
317 extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
318 }
319 return extent;
320}
321
323{
324 int count = 0;
325 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
326 count += entity->pendingJobsCount();
327 return count;
328}
329
330double Qgs3DMapScene::worldSpaceError( double epsilon, double distance ) const
331{
332 Qt3DRender::QCamera *camera = mCameraController->camera();
333 const double fov = camera->fieldOfView();
334 const QSize size = mEngine->size();
335 const int screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
336
337 // see Qgs3DUtils::screenSpaceError() for the inverse calculation (world space error to screen space error)
338 // with explanation of the math.
339 const double frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
340 const double err = frustumWidthAtDistance * epsilon / screenSizePx;
341 return err;
342}
343
344void Qgs3DMapScene::onCameraChanged()
345{
346 updateScene( true );
347 updateCameraNearFarPlanes();
348
349 onShadowSettingsChanged();
350
351 const QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
352 emit viewed2DExtentFrom3DChanged( extent2D );
353 schedule2DMapOverlayUpdate();
354
355 // The magic to make things work better in large scenes (e.g. more than 50km across)
356 // is here: we will simply move the origin of the scene, and update transforms
357 // of the camera and all other entities. That should ensure we will not need to deal
358 // with large coordinates in 32-bit floats (and if we do have large coordinates,
359 // because the scene is far from the camera, we don't care, because those errors
360 // end up being tiny when viewed from far away).
361 constexpr float ORIGIN_SHIFT_THRESHOLD = 10'000;
362 if ( mSceneOriginShiftEnabled && mEngine->camera()->position().length() > ORIGIN_SHIFT_THRESHOLD )
363 {
364 const QgsVector3D newOrigin = mMap.origin() + QgsVector3D( mEngine->camera()->position() );
365 QgsDebugMsgLevel( u"Rebasing scene origin from %1 to %2"_s.arg( mMap.origin().toString( 1 ), newOrigin.toString( 1 ) ), 2 );
366 mMap.setOrigin( newOrigin );
367 }
368}
369
370bool Qgs3DMapScene::updateScene( bool forceUpdate )
371{
372 if ( !mSceneUpdatesEnabled )
373 {
374 QgsDebugMsgLevel( "Scene update skipped", 2 );
375 return false;
376 }
377
378 QgsEventTracing::ScopedEvent traceEvent( u"3D"_s, forceUpdate ? u"Force update scene"_s : u"Update scene"_s );
379
380 Qgs3DMapSceneEntity::SceneContext sceneContext;
381 Qt3DRender::QCamera *camera = mEngine->camera();
382 sceneContext.cameraFov = camera->fieldOfView();
383 sceneContext.cameraPos = camera->position();
384 const QSize size = mEngine->size();
385 sceneContext.screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
386
387 // Make our own projection matrix so that frustum culling done by the
388 // entities isn't dependent on the current near/far planes, which would then
389 // require multiple steps to stabilize.
390 // The matrix is constructed just like in QMatrix4x4::perspective() /
391 // ortho(), but for all elements involving the near and far plane, the limit
392 // of the expression with the far plane going to infinity is taken.
393 QMatrix4x4 projMatrix;
394 switch ( mMap.projectionType() )
395 {
396 case Qt3DRender::QCameraLens::PerspectiveProjection:
397 {
398 float fovRadians = ( camera->fieldOfView() / 2.0f ) * static_cast<float>( M_PI ) / 180.0f;
399 float fovCotan = std::cos( fovRadians ) / std::sin( fovRadians );
400 // clang-format off
401 projMatrix = {
402 fovCotan / camera->aspectRatio(), 0, 0, 0,
403 0, fovCotan, 0, 0,
404 0, 0, -1, -2,
405 0, 0, -1, 0
406 };
407 // clang-format on
408 break;
409 }
410 case Qt3DRender::QCameraLens::OrthographicProjection:
411 {
412 Qt3DRender::QCameraLens *lens = camera->lens();
413 // clang-format off
414 projMatrix = {
415 2.0f / ( lens->right() - lens->left() ), 0, 0, 0,
416 0, 2.0f / ( lens->top() - lens->bottom() ), 0, 0,
417 0, 0, 1, 0,
418 -( lens->left() + lens->right() ) / ( lens->right() - lens->left() ), -( lens->top() + lens->bottom() ) / ( lens->top() - lens->bottom() ), -1.0f, 1.0f
419 };
420 // clang-format on
421 break;
422 }
423 default:
424 QgsDebugError( "Unhandled 3D projection type" );
425 projMatrix = camera->projectionMatrix(); // Give up and use the current matrix
426 }
427 sceneContext.viewProjectionMatrix = projMatrix * camera->viewMatrix();
428
429
430 bool anyUpdated = false;
431 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
432 {
433 if ( forceUpdate || ( entity->isEnabled() && entity->needsUpdate() ) )
434 {
435 anyUpdated = true;
436 entity->handleSceneUpdate( sceneContext );
437 if ( entity->hasReachedGpuMemoryLimit() )
439 }
440 }
441
442 updateSceneState();
443
444 return anyUpdated;
445}
446
447bool Qgs3DMapScene::updateCameraNearFarPlanes()
448{
449 // Update near and far plane from the terrain.
450 // this needs to be done with great care as we have kind of circular dependency here:
451 // active nodes are culled based on the current frustum (which involves near + far plane)
452 // and then based on active nodes we set near and far plane.
453 //
454 // All of this is just heuristics assuming that all other stuff is being rendered somewhere
455 // around the area where the terrain is.
456 //
457 // Near/far plane is setup in order to make best use of the depth buffer to avoid:
458 // 1. precision errors - if the range is too great
459 // 2. unwanted clipping of scene - if the range is too small
460
461 Qt3DRender::QCamera *camera = cameraController()->camera();
462 QMatrix4x4 viewMatrix = camera->viewMatrix();
463 float fnear = 1e9;
464 float ffar = 0;
465
466 // Iterate all scene entities to make sure that they will not get
467 // clipped by the near or far plane
468 for ( Qgs3DMapSceneEntity *se : std::as_const( mSceneEntities ) )
469 {
470 const QgsRange<float> depthRange = se->getNearFarPlaneRange( viewMatrix );
471
472 fnear = std::min( fnear, depthRange.lower() );
473 ffar = std::max( ffar, depthRange.upper() );
474 }
475
476 if ( fnear < 1 )
477 fnear = 1; // does not really make sense to use negative far plane (behind camera)
478
479 // the update didn't work out... this can happen if the scene does not contain
480 // any Qgs3DMapSceneEntity. Use the scene extent to compute near and far planes
481 // as a fallback.
482 if ( fnear == 1e9 && ffar == 0 )
483 {
484 QgsDoubleRange sceneZRange = elevationRange();
485 sceneZRange = sceneZRange.isInfinite() ? QgsDoubleRange( 0.0, 0.0 ) : sceneZRange;
486 const QgsAABB sceneBbox = Qgs3DUtils::mapToWorldExtent( mMap.extent(), sceneZRange.lower(), sceneZRange.upper(), mMap.origin() );
487 Qgs3DUtils::computeBoundingBoxNearFarPlanes( sceneBbox, viewMatrix, fnear, ffar );
488 }
489
490 // when zooming in a lot, fnear can become smaller than ffar. This should not happen
491 if ( fnear > ffar )
492 std::swap( fnear, ffar );
493
494 // set near/far plane - with some tolerance in front/behind expected near/far planes
495 float newFar = ffar * 2;
496 float newNear = fnear / 2;
497 if ( !qgsFloatNear( newFar, camera->farPlane() ) || !qgsFloatNear( newNear, camera->nearPlane() ) )
498 {
499 camera->setFarPlane( newFar );
500 camera->setNearPlane( newNear );
501 return true;
502 }
503
504 return false;
505}
506
507void Qgs3DMapScene::onFrameTriggered( float dt )
508{
509 QgsEventTracing::addEvent( QgsEventTracing::EventType::Instant, u"3D"_s, u"Frame begins"_s );
510
511 mCameraController->frameTriggered( dt );
512
513 if ( updateScene() )
514 // If the scene was changed, node bboxes might have changed, so we need to
515 // update near/far planes.
516 updateCameraNearFarPlanes();
517
518 // lock changing the FPS counter to 5 fps
519 static int frameCount = 0;
520 static float accumulatedTime = 0.0f;
521
522 if ( !mMap.isFpsCounterEnabled() )
523 {
524 frameCount = 0;
525 accumulatedTime = 0;
526 return;
527 }
528
529 frameCount++;
530 accumulatedTime += dt;
531 if ( accumulatedTime >= 0.2f )
532 {
533 float fps = ( float ) frameCount / accumulatedTime;
534 frameCount = 0;
535 accumulatedTime = 0.0f;
536 emit fpsCountChanged( fps );
537 }
538}
539
540void Qgs3DMapScene::update2DMapOverlay( const QVector<QgsPointXY> &extent2DAsPoints )
541{
542 QgsFrameGraph *frameGraph = mEngine->frameGraph();
543 QgsOverlayTextureRenderView &overlayRenderView = frameGraph->overlayTextureRenderView();
544
545 if ( !mMap.is2DMapOverlayEnabled() )
546 {
547 if ( mMapOverlayEntity )
548 {
549 mMapOverlayEntity.reset();
550 }
551 overlayRenderView.setEnabled( mMap.debugDepthMapEnabled() );
552 return;
553 }
554
555 if ( !mMapOverlayEntity )
556 {
557 QgsWindow3DEngine *engine = qobject_cast<QgsWindow3DEngine *>( mEngine );
558 mMapOverlayEntity.reset( new QgsMapOverlayEntity( engine, &overlayRenderView, &mMap, this ) );
559 mMapOverlayEntity->setEnabled( true );
560 overlayRenderView.setEnabled( true );
561 }
562
563
564 Qt3DRender::QCamera *camera = mEngine->camera();
565 const QgsVector3D extentCenter3D = mMap.worldToMapCoordinates( camera->position() );
566 const QgsPointXY extentCenter2D( extentCenter3D.x(), extentCenter3D.y() );
567
568 // Compute an extent that provides an overview around the camera position.
569 // When the view is from above (pitch near 0°), the scene's extent is reduced.
570 // As the pitch increases (up to 90°), a larger portion of the scene becomes visible.
571 // The smoothing factor allows reproducing the behavior of the maximum extent
572 // as the pitch changes. The calculated extent is bounded to prevent covering too large a scene.
573 double minHalfExtent = std::numeric_limits<double>::max();
574 double maxHalfExtent = 0.0;
575 for ( const QgsPointXY &extentPoint : extent2DAsPoints )
576 {
577 const double distance = extentCenter2D.distance( extentPoint );
578 minHalfExtent = std::min( minHalfExtent, distance );
579 maxHalfExtent = std::max( maxHalfExtent, distance );
580 }
581
582 const QgsRectangle fullExtent = sceneExtent();
583 const double sceneHalfExtent = 0.6 * std::max( fullExtent.width(), fullExtent.height() );
584
585 minHalfExtent = std::min( 50., minHalfExtent );
586 maxHalfExtent = std::min( 100., maxHalfExtent );
587 const double smoothFactor = std::sin( mCameraController->pitch() / 90.0 * M_PI_2 );
588
589 // Using the scene extent to prevent the image from becoming too wide when zooming out
590 const double adjustedHalfExtent = std::min( 3.0 * ( minHalfExtent + smoothFactor * maxHalfExtent ), sceneHalfExtent );
591
592 const QgsRectangle overviewExtent = QgsRectangle::fromCenterAndSize( extentCenter2D, adjustedHalfExtent, adjustedHalfExtent );
593 const bool showFrustum = mMap.viewFrustumVisualizationEnabled();
594 mMapOverlayEntity->update( overviewExtent, extent2DAsPoints, mCameraController->yaw(), showFrustum );
595}
596
597void Qgs3DMapScene::createTerrain()
598{
599 if ( mTerrain )
600 {
601 mSceneEntities.removeOne( mTerrain );
602
603 delete mTerrain;
604 mTerrain = nullptr;
605 }
606
607 if ( mGlobe )
608 {
609 mSceneEntities.removeOne( mGlobe );
610
611 delete mGlobe;
612 mGlobe = nullptr;
613 }
614
615 if ( !mTerrainUpdateScheduled )
616 {
617 // defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
618 QTimer::singleShot( 0, this, &Qgs3DMapScene::createTerrainDeferred );
619 mTerrainUpdateScheduled = true;
620 setSceneState( Updating );
621 }
622 else
623 {
625 }
626}
627
628void Qgs3DMapScene::createTerrainDeferred()
629{
630 QgsChunkedEntity *terrainOrGlobe = nullptr;
631
632 if ( mMap.sceneMode() == Qgis::SceneMode::Globe && mMap.terrainRenderingEnabled() )
633 {
634 mGlobe = new QgsGlobeEntity( &mMap );
635 terrainOrGlobe = mGlobe;
636 }
637 else if ( mMap.sceneMode() == Qgis::SceneMode::Local && mMap.terrainRenderingEnabled() && mMap.terrainGenerator() )
638 {
639 double tile0width = mMap.terrainGenerator()->rootChunkExtent().width();
640 int maxZoomLevel = Qgs3DUtils::maxZoomLevel( tile0width, mMap.terrainSettings()->mapTileResolution(), mMap.terrainSettings()->maximumGroundError() );
641 const QgsBox3D rootBox3D = mMap.terrainGenerator()->rootChunkBox3D( mMap );
642 float rootError = mMap.terrainGenerator()->rootChunkError( mMap );
643 const QgsBox3D clippingBox3D( mMap.extent(), rootBox3D.zMinimum(), rootBox3D.zMaximum() );
644 mMap.terrainGenerator()->setupQuadtree( rootBox3D, rootError, maxZoomLevel, clippingBox3D );
645
646 mTerrain = new QgsTerrainEntity( &mMap );
647 terrainOrGlobe = mTerrain;
648 }
649
650 if ( terrainOrGlobe )
651 {
652 // The terrain is not added through addSceneEntity(), so we add the required QLayers here
653 QgsFrameGraph *frameGraph = mEngine->frameGraph();
654 terrainOrGlobe->addComponent( frameGraph->forwardRenderView().renderLayer() );
655 terrainOrGlobe->addComponent( frameGraph->shadowRenderView().entityCastingShadowsLayer() );
656
657 terrainOrGlobe->setParent( this );
658 terrainOrGlobe->setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );
659
660 mSceneEntities << terrainOrGlobe;
661
662 connect( terrainOrGlobe, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
663 connect( terrainOrGlobe, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity *entity ) {
664 // let's make sure that any entity we're about to show has the right scene origin set
665 const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
666 for ( QgsGeoTransform *transform : transforms )
667 {
668 transform->setOrigin( mMap.origin() );
669 }
670
671 // enable clipping on the terrain if necessary
672 handleClippingOnEntity( entity );
673 } );
674 }
675
676 // make sure that renderers for layers are re-created as well
677 const QList<QgsMapLayer *> layers = mMap.layers();
678 for ( QgsMapLayer *layer : layers )
679 {
680 // remove old entity - if any
681 removeLayerEntity( layer );
682
683 // add new entity - if any 3D renderer
684 addLayerEntity( layer );
685 }
686
688 onCameraChanged(); // force update of the new terrain
689 mTerrainUpdateScheduled = false;
690}
691
692void Qgs3DMapScene::onBackgroundColorChanged()
693{
694 mEngine->setClearColor( mMap.backgroundColor() );
695}
696
697void Qgs3DMapScene::updateLights()
698{
699 for ( Qt3DCore::QEntity *entity : std::as_const( mLightEntities ) )
700 entity->deleteLater();
701 mLightEntities.clear();
702
703 QgsFrameGraph *frameGraph = mEngine->frameGraph();
704 const QList<QgsLightSource *> newLights = mMap.lightSources();
705 for ( const QgsLightSource *source : newLights )
706 {
707 Qt3DCore::QEntity *entity = source->createEntity( mMap, this );
708 entity->addComponent( frameGraph->forwardRenderView().renderLayer() );
709 mLightEntities.append( entity );
710 }
711
712 onShadowSettingsChanged();
713}
714
715void Qgs3DMapScene::updateCameraLens()
716{
717 mEngine->camera()->lens()->setFieldOfView( mMap.fieldOfView() );
718 mEngine->camera()->lens()->setProjectionType( mMap.projectionType() );
719 onCameraChanged();
720}
721
722void Qgs3DMapScene::onLayerRenderer3DChanged()
723{
724 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
725 Q_ASSERT( layer );
726
727 // remove old entity - if any
728 removeLayerEntity( layer );
729
730 // add new entity - if any 3D renderer
731 addLayerEntity( layer );
732}
733
734void Qgs3DMapScene::onLayersChanged()
735{
736 QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
737 QList<QgsMapLayer *> layersAdded;
738 const QList<QgsMapLayer *> layers = mMap.layers();
739 for ( QgsMapLayer *layer : layers )
740 {
741 if ( !layersBefore.contains( layer ) )
742 {
743 layersAdded << layer;
744 }
745 else
746 {
747 layersBefore.remove( layer );
748 }
749 }
750
751 // what is left in layersBefore are layers that have been removed
752 for ( QgsMapLayer *layer : std::as_const( layersBefore ) )
753 {
754 removeLayerEntity( layer );
755 }
756
757 for ( QgsMapLayer *layer : std::as_const( layersAdded ) )
758 {
759 addLayerEntity( layer );
760 }
761}
762
764{
765 const QList<QgsMapLayer *> layers = mLayerEntities.keys();
766 for ( QgsMapLayer *layer : layers )
767 {
768 if ( QgsMapLayerTemporalProperties *temporalProperties = layer->temporalProperties() )
769 {
770 if ( temporalProperties->isActive() )
771 {
772 removeLayerEntity( layer );
773 addLayerEntity( layer );
774 }
775 }
776 }
777}
778
779void Qgs3DMapScene::addSceneEntity( Qgs3DMapSceneEntity *sceneNewEntity )
780{
781 Q_ASSERT( sceneNewEntity );
782
783 mSceneEntities.append( sceneNewEntity );
784
785 sceneNewEntity->setParent( this );
786
787 finalizeNewEntity( sceneNewEntity );
788
789 connect( sceneNewEntity, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity *entity ) {
790 finalizeNewEntity( entity );
791 // this ensures to update the near/far planes with the exact bounding box of the new entity.
792 updateCameraNearFarPlanes();
793 } );
794
795 connect( sceneNewEntity, &Qgs3DMapSceneEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
796
797 onCameraChanged(); // needed for chunked entities
798}
799
800void Qgs3DMapScene::removeSceneEntity( Qgs3DMapSceneEntity *sceneEntity )
801{
802 Q_ASSERT( sceneEntity );
803
804 mSceneEntities.removeOne( sceneEntity );
805
806 sceneEntity->deleteLater();
807}
808
809
810void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
811{
812 QgsAbstract3DRenderer *renderer = layer->renderer3D();
813 if ( renderer )
814 {
815 // Fix vector layer's renderer to make sure the renderer is pointing to its layer.
816 // It has happened before that renderer pointed to a different layer (probably after copying a style).
817 // This is a bit of a hack and it should be handled in QgsMapLayer::setRenderer3D() but in qgis_core
818 // the vector layer 3D renderer classes are not available.
819 if ( layer->type() == Qgis::LayerType::Vector && ( renderer->type() == "vector"_L1 || renderer->type() == "rulebased"_L1 || renderer->type() == "categorized"_L1 ) )
820 {
821 static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
822 if ( renderer->type() == "vector"_L1 )
823 {
824 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
825 if ( vlayer->geometryType() == Qgis::GeometryType::Point )
826 {
827 const QgsPoint3DSymbol *pointSymbol = static_cast<const QgsPoint3DSymbol *>( static_cast<QgsVectorLayer3DRenderer *>( renderer )->symbol() );
828 if ( pointSymbol->shape() == Qgis::Point3DShape::Model )
829 {
830 mModelVectorLayers.append( layer );
831 }
832 }
833 }
834 else if ( renderer->type() == "rulebased"_L1 )
835 {
836 const QgsRuleBased3DRenderer::RuleList rules = static_cast<QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
837 for ( auto rule : rules )
838 {
839 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( rule->symbol() );
840 if ( pointSymbol && pointSymbol->shape() == Qgis::Point3DShape::Model )
841 {
842 mModelVectorLayers.append( layer );
843 break;
844 }
845 }
846 }
847 else if ( renderer->type() == "categorized"_L1 )
848 {
849 const Qgs3DCategoryList categories = static_cast<QgsCategorized3DRenderer *>( renderer )->categories();
850 for ( const Qgs3DRendererCategory &category : categories )
851 {
852 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( category.symbol() );
853 if ( pointSymbol && pointSymbol->shape() == Qgis::Point3DShape::Model )
854 {
855 mModelVectorLayers.append( layer );
856 break;
857 }
858 }
859 }
860 }
861 else if ( layer->type() == Qgis::LayerType::Mesh && renderer->type() == "mesh"_L1 )
862 {
863 QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
864 meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
865
866 // Before entity creation, set the maximum texture size
867 // Not very clean, but for now, only place found in the workflow to do that simple
868 QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
869 sym->setMaximumTextureSize( maximumTextureSize() );
870 meshRenderer->setSymbol( sym );
871 }
872 else if ( layer->type() == Qgis::LayerType::PointCloud && renderer->type() == "pointcloud"_L1 )
873 {
874 QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
875 pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
876 }
877 else if ( layer->type() == Qgis::LayerType::TiledScene && renderer->type() == "tiledscene"_L1 )
878 {
879 QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
880 tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
881 }
882 else if ( layer->type() == Qgis::LayerType::Annotation && renderer->type() == "annotation"_L1 )
883 {
884 auto annotationLayerRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( renderer );
885 annotationLayerRenderer->setLayer( qobject_cast<QgsAnnotationLayer *>( layer ) );
886 }
887
888 Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
889 if ( newEntity )
890 {
891 mLayerEntities.insert( layer, newEntity );
892
893 if ( Qgs3DMapSceneEntity *sceneNewEntity = qobject_cast<Qgs3DMapSceneEntity *>( newEntity ) )
894 {
895 // also sets this scene as the entity's parent and finalizes it
896 addSceneEntity( sceneNewEntity );
897 }
898 else
899 {
900 newEntity->setParent( this );
901 finalizeNewEntity( newEntity );
902 }
903 }
904 }
905
906 connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
907
908 if ( layer->type() == Qgis::LayerType::Vector )
909 {
910 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
911 connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
912 connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
913 connect( vlayer, &QgsVectorLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
914 }
915
916 if ( layer->type() == Qgis::LayerType::Mesh )
917 {
918 connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
919 }
920
921 if ( layer->type() == Qgis::LayerType::PointCloud )
922 {
923 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
924 connect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
925 connect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
926 }
927}
928
929void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
930{
931 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
932
933 if ( Qgs3DMapSceneEntity *sceneEntity = qobject_cast<Qgs3DMapSceneEntity *>( entity ) )
934 {
935 // also schedules the entity for deletion
936 removeSceneEntity( sceneEntity );
937 }
938 else
939 {
940 if ( entity )
941 entity->deleteLater();
942 }
943
944 disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
945
946 if ( layer->type() == Qgis::LayerType::Vector )
947 {
948 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
949 disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
950 disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
951 disconnect( vlayer, &QgsVectorLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
952 mModelVectorLayers.removeAll( layer );
953 }
954
955 if ( layer->type() == Qgis::LayerType::Mesh )
956 {
957 disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
958 }
959
960 if ( layer->type() == Qgis::LayerType::PointCloud )
961 {
962 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
963 disconnect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
964 disconnect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
965 disconnect( pclayer, &QgsPointCloudLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
966 }
967}
968
969void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
970{
971 // let's make sure that any entity we're about to show has the right scene origin set
972 const QList<QgsGeoTransform *> transforms = newEntity->findChildren<QgsGeoTransform *>();
973 for ( QgsGeoTransform *transform : transforms )
974 {
975 transform->setOrigin( mMap.origin() );
976 }
977
978 // set clip planes on the new entity if necessary
979 handleClippingOnEntity( newEntity );
980
981 // this is probably not the best place for material-specific configuration,
982 // maybe this could be more generalized when other materials need some specific treatment
983 const QList<Qt3DRender::QMaterial *> childMaterials = newEntity->findChildren<Qt3DRender::QMaterial *>();
984
985 // first pass over materials -- setup viewport sizing logic for ALL materials that require it
986 // (this needs to apply to all materials, includes those for highlight entities)
987 for ( Qt3DRender::QMaterial *material : childMaterials )
988 {
989 if ( auto lm = qobject_cast< QgsLineMaterial * >( material ) )
990 {
991 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, lm, [lm, this] { lm->setViewportSize( mEngine->size() ); } );
992 lm->setViewportSize( mEngine->size() );
993 }
994 else if ( auto bm = qobject_cast< QgsPoint3DBillboardMaterial * >( material ) )
995 {
996 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, bm, [bm, this] { bm->setViewportSize( mEngine->size() ); } );
997 bm->setViewportSize( mEngine->size() );
998 }
999 }
1000
1001 QgsFrameGraph *frameGraph = mEngine->frameGraph();
1002
1003 // Here we check if the entity should not be rendered in the forward render view
1004 // For example highlight entities should only be rendered in the highlights render view, so we check for attached QLayers
1005 const QVector<Qt3DRender::QLayer *> layers = newEntity->componentsOfType<Qt3DRender::QLayer>();
1006 if ( layers.contains( frameGraph->highlightsRenderView().highlightsLayer() ) )
1007 return;
1008
1009 // Add the required QLayers to the entity
1010 newEntity->addComponent( frameGraph->forwardRenderView().renderLayer() );
1011
1012 Qt3DRender::QLayer *shadowCastingEntityLayer = frameGraph->shadowRenderView().entityCastingShadowsLayer();
1013 Qt3DRender::QLayer *transparentLayer = frameGraph->forwardRenderView().transparentObjectLayer();
1014 for ( Qt3DRender::QMaterial *material : childMaterials )
1015 {
1016 // find the specific entity this material belongs to -- it may be a child of the parent entity
1017 // being finalized
1018 auto materialEntity = qobject_cast<Qt3DCore::QEntity *>( material->parent() );
1019 if ( !materialEntity )
1020 continue;
1021
1022 bool materialCastsShadows = false;
1023 if ( auto qgsMaterial = qobject_cast< QgsMaterial * >( material ) )
1024 {
1025 if ( qgsMaterial->castsShadows() )
1026 {
1027 materialCastsShadows = true;
1028 }
1029 }
1030 else
1031 {
1032 // for non QgsMaterial materials we assume they need shadows
1033 materialCastsShadows = true;
1034 }
1035
1036 if ( materialCastsShadows && !materialEntity->components().contains( shadowCastingEntityLayer ) )
1037 {
1038 materialEntity->addComponent( shadowCastingEntityLayer );
1039 }
1040
1041 // Finalize adding the 3D transparent objects by adding the layer components to the entities
1042
1043 // This handles the phong material without data defined properties.
1044 if ( auto ph = qobject_cast<Qt3DExtras::QDiffuseSpecularMaterial *>( material ) )
1045 {
1046 if ( ph->diffuse().value<QColor>().alphaF() != 1.0f )
1047 {
1048 if ( !materialEntity->components().contains( transparentLayer ) )
1049 {
1050 materialEntity->addComponent( transparentLayer );
1051 }
1052 }
1053 }
1054#if 0
1055 /*
1056 * Adds transparency layer to QgsPoint3DBillboardMaterial entities,
1057 * so that they get rendered in the transparent pipeline instead
1058 * of the opaque pipeline. Permits semi-opaque pixel rendering.
1059 *
1060 * Pros: nicely smoothed billboard symbol rendering, without harsh
1061 * aliased edges. Billboard symbols can use semi-transparent colors.
1062 *
1063 * Cons: Introduces ordering issues for billboards, where billboards
1064 * which should be shown behind others will appear in front from
1065 * some angles (i.e. the same issue as we get for 3d polygon objects
1066 * with transparency)
1067 *
1068 * Consider enabling if/when we have some workaround for the stacking issue,
1069 * eg CPU based sorting on camera movement...
1070 */
1071 else if ( auto billboardMaterial = qobject_cast<QgsPoint3DBillboardMaterial *>( material ) )
1072 {
1073 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( billboardMaterial->parent() );
1074 if ( !materialEntity->components().contains( transparentLayer ) )
1075 {
1076 materialEntity->addComponent( transparentLayer );
1077 }
1078 }
1079#endif
1080 else
1081 {
1082 // This handles the phong material with data defined properties, the textured case and point (instanced) symbols.
1083 if ( Qt3DRender::QEffect *effect = material->effect() )
1084 {
1085 const QVector<Qt3DRender::QParameter *> parameters = effect->parameters();
1086 for ( const Qt3DRender::QParameter *parameter : parameters )
1087 {
1088 if ( parameter->name() == "opacity" && parameter->value() != 1.0f )
1089 {
1090 if ( !materialEntity->components().contains( transparentLayer ) )
1091 {
1092 materialEntity->addComponent( transparentLayer );
1093 }
1094 break;
1095 }
1096 }
1097 }
1098 }
1099 }
1100
1101 if ( childMaterials.empty() )
1102 {
1103 // handle shadows for entities without materials -- eg point models
1104 newEntity->addComponent( shadowCastingEntityLayer );
1105 }
1106}
1107
1108int Qgs3DMapScene::maximumTextureSize() const
1109{
1110 QSurface *surface = mEngine->surface();
1111 QOpenGLContext context;
1112 context.create();
1113 bool success = context.makeCurrent( surface );
1114
1115 if ( success )
1116 {
1117 QOpenGLFunctions openglFunctions = QOpenGLFunctions( &context );
1118
1119 GLint size;
1120 openglFunctions.initializeOpenGLFunctions();
1121 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
1122 return int( size );
1123 }
1124 else
1125 {
1126 return 4096; //we can't have a context to defined the max texture size, we use this reasonable value
1127 }
1128}
1129
1130void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
1131{
1132 mEntityCameraViewCenter = new Qt3DCore::QEntity;
1133
1134 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
1135 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
1136 connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera] { trCameraViewCenter->setTranslation( camera->viewCenter() ); } );
1137
1138 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
1139 materialCameraViewCenter->setAmbient( Qt::red );
1140 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
1141
1142 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
1143 rendererCameraViewCenter->setRadius( 10 );
1144 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
1145
1146 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
1147 mEntityCameraViewCenter->setParent( this );
1148
1149 connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this] { mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() ); } );
1150}
1151
1152void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
1153{
1154 if ( mSceneState == state )
1155 return;
1156 mSceneState = state;
1157 emit sceneStateChanged();
1158}
1159
1160void Qgs3DMapScene::updateSceneState()
1161{
1162 if ( mTerrainUpdateScheduled )
1163 {
1164 setSceneState( Updating );
1165 return;
1166 }
1167
1168 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
1169 {
1170 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
1171 {
1172 setSceneState( Updating );
1173 return;
1174 }
1175 }
1176
1177 setSceneState( Ready );
1178}
1179
1180void Qgs3DMapScene::onBackgroundSettingsChanged()
1181{
1182 if ( mBackgroundEntity )
1183 {
1184 mBackgroundEntity->deleteLater();
1185 mBackgroundEntity = nullptr;
1186 }
1187
1188 const QgsAbstract3DMapBackgroundSettings *settings = mMap.backgroundSettings();
1189 if ( !settings )
1190 return;
1191
1192 QgsFrameGraph *frameGraph = mEngine->frameGraph();
1193
1195 {
1196 const QgsSkyboxSettings *skyboxSettings = dynamic_cast<const QgsSkyboxSettings *>( settings );
1197 const QMap<QString, QString> faces = skyboxSettings->cubeMapFacesPaths();
1198 mBackgroundEntity = new QgsCubeFacesSkyboxEntity( skyboxSettings->cubeMapping(), faces[u"posX"_s], faces[u"posY"_s], faces[u"posZ"_s], faces[u"negX"_s], faces[u"negY"_s], faces[u"negZ"_s], this );
1199 }
1201 {
1202 const QgsFixedGradientBackgroundSettings *gradientSettings = dynamic_cast<const QgsFixedGradientBackgroundSettings *>( settings );
1203 mBackgroundEntity = new QgsGradientBackgroundEntity( gradientSettings->topColor(), gradientSettings->bottomColor(), this );
1204 }
1205
1206 mBackgroundEntity->addComponent( frameGraph->forwardRenderView().backgroundLayer() );
1207 mBackgroundEntity->addComponent( frameGraph->forwardRenderView().renderLayer() );
1208}
1209
1210void Qgs3DMapScene::onShadowSettingsChanged()
1211{
1212 mEngine->frameGraph()->updateShadowSettings( mMap.shadowSettings(), mMap.lightSources() );
1213}
1214
1215void Qgs3DMapScene::onAmbientOcclusionSettingsChanged()
1216{
1217 mEngine->frameGraph()->updateAmbientOcclusionSettings( mMap.ambientOcclusionSettings() );
1218}
1219
1220void Qgs3DMapScene::onBloomSettingsChanged()
1221{
1222 mEngine->frameGraph()->updateBloomSettings( mMap.bloomSettings() );
1223}
1224
1225void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1226{
1227 mEngine->frameGraph()->updateDebugDepthMapSettings( mMap );
1228}
1229
1230void Qgs3DMapScene::onDebugOverlayEnabledChanged()
1231{
1232 mEngine->frameGraph()->setDebugOverlayEnabled( mMap.isDebugOverlayEnabled() );
1233 mEngine->renderSettings()->setRenderPolicy( mMap.isDebugOverlayEnabled() ? Qt3DRender::QRenderSettings::Always : Qt3DRender::QRenderSettings::OnDemand );
1234}
1235
1236void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1237{
1238 mEngine->frameGraph()->updateEyeDomeSettings( mMap );
1239}
1240
1241void Qgs3DMapScene::onMsaaEnabledChanged()
1242{
1243 mEngine->frameGraph()->setMsaaEnabled( mMap.isMsaaEnabled() );
1244}
1245
1246void Qgs3DMapScene::onShowMapOverlayChanged()
1247{
1248 const QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
1249 update2DMapOverlay( extent2D );
1250}
1251
1252void Qgs3DMapScene::onCameraMovementSpeedChanged()
1253{
1254 mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1255}
1256
1257void Qgs3DMapScene::onCameraNavigationModeChanged()
1258{
1259 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
1260}
1261
1263{
1264 QVector<QString> notParsedLayers;
1265 Qgs3DSceneExporter exporter;
1266
1267 exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1268 exporter.setSmoothEdges( exportSettings.smoothEdges() );
1269 exporter.setExportNormals( exportSettings.exportNormals() );
1270 exporter.setExportTextures( exportSettings.exportTextures() );
1271 exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1272 exporter.setScale( exportSettings.scale() );
1273 exporter.setTerrainExportEnabled( exportSettings.terrainExportEnabled() );
1274
1275 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1276 {
1277 QgsMapLayer *layer = it.key();
1278 Qt3DCore::QEntity *rootEntity = it.value();
1279 Qgis::LayerType layerType = layer->type();
1280 switch ( layerType )
1281 {
1283 if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1284 notParsedLayers.push_back( layer->name() );
1285 break;
1294 notParsedLayers.push_back( layer->name() );
1295 break;
1296 }
1297 }
1298
1299 if ( mTerrain )
1300 exporter.parseTerrain( mTerrain, "Terrain" );
1301
1302 const bool sceneSaved = exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath(), exportSettings.exportFormat() );
1303 if ( !sceneSaved )
1304 {
1305 return false;
1306 }
1307
1308 if ( !notParsedLayers.empty() )
1309 {
1310 QString message = tr( "The following layers were not exported:" ) + "\n";
1311 for ( const QString &layerName : notParsedLayers )
1312 message += layerName + "\n";
1313 QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, Qgis::StringFormat::PlainText );
1314 }
1315
1316 return true;
1317}
1318
1319QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1320{
1321 QVector<const QgsChunkNode *> chunks;
1322 if ( !mLayerEntities.contains( layer ) )
1323 return chunks;
1324 if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[layer] ) )
1325 {
1326 const QList<QgsChunkNode *> activeNodes = c->activeNodes();
1327 for ( QgsChunkNode *n : activeNodes )
1328 chunks.push_back( n );
1329 }
1330 return chunks;
1331}
1332
1334{
1335 return mMap.extent();
1336}
1337
1338QgsDoubleRange Qgs3DMapScene::elevationRange( const bool ignoreTerrain ) const
1339{
1340 double zMin = std::numeric_limits<double>::max();
1341 double zMax = std::numeric_limits<double>::lowest();
1342 if ( mMap.terrainRenderingEnabled() && mTerrain && !ignoreTerrain )
1343 {
1344 const QgsBox3D box3D = mTerrain->rootNode()->box3D();
1345 zMin = std::min( zMin, box3D.zMinimum() );
1346 zMax = std::max( zMax, box3D.zMaximum() );
1347 }
1348
1349 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); it++ )
1350 {
1351 QgsMapLayer *layer = it.key();
1352 switch ( layer->type() )
1353 {
1355 {
1356 QgsPointCloudLayer *pcl = qobject_cast<QgsPointCloudLayer *>( layer );
1357 QgsDoubleRange zRange = pcl->elevationProperties()->calculateZRange( pcl );
1358 zMin = std::min( zMin, zRange.lower() );
1359 zMax = std::max( zMax, zRange.upper() );
1360 break;
1361 }
1363 {
1364 QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( layer );
1365 QgsAbstract3DRenderer *renderer3D = meshLayer->renderer3D();
1366 if ( renderer3D )
1367 {
1368 QgsMeshLayer3DRenderer *meshLayerRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer3D );
1369 const int verticalGroupDatasetIndex = meshLayerRenderer->symbol()->verticalDatasetGroupIndex();
1370 const QgsMeshDatasetGroupMetadata verticalGroupMetadata = meshLayer->datasetGroupMetadata( verticalGroupDatasetIndex );
1371 const double verticalScale = meshLayerRenderer->symbol()->verticalScale();
1372 zMin = std::min( zMin, verticalGroupMetadata.minimum() * verticalScale );
1373 zMax = std::max( zMax, verticalGroupMetadata.maximum() * verticalScale );
1374 }
1375 break;
1376 }
1378 {
1379 QgsTiledSceneLayer *sceneLayer = qobject_cast<QgsTiledSceneLayer *>( layer );
1380 const QgsDoubleRange zRange = sceneLayer->elevationProperties()->calculateZRange( sceneLayer );
1381 if ( !zRange.isInfinite() && !zRange.isEmpty() )
1382 {
1383 zMin = std::min( zMin, zRange.lower() );
1384 zMax = std::max( zMax, zRange.upper() );
1385 }
1386 break;
1387 }
1394 break;
1395 }
1396 }
1397 const QgsDoubleRange zRange( std::min( zMin, std::numeric_limits<double>::max() ), std::max( zMax, std::numeric_limits<double>::lowest() ) );
1398 return zRange.isEmpty() ? QgsDoubleRange() : zRange;
1399}
1400
1401QMap<QString, Qgs3DMapScene *> Qgs3DMapScene::openScenes()
1402{
1403 return sOpenScenesFunction();
1404}
1405
1406void Qgs3DMapScene::addCameraRotationCenterEntity( QgsCameraController *controller )
1407{
1408 mEntityRotationCenter = new Qt3DCore::QEntity;
1409
1410 Qt3DCore::QTransform *trRotationCenter = new Qt3DCore::QTransform;
1411 mEntityRotationCenter->addComponent( trRotationCenter );
1412 Qt3DExtras::QPhongMaterial *materialRotationCenter = new Qt3DExtras::QPhongMaterial;
1413 materialRotationCenter->setAmbient( Qt::blue );
1414 mEntityRotationCenter->addComponent( materialRotationCenter );
1415 Qt3DExtras::QSphereMesh *rendererRotationCenter = new Qt3DExtras::QSphereMesh;
1416 rendererRotationCenter->setRadius( 10 );
1417 mEntityRotationCenter->addComponent( rendererRotationCenter );
1418 mEntityRotationCenter->setEnabled( false );
1419 mEntityRotationCenter->setParent( this );
1420
1421 connect( controller, &QgsCameraController::cameraRotationCenterChanged, this, [trRotationCenter]( QVector3D center ) { trRotationCenter->setTranslation( center ); } );
1422
1423 connect( &mMap, &Qgs3DMapSettings::showCameraRotationCenterChanged, this, [this] { mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() ); } );
1424}
1425
1426void Qgs3DMapScene::on3DAxisSettingsChanged()
1427{
1428 if ( m3DAxis )
1429 {
1430 m3DAxis->onAxisSettingsChanged();
1431 }
1432 else
1433 {
1434 if ( QgsWindow3DEngine *engine = dynamic_cast<QgsWindow3DEngine *>( mEngine ) )
1435 {
1436 m3DAxis = new Qgs3DAxis( static_cast<Qgs3DMapCanvas *>( engine->window() ), engine->root(), this, mCameraController, &mMap );
1437 }
1438 }
1439}
1440
1441void Qgs3DMapScene::onOriginChanged()
1442{
1443 const QList<QgsGeoTransform *> geoTransforms = findChildren<QgsGeoTransform *>();
1444 for ( QgsGeoTransform *transform : geoTransforms )
1445 {
1446 transform->setOrigin( mMap.origin() );
1447 }
1448
1449 const QList<QgsGeoTransform *> rubberBandGeoTransforms = mEngine->frameGraph()->rubberBandsRootEntity()->findChildren<QgsGeoTransform *>();
1450 for ( QgsGeoTransform *transform : rubberBandGeoTransforms )
1451 {
1452 transform->setOrigin( mMap.origin() );
1453 }
1454
1455 const QgsVector3D oldOrigin = mCameraController->origin();
1456 mCameraController->setOrigin( mMap.origin() );
1457
1458 if ( !mClipPlanesEquations.isEmpty() )
1459 {
1460 // how the math works - for a plane defined as (a,b,c,d), only "d" changes when
1461 // moving the origin - the plane normal vector (a,b,c) stays the same.
1462 // - line equation for old shift: a * (x - x0) + b * (y - y0) + c * (z - z0) + d0 = 0
1463 // - line equation for new shift: a * (x - x1) + b * (y - y1) + c * (z - z1) + d1 = 0
1464 // - we solve for d1:
1465 // d1 = a * (x1 - x0) + b * (y1 - y0) + c * (z1 - z0) + d0
1466
1467 QList<QVector4D> newPlanes;
1468 QgsVector3D originShift = mMap.origin() - oldOrigin;
1469 for ( QVector4D plane : std::as_const( mClipPlanesEquations ) )
1470 {
1471 plane.setW( originShift.x() * plane.x() + originShift.y() * plane.y() + originShift.z() * plane.z() + plane.w() );
1472 newPlanes.append( plane );
1473 }
1474 enableClipping( newPlanes );
1475 }
1476}
1477
1478void Qgs3DMapScene::handleClippingOnEntity( QEntity *entity ) const
1479{
1480 if ( mClipPlanesEquations.isEmpty() ) // no clip plane equations, disable clipping
1481 {
1482 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1483 {
1484 material->disableClipping();
1485 }
1486 }
1487 else // enable clipping
1488 {
1489 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1490 {
1491 material->enableClipping( mClipPlanesEquations );
1492 }
1493 }
1494
1495 // recursive call
1496 // enable or disable clipping on the children accordingly
1497 for ( QObject *child : entity->children() )
1498 {
1499 Qt3DCore::QEntity *childEntity = qobject_cast<Qt3DCore::QEntity *>( child );
1500 if ( childEntity )
1501 {
1502 handleClippingOnEntity( childEntity );
1503 }
1504 }
1505}
1506
1507void Qgs3DMapScene::handleClippingOnAllEntities() const
1508{
1509 // Need to loop mLayerEntities instead of mSceneEntities to handle entities
1510 // which do no inherit from Qgs3DMapSceneEntity. For example, mesh entities.
1511 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1512 {
1513 handleClippingOnEntity( it.value() );
1514 }
1515 if ( mTerrain )
1516 {
1517 handleClippingOnEntity( mTerrain );
1518 }
1519 if ( mGlobe )
1520 {
1521 handleClippingOnEntity( mGlobe );
1522 }
1523}
1524
1526{
1527 if ( clipPlaneEquations.size() > mMaxClipPlanes )
1528 {
1529 QgsDebugMsgLevel( u"Qgs3DMapScene::enableClipping: it is not possible to use more than %1 clipping planes."_s.arg( mMaxClipPlanes ), 2 );
1530 }
1531 mClipPlanesEquations = clipPlaneEquations.mid( 0, mMaxClipPlanes );
1532
1533 // enable the clip planes on the framegraph
1534 mEngine->frameGraph()->addClipPlanes( clipPlaneEquations.size() );
1535
1536 // Enable the clip planes for the material of each entity.
1537 handleClippingOnAllEntities();
1538}
1539
1541{
1542 mClipPlanesEquations.clear();
1543
1544 // disable the clip planes on the framegraph
1545 mEngine->frameGraph()->removeClipPlanes();
1546
1547 // Disable the clip planes for the material of each entity.
1548 handleClippingOnAllEntities();
1549}
1550
1551void Qgs3DMapScene::onStopUpdatesChanged()
1552{
1553 mSceneUpdatesEnabled = !mMap.stopUpdates();
1554}
1555
1556void Qgs3DMapScene::schedule2DMapOverlayUpdate()
1557{
1558 // Start the overlay update timer if overlay is active and not already running
1559 if ( mMap.is2DMapOverlayEnabled() && mOverlayUpdateTimer && !mOverlayUpdateTimer->isActive() )
1560 {
1561 mOverlayUpdateTimer->start();
1562 }
1563}
1564
1565void Qgs3DMapScene::applyPendingOverlayUpdate()
1566{
1567 if ( mMap.is2DMapOverlayEnabled() )
1568 {
1569 const QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
1570 update2DMapOverlay( extent2D );
1571 }
1572}
@ DistinctTextureSkybox
Skybox with 6 distinct textures for different faces.
Definition qgis.h:4418
@ FixedGradientBackground
Two color gradient, fixed in place.
Definition qgis.h:4417
@ Model
Model.
Definition qgis.h:4328
@ Point
Points.
Definition qgis.h:380
@ PlainText
Text message.
Definition qgis.h:176
LayerType
Types of layers that can be added to a map.
Definition qgis.h:206
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
@ Globe
Scene is represented as a globe using a geocentric CRS.
Definition qgis.h:4458
@ Local
Local scene based on a projected CRS.
Definition qgis.h:4457
Manages the various settings the user can choose from when exporting a 3D scene.
bool exportNormals() const
Returns whether normals will be exported.
int terrrainResolution() const
Returns the terrain resolution.
QString sceneFolderPath() const
Returns the scene folder path.
float scale() const
Returns the scale of the exported model.
int terrainTextureResolution() const
Returns the terrain texture resolution.
bool terrainExportEnabled() const
Returns whether terrain export is enabled.
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.
Qgis::Export3DSceneFormat exportFormat() const
Returns the export format for the 3D scene.
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
QList< QVector4D > clipPlaneEquations() const
Returns list of clipping planes if clipping is enabled, otherwise an empty list.
static std::function< QMap< QString, Qgs3DMapScene * >()> sOpenScenesFunction
Static function for returning open 3D map scenes.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes.
void setViewFrom2DExtent(const QgsRectangle &extent)
Resets camera view to show the extent extent (top view).
void disableClipping()
Disables OpenGL clipping.
QVector< const QgsChunkNode * > getLayerActiveChunkNodes(QgsMapLayer *layer) SIP_SKIP
Returns the active chunk nodes of layer.
void gpuMemoryLimitReached()
Emitted when one of the entities reaches its GPU memory limit and it is not possible to lower the GPU...
static Q_DECL_DEPRECATED QMap< QString, Qgs3DMapScene * > openScenes() SIP_DEPRECATED
Returns a map of 3D map scenes (by name) open in the QGIS application.
QgsCameraController * cameraController() const
Returns camera controller.
SceneState
Enumeration of possible states of the 3D scene.
@ Ready
The scene is fully loaded/updated.
@ Updating
The scene is still being loaded/updated.
bool exportScene(const Qgs3DMapExportSettings &exportSettings)
Exports the scene according to the scene export settings Returns false if the operation failed.
int totalPendingJobsCount() const
Returns number of pending jobs for all chunked entities.
QList< QgsMapLayer * > layers() const SIP_SKIP
Returns the layers that contain chunked entities.
void addSceneEntity(Qgs3DMapSceneEntity *entity) SIP_SKIP
Adds a 3D map scene entity to the scene.
void updateTemporal()
Updates the temporale entities.
void totalPendingJobsCountChanged()
Emitted when the total number of pending jobs changes.
Qgs3DMapScene(Qgs3DMapSettings &map, QgsAbstract3DEngine *engine) SIP_SKIP
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is activated or deactivated.
void removeSceneEntity(Qgs3DMapSceneEntity *entity) SIP_SKIP
Removes a 3D scene entity for the scene.
QgsDoubleRange elevationRange(bool ignoreTerrain=false) const
Returns the scene's elevation range.
QgsRectangle sceneExtent() const
Returns the scene extent in the map's CRS.
void sceneStateChanged()
Emitted when the scene's state has changed.
QgsAbstract3DEngine * engine() const SIP_SKIP
Returns the abstract 3D engine.
QVector< QgsPointXY > viewFrustum2DExtent() const
Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid.
void enableClipping(const QList< QVector4D > &clipPlaneEquations)
Enables OpenGL clipping based on the planes equations defined in clipPlaneEquations.
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
void viewZoomFull()
Resets camera view to show the whole scene (top view).
double worldSpaceError(double epsilon, double distance) const
Given screen error (in pixels) and distance from camera (in 3D world coordinates),...
Definition of the world.
void extentChanged()
Emitted when the 3d view's 2d extent has changed.
void originChanged()
Emitted when the world's origin point has been shifted.
void eyeDomeLightingDistanceChanged()
Emitted when the eye dome lighting distance has changed.
void terrainShadingChanged()
Emitted when terrain shading enabled flag or terrain shading material has changed.
void bloomSettingsChanged()
Emitted when the bloom lighting effect settings are changed.
void debugDepthMapSettingsChanged()
Emitted when depth map debugging has changed.
void backgroundSettingsChanged()
Emitted when background settings are changed.
void backgroundColorChanged()
Emitted when the background color has changed.
void showCameraRotationCenterChanged()
Emitted when the flag whether camera's rotation center is shown has changed.
void cameraNavigationModeChanged()
Emitted when the camera navigation mode was changed.
void shadowSettingsChanged()
Emitted when shadow rendering settings are changed.
void show2DMapOverlayChanged()
Emitted when the 2D map overlay is enabled or disabled.
bool stopUpdates() const
Returns whether the scene updates on camera movement.
void eyeDomeLightingEnabledChanged()
Emitted when the flag whether eye dome lighting is used has changed.
void debugOverlayEnabledChanged(bool debugOverlayEnabled)
Emitted when the debug overaly is enabled or disabled.
void setOrigin(const QgsVector3D &origin)
Sets coordinates in map CRS at which our 3D world has origin (0,0,0).
void msaaEnabledChanged()
Emitted when the MSAA enabled flag has changed.
void projectionTypeChanged()
Emitted when the camera lens projection type changes.
void stopUpdatesChanged()
Emitted when the flag whether to keep updating scene has changed.
void lightSourcesChanged()
Emitted when any of the light source settings in the map changes.
void showLightSourceOriginsChanged()
Emitted when the flag whether light source origins are shown has changed.
void terrainSettingsChanged()
Emitted when the terrain settings are changed.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is enabled or disabled.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
void viewFrustumVisualizationEnabledChanged()
Emitted when the camera's view frustum visualization on the main 2D map canvas is enabled or disabled...
void ambientOcclusionSettingsChanged()
Emitted when ambient occlusion rendering settings are changed.
void layersChanged()
Emitted when the list of map layers for 3d rendering has changed.
void eyeDomeLightingStrengthChanged()
Emitted when the eye dome lighting strength has changed.
void cameraMovementSpeedChanged()
Emitted when the camera movement speed was changed.
void fieldOfViewChanged()
Emitted when the camera lens field of view changes.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
void showCameraViewCenterChanged()
Emitted when the flag whether camera's view center is shown has changed.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0).
Entity that handles the exporting of 3D scenes.
void setExportTextures(bool exportTextures)
Sets whether the textures will be exported.
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
void setTerrainResolution(int resolution)
Sets the terrain resolution.
void setTerrainTextureResolution(int resolution)
Sets the terrain texture resolution.
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
void setScale(float scale)
Sets the scale of the exported 3D model.
bool save(QString sceneName, QString sceneFolderPath, const Qgis::Export3DSceneFormat &exportFormat=Qgis::Export3DSceneFormat::Obj, int precision=6) const
Saves the scene to a file Returns false if the operation failed.
void setExportNormals(bool exportNormals)
Sets whether the normals will be exported.
void setSmoothEdges(bool smoothEdges)
Sets whether the triangles will look smooth.
void setTerrainExportEnabled(bool enabled)
Sets whether terrain export is enabled.
static int maxZoomLevel(double tile0width, double tileResolution, double maxError)
Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom le...
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
static void computeBoundingBoxNearFarPlanes(const QgsAABB &bbox, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar)
This routine computes nearPlane farPlane from the closest and farthest corners point of bounding box ...
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
static int openGlMaxClipPlanes(QSurface *surface)
Gets the maximum number of clip planes that can be used.
Base class for 3D engine implementation.
void sizeChanged()
Emitted after a call to setSize().
virtual Qt3DRender::QCamera * camera()=0
Returns pointer to the engine's camera entity.
virtual Qgis::Map3DBackgroundType type() const =0
Returns the unique type for this background settings class.
Base class for all renderers that participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass).
virtual Qt3DCore::QEntity * createEntity(Qgs3DMapSettings *map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
virtual void setEnabled(bool enable)
Enable or disable via enable the render view sub tree.
Base class for 3D renderers that are based on vector layers.
static QgsSourceCache * sourceCache()
Returns the application's source cache, used for caching embedded and remote source strings as local ...
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
Object that controls camera movement based on user input.
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
void cameraChanged()
Emitted when camera has been updated.
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
QgsRange which stores a range of double values.
Definition qgsrange.h:217
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition qgsrange.h:266
QColor topColor() const
Returns the color at the top of the gradient.
QColor bottomColor() const
Returns the color at the bottom of the gradient.
Qt3DRender::QLayer * renderLayer()
Returns a layer object used to indicate that the object is transparent.
Qt3DRender::QLayer * transparentObjectLayer()
Returns a layer object used to indicate that the object is transparent.
Qt3DRender::QLayer * backgroundLayer()
Returns a layer object used for skybox and background gradient entities.
void updateShadowSettings(const QgsShadowSettings &shadowSettings, const QList< QgsLightSource * > &lightSources)
Updates shadow bias, light and texture size according to shadowSettings and lightSources.
QgsHighlightsRenderView & highlightsRenderView()
Returns the highlights renderview, used for rendering highlight overlays of identified features.
QgsForwardRenderView & forwardRenderView()
Returns forward renderview.
QgsOverlayTextureRenderView & overlayTextureRenderView()
Returns overlay texture renderview.
QgsShadowRenderView & shadowRenderView()
Returns shadow renderview.
Qt3DRender::QLayer * highlightsLayer()
Returns a layer that should be attached to entities meant to be rendered by QgsHighlightsRenderView.
virtual QgsDoubleRange calculateZRange(QgsMapLayer *layer) const
Attempts to calculate the overall elevation or z range for the specified layer, using the settings de...
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
Qgis::LayerType type
Definition qgsmaplayer.h:93
void rendererChanged()
Signal emitted when renderer is changed.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
void layerModified()
Emitted when modifications has been done on layer.
double verticalScale() const
Returns mesh vertical scale.
int verticalDatasetGroupIndex() const
Returns the index of the dataset group that will be used to render the vertical component of the 3D m...
void setMaximumTextureSize(int maximumTextureSize)
Sets the maximum texture size supported by the hardware Used to store the GL_MAX_TEXTURE_SIZE value t...
QgsMesh3DSymbol * clone() const override SIP_FACTORY
Returns a new instance of the symbol with the same settings.
A collection of dataset group metadata such as whether the data is vector or scalar,...
double minimum() const
Returns minimum scalar value/vector magnitude present for whole dataset group.
double maximum() const
Returns maximum scalar value/vector magnitude present for whole dataset group.
3D renderer that renders all mesh triangles of a mesh layer.
void setSymbol(QgsMesh3DSymbol *symbol)
Sets 3D symbol associated with the renderer.
const QgsMesh3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
void setLayer(QgsMeshLayer *layer)
Sets vector layer associated with the renderer.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
3D symbol that draws point geometries as 3D objects using one of the predefined shapes.
Qgis::Point3DShape shape() const
Returns 3D shape for points.
QVariant shapeProperty(const QString &property) const
Returns the value for a specific shape property.
void setLayer(QgsPointCloudLayer *layer)
Sets point cloud layer associated with the renderer.
Represents a map layer supporting display of point clouds.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
T lower() const
Returns the lower bound of the range.
Definition qgsrange.h:79
T upper() const
Returns the upper bound of the range.
Definition qgsrange.h:86
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:126
A representation of a ray in 3D.
Definition qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition qgsray3d.h:43
QVector3D direction() const
Returns the direction of the ray see setDirection().
Definition qgsray3d.h:49
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
QgsPointXY center
A child rule for a QgsRuleBased3DRenderer.
Rule-based 3D renderer.
QList< QgsRuleBased3DRenderer::Rule * > RuleList
Qt3DRender::QLayer * entityCastingShadowsLayer() const
Returns the layer to be used by entities to be included in this renderview.
QMap< QString, QString > cubeMapFacesPaths() const
Returns a map containing the path of each texture specified by the user.
Qgis::SkyboxCubeMapping cubeMapping() const
Returns the cube face mapping scheme.
void remoteSourceFetched(const QString &url)
Emitted when the cache has finished retrieving a 3D model from a remote url.
void setLayer(QgsTiledSceneLayer *layer)
Sets tiled scene layer associated with the renderer.
Represents a map layer supporting display of tiled scene objects.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
QString toString(int precision=17) const
Returns a string representation of the 3D vector.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
3D renderer that renders all features of a vector layer with the same 3D symbol.
const QgsAbstract3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Represents a vector layer which manages a vector based dataset.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference).
Definition qgis.h:7233
QList< Qgs3DRendererCategory > Qgs3DCategoryList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59