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