QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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"
42#include "qgslightsource.h"
43#include "qgslinematerial_p.h"
44#include "qgslogger.h"
47#include "qgsmaterial.h"
48#include "qgsmeshlayer.h"
50#include "qgsmessageoutput.h"
53#include "qgspoint3dsymbol.h"
54#include "qgspointcloudlayer.h"
58#include "qgsshadowrenderview.h"
59#include "qgsskyboxentity.h"
60#include "qgsskyboxsettings.h"
61#include "qgssourcecache.h"
62#include "qgsterrainentity.h"
63#include "qgsterraingenerator.h"
64#include "qgstiledscenelayer.h"
66#include "qgsvectorlayer.h"
68#include "qgswindow3dengine.h"
69
70#include <QOpenGLContext>
71#include <QOpenGLFunctions>
72#include <QString>
73#include <QSurface>
74#include <QTimer>
75#include <QUrl>
76#include <Qt3DExtras/QDiffuseSpecularMaterial>
77#include <Qt3DExtras/QForwardRenderer>
78#include <Qt3DExtras/QPhongAlphaMaterial>
79#include <Qt3DExtras/QPhongMaterial>
80#include <Qt3DExtras/QSphereMesh>
81#include <Qt3DLogic/QFrameAction>
82#include <Qt3DRender/QCamera>
83#include <Qt3DRender/QCullFace>
84#include <Qt3DRender/QDepthTest>
85#include <Qt3DRender/QEffect>
86#include <Qt3DRender/QMesh>
87#include <Qt3DRender/QRenderPass>
88#include <Qt3DRender/QRenderSettings>
89#include <Qt3DRender/QRenderState>
90#include <Qt3DRender/QSceneLoader>
91#include <Qt3DRender/QTechnique>
92#include <QtMath>
93
94#include "moc_qgs3dmapscene.cpp"
95
96using namespace Qt::StringLiterals;
97
98std::function<QMap<QString, Qgs3DMapScene *>()> Qgs3DMapScene::sOpenScenesFunction = [] { return QMap<QString, Qgs3DMapScene *>(); };
99
101 : mMap( map )
102 , mEngine( engine )
103{
104 connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged );
105 onBackgroundColorChanged();
106
107 // The default render policy in Qt3D is "Always" - i.e. the 3D map scene gets refreshed up to 60 fps
108 // even if there's no change. Switching to "on demand" should only re-render when something has changed
109 // and we save quite a lot of resources
110 mEngine->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand );
111
112 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
113
114 // Get the maximum of clip planes available
115 mMaxClipPlanes = Qgs3DUtils::openGlMaxClipPlanes( mEngine->surface() );
116
117 // Camera
118 float aspectRatio = ( float ) viewportRect.width() / viewportRect.height();
119 mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f );
120
121 mFrameAction = new Qt3DLogic::QFrameAction();
122 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qgs3DMapScene::onFrameTriggered );
123 addComponent( mFrameAction ); // takes ownership
124
125 // Camera controlling
126 mCameraController = new QgsCameraController( this ); // attaches to the scene
127
128 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
129 mCameraController->resetGlobe( 10'000'000 );
130 else
131 mCameraController->resetView( 1000 );
132
133 addCameraViewCenterEntity( mEngine->camera() );
134 addCameraRotationCenterEntity( mCameraController );
135 updateLights();
136
137 // create terrain entity
138
139 createTerrainDeferred();
140 connect( &map, &Qgs3DMapSettings::extentChanged, this, &Qgs3DMapScene::createTerrain );
141 connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain );
142
143 connect( &map, &Qgs3DMapSettings::terrainSettingsChanged, this, &Qgs3DMapScene::createTerrain );
144
145 connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );
146 connect( &map, &Qgs3DMapSettings::lightSourcesChanged, this, &Qgs3DMapScene::updateLights );
147 connect( &map, &Qgs3DMapSettings::showLightSourceOriginsChanged, this, &Qgs3DMapScene::updateLights );
148 connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
149 connect( &map, &Qgs3DMapSettings::projectionTypeChanged, this, &Qgs3DMapScene::updateCameraLens );
150 connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged );
151 connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged );
152 connect( &map, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapScene::onAmbientOcclusionSettingsChanged );
153 connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
154 connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
155 connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged );
156 connect( &map, &Qgs3DMapSettings::debugShadowMapSettingsChanged, this, &Qgs3DMapScene::onDebugShadowMapSettingsChanged );
157 connect( &map, &Qgs3DMapSettings::debugDepthMapSettingsChanged, this, &Qgs3DMapScene::onDebugDepthMapSettingsChanged );
159 connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
160 connect( &map, &Qgs3DMapSettings::cameraNavigationModeChanged, this, &Qgs3DMapScene::onCameraNavigationModeChanged );
161 connect( &map, &Qgs3DMapSettings::debugOverlayEnabledChanged, this, &Qgs3DMapScene::onDebugOverlayEnabledChanged );
162 connect( &map, &Qgs3DMapSettings::stopUpdatesChanged, this, &Qgs3DMapScene::onStopUpdatesChanged );
163 connect( &map, &Qgs3DMapSettings::show2DMapOverlayChanged, this, &Qgs3DMapScene::onShowMapOverlayChanged );
164 connect( &map, &Qgs3DMapSettings::viewFrustumVisualizationEnabledChanged, this, &Qgs3DMapScene::onShowMapOverlayChanged );
165
166 connect( &map, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapScene::on3DAxisSettingsChanged );
167
168 connect( &map, &Qgs3DMapSettings::originChanged, this, &Qgs3DMapScene::onOriginChanged );
169
170 connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [this]( const QString &url ) {
171 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
172 for ( QgsMapLayer *layer : modelVectorLayers )
173 {
174 QgsAbstract3DRenderer *renderer = layer->renderer3D();
175 if ( renderer )
176 {
177 if ( renderer->type() == "vector"_L1 )
178 {
179 const QgsPoint3DSymbol *pointSymbol = static_cast<const QgsPoint3DSymbol *>( static_cast<QgsVectorLayer3DRenderer *>( renderer )->symbol() );
180 if ( pointSymbol && pointSymbol->shapeProperty( u"model"_s ).toString() == url )
181 {
182 removeLayerEntity( layer );
183 addLayerEntity( layer );
184 }
185 }
186 else if ( renderer->type() == "rulebased"_L1 )
187 {
188 const QgsRuleBased3DRenderer::RuleList rules = static_cast<QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
189 for ( const QgsRuleBased3DRenderer::Rule *rule : rules )
190 {
191 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( rule->symbol() );
192 if ( pointSymbol && pointSymbol->shapeProperty( u"model"_s ).toString() == url )
193 {
194 removeLayerEntity( layer );
195 addLayerEntity( layer );
196 break;
197 }
198 }
199 }
200 }
201 }
202 } );
203
204 // listen to changes of layers in order to add/remove 3D renderer entities
205 connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged );
206
207 connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged );
208 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, this, &Qgs3DMapScene::onCameraChanged );
209
210 onSkyboxSettingsChanged();
211
212 // force initial update of chunked entities
213 onCameraChanged();
214 // force initial update of eye dome shading
215 onEyeDomeShadingSettingsChanged();
216 // force initial update of debugging setting of preview quads
217 onDebugShadowMapSettingsChanged();
218 onDebugDepthMapSettingsChanged();
219 // force initial update of ambient occlusion settings
220 onAmbientOcclusionSettingsChanged();
221
222 // timer used to refresh the map overlay every 250 ms while the camera is moving.
223 // schedule2DMapOverlayUpdate() is called to schedule the update.
224 // At the end of the delay, applyPendingOverlayUpdate() performs the update.
225 mOverlayUpdateTimer = new QTimer( this );
226 mOverlayUpdateTimer->setSingleShot( true );
227 mOverlayUpdateTimer->setInterval( 250 );
228 connect( mOverlayUpdateTimer, &QTimer::timeout, this, &Qgs3DMapScene::applyPendingOverlayUpdate );
229
230 // force initial update of map overlay entity
231 onShowMapOverlayChanged();
232
233 onCameraMovementSpeedChanged();
234
235 on3DAxisSettingsChanged();
236}
237
239{
240 if ( mMap.sceneMode() == Qgis::SceneMode::Globe )
241 {
242 mCameraController->resetGlobe( 10'000'000 );
243 return;
244 }
245
246 const QgsDoubleRange zRange = elevationRange();
247 const QgsRectangle extent = sceneExtent();
248 const double side = std::max( extent.width(), extent.height() );
249 double d = side / 2 / std::tan( cameraController()->camera()->fieldOfView() / 2 * M_PI / 180 );
250 d += zRange.isInfinite() ? 0. : zRange.upper();
251 mCameraController->resetView( static_cast<float>( d ) );
252}
253
255{
256 QgsPointXY center = extent.center();
257 const QgsVector3D origin = mMap.origin();
258
259 const QgsVector3D p1 = mMap.mapToWorldCoordinates( QgsVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
260 const QgsVector3D p2 = mMap.mapToWorldCoordinates( QgsVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
261
262 const double xSide = std::abs( p1.x() - p2.x() );
263 const double ySide = std::abs( p1.y() - p2.y() );
264 const double side = std::max( xSide, ySide );
265
266 const double fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
267 double distance = side / 2.0f / std::tan( fov / 2.0f );
268
269 // adjust by elevation
270 const QgsDoubleRange zRange = elevationRange();
271 if ( !zRange.isInfinite() )
272 distance += zRange.upper();
273
274 // subtract map origin so coordinates are relative to it
275 // clang-format off
276 mCameraController->setViewFromTop(
277 static_cast<float>( center.x() - origin.x() ),
278 static_cast<float>( center.y() - origin.y() ),
279 static_cast<float>( distance )
280 );
281 // clang-format on
282}
283
284QVector<QgsPointXY> Qgs3DMapScene::viewFrustum2DExtent() const
285{
286 Qt3DRender::QCamera *camera = mCameraController->camera();
287 QVector<QgsPointXY> extent;
288 QVector<int> pointsOrder = { 0, 1, 3, 2 };
289 for ( int i : pointsOrder )
290 {
291 const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : mEngine->size().width(), ( ( i >> 1 ) & 1 ) ? 0 : mEngine->size().height() );
292 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, mEngine->size(), camera );
293 QVector3D dir = ray.direction();
294 if ( dir.z() == 0.0 )
295 dir.setZ( 0.000001 );
296 double t = -ray.origin().z() / dir.z();
297 if ( t < 0 )
298 {
299 // If the projected point is on the back of the camera we choose the farthest point in the front
300 t = camera->farPlane();
301 }
302 else
303 {
304 // If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
305 t = std::min<float>( t, camera->farPlane() );
306 }
307 QVector3D planePoint = ray.origin() + t * dir;
308 QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
309 extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
310 }
311 return extent;
312}
313
315{
316 int count = 0;
317 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
318 count += entity->pendingJobsCount();
319 return count;
320}
321
322double Qgs3DMapScene::worldSpaceError( double epsilon, double distance ) const
323{
324 Qt3DRender::QCamera *camera = mCameraController->camera();
325 const double fov = camera->fieldOfView();
326 const QSize size = mEngine->size();
327 const int screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?
328
329 // see Qgs3DUtils::screenSpaceError() for the inverse calculation (world space error to screen space error)
330 // with explanation of the math.
331 const double frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
332 const double err = frustumWidthAtDistance * epsilon / screenSizePx;
333 return err;
334}
335
336void Qgs3DMapScene::onCameraChanged()
337{
338 if ( mMap.projectionType() == Qt3DRender::QCameraLens::OrthographicProjection )
339 {
340 QRect viewportRect( QPoint( 0, 0 ), mEngine->size() );
341 const float viewWidthFromCenter = mCameraController->distance();
342 const float viewHeightFromCenter = viewportRect.height() * viewWidthFromCenter / viewportRect.width();
343 mEngine->camera()->lens()->setOrthographicProjection( -viewWidthFromCenter, viewWidthFromCenter, -viewHeightFromCenter, viewHeightFromCenter, mEngine->camera()->nearPlane(), mEngine->camera()->farPlane() );
344 }
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.debugShadowMapEnabled() || 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 ) )
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 }
848 else if ( layer->type() == Qgis::LayerType::Mesh && renderer->type() == "mesh"_L1 )
849 {
850 QgsMeshLayer3DRenderer *meshRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer );
851 meshRenderer->setLayer( static_cast<QgsMeshLayer *>( layer ) );
852
853 // Before entity creation, set the maximum texture size
854 // Not very clean, but for now, only place found in the workflow to do that simple
855 QgsMesh3DSymbol *sym = meshRenderer->symbol()->clone();
856 sym->setMaximumTextureSize( maximumTextureSize() );
857 meshRenderer->setSymbol( sym );
858 }
859 else if ( layer->type() == Qgis::LayerType::PointCloud && renderer->type() == "pointcloud"_L1 )
860 {
861 QgsPointCloudLayer3DRenderer *pointCloudRenderer = static_cast<QgsPointCloudLayer3DRenderer *>( renderer );
862 pointCloudRenderer->setLayer( static_cast<QgsPointCloudLayer *>( layer ) );
863 }
864 else if ( layer->type() == Qgis::LayerType::TiledScene && renderer->type() == "tiledscene"_L1 )
865 {
866 QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
867 tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
868 }
869 else if ( layer->type() == Qgis::LayerType::Annotation && renderer->type() == "annotation"_L1 )
870 {
871 auto annotationLayerRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( renderer );
872 annotationLayerRenderer->setLayer( qobject_cast<QgsAnnotationLayer *>( layer ) );
873 }
874
875 Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
876 if ( newEntity )
877 {
878 mLayerEntities.insert( layer, newEntity );
879
880 if ( Qgs3DMapSceneEntity *sceneNewEntity = qobject_cast<Qgs3DMapSceneEntity *>( newEntity ) )
881 {
882 // also sets this scene as the entity's parent and finalizes it
883 addSceneEntity( sceneNewEntity );
884 }
885 else
886 {
887 newEntity->setParent( this );
888 finalizeNewEntity( newEntity );
889 }
890 }
891 }
892
893 connect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
894
895 if ( layer->type() == Qgis::LayerType::Vector )
896 {
897 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
898 connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
899 connect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
900 connect( vlayer, &QgsVectorLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
901 }
902
903 if ( layer->type() == Qgis::LayerType::Mesh )
904 {
905 connect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
906 }
907
908 if ( layer->type() == Qgis::LayerType::PointCloud )
909 {
910 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
911 connect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
912 connect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
913 }
914}
915
916void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
917{
918 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
919
920 if ( Qgs3DMapSceneEntity *sceneEntity = qobject_cast<Qgs3DMapSceneEntity *>( entity ) )
921 {
922 // also schedules the entity for deletion
923 removeSceneEntity( sceneEntity );
924 }
925 else
926 {
927 if ( entity )
928 entity->deleteLater();
929 }
930
931 disconnect( layer, &QgsMapLayer::request3DUpdate, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
932
933 if ( layer->type() == Qgis::LayerType::Vector )
934 {
935 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
936 disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
937 disconnect( vlayer, &QgsVectorLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
938 disconnect( vlayer, &QgsVectorLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
939 mModelVectorLayers.removeAll( layer );
940 }
941
942 if ( layer->type() == Qgis::LayerType::Mesh )
943 {
944 disconnect( layer, &QgsMapLayer::rendererChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
945 }
946
947 if ( layer->type() == Qgis::LayerType::PointCloud )
948 {
949 QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( layer );
950 disconnect( pclayer, &QgsPointCloudLayer::renderer3DChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
951 disconnect( pclayer, &QgsPointCloudLayer::subsetStringChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
952 disconnect( pclayer, &QgsPointCloudLayer::layerModified, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
953 }
954}
955
956void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
957{
958 // let's make sure that any entity we're about to show has the right scene origin set
959 const QList<QgsGeoTransform *> transforms = newEntity->findChildren<QgsGeoTransform *>();
960 for ( QgsGeoTransform *transform : transforms )
961 {
962 transform->setOrigin( mMap.origin() );
963 }
964
965 // set clip planes on the new entity if necessary
966 handleClippingOnEntity( newEntity );
967
968 // this is probably not the best place for material-specific configuration,
969 // maybe this could be more generalized when other materials need some specific treatment
970 const QList<QgsLineMaterial *> childLineMaterials = newEntity->findChildren<QgsLineMaterial *>();
971 for ( QgsLineMaterial *lm : childLineMaterials )
972 {
973 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, lm, [lm, this] { lm->setViewportSize( mEngine->size() ); } );
974
975 lm->setViewportSize( mEngine->size() );
976 }
977 // configure billboard's viewport when the viewport is changed.
978 const QList<QgsPoint3DBillboardMaterial *> childBillboardMaterials = newEntity->findChildren<QgsPoint3DBillboardMaterial *>();
979 for ( QgsPoint3DBillboardMaterial *bm : childBillboardMaterials )
980 {
981 connect( mEngine, &QgsAbstract3DEngine::sizeChanged, bm, [bm, this] { bm->setViewportSize( mEngine->size() ); } );
982
983 bm->setViewportSize( mEngine->size() );
984 }
985
986 QgsFrameGraph *frameGraph = mEngine->frameGraph();
987
988 // Here we check if the entity should not be rendered in the forward render view
989 // For example highlight entities should only be rendered in the highlights render view, so we check for attached QLayers
990 const QVector<Qt3DRender::QLayer *> layers = newEntity->componentsOfType<Qt3DRender::QLayer>();
991 if ( layers.contains( frameGraph->highlightsRenderView().highlightsLayer() ) )
992 return;
993
994 // Add the required QLayers to the entity
995 newEntity->addComponent( frameGraph->forwardRenderView().renderLayer() );
996 newEntity->addComponent( frameGraph->shadowRenderView().entityCastingShadowsLayer() );
997
998 // Finalize adding the 3D transparent objects by adding the layer components to the entities
999 Qt3DRender::QLayer *transparentLayer = frameGraph->forwardRenderView().transparentObjectLayer();
1000 const QList<Qt3DRender::QMaterial *> childMaterials = newEntity->findChildren<Qt3DRender::QMaterial *>();
1001 for ( Qt3DRender::QMaterial *material : childMaterials )
1002 {
1003 // This handles the phong material without data defined properties.
1004 if ( Qt3DExtras::QDiffuseSpecularMaterial *ph = qobject_cast<Qt3DExtras::QDiffuseSpecularMaterial *>( material ) )
1005 {
1006 if ( ph->diffuse().value<QColor>().alphaF() != 1.0f )
1007 {
1008 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( ph->parent() );
1009 if ( entity && !entity->components().contains( transparentLayer ) )
1010 {
1011 entity->addComponent( transparentLayer );
1012 }
1013 }
1014 }
1015#if 0
1016 /*
1017 * Adds transparency layer to QgsPoint3DBillboardMaterial entities,
1018 * so that they get rendered in the transparent pipeline instead
1019 * of the opaque pipeline. Permits semi-opaque pixel rendering.
1020 *
1021 * Pros: nicely smoothed billboard symbol rendering, without harsh
1022 * aliased edges. Billboard symbols can use semi-transparent colors.
1023 *
1024 * Cons: Introduces ordering issues for billboards, where billboards
1025 * which should be shown behind others will appear in front from
1026 * some angles (i.e. the same issue as we get for 3d polygon objects
1027 * with transparency)
1028 *
1029 * Consider enabling if/when we have some workaround for the stacking issue,
1030 * eg CPU based sorting on camera movement...
1031 */
1032 else if ( QgsPoint3DBillboardMaterial *billboardMaterial = qobject_cast<QgsPoint3DBillboardMaterial *>( material ) )
1033 {
1034 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( billboardMaterial->parent() );
1035 if ( entity && !entity->components().contains( transparentLayer ) )
1036 {
1037 entity->addComponent( transparentLayer );
1038 }
1039 }
1040#endif
1041 else
1042 {
1043 // This handles the phong material with data defined properties, the textured case and point (instanced) symbols.
1044 Qt3DRender::QEffect *effect = material->effect();
1045 if ( effect )
1046 {
1047 const QVector<Qt3DRender::QParameter *> parameters = effect->parameters();
1048 for ( const Qt3DRender::QParameter *parameter : parameters )
1049 {
1050 if ( parameter->name() == "opacity" && parameter->value() != 1.0f )
1051 {
1052 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( material->parent() );
1053 if ( entity && !entity->components().contains( transparentLayer ) )
1054 {
1055 entity->addComponent( transparentLayer );
1056 }
1057 break;
1058 }
1059 }
1060 }
1061 }
1062 }
1063}
1064
1065int Qgs3DMapScene::maximumTextureSize() const
1066{
1067 QSurface *surface = mEngine->surface();
1068 QOpenGLContext context;
1069 context.create();
1070 bool success = context.makeCurrent( surface );
1071
1072 if ( success )
1073 {
1074 QOpenGLFunctions openglFunctions = QOpenGLFunctions( &context );
1075
1076 GLint size;
1077 openglFunctions.initializeOpenGLFunctions();
1078 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
1079 return int( size );
1080 }
1081 else
1082 {
1083 return 4096; //we can't have a context to defined the max texture size, we use this reasonable value
1084 }
1085}
1086
1087void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
1088{
1089 mEntityCameraViewCenter = new Qt3DCore::QEntity;
1090
1091 Qt3DCore::QTransform *trCameraViewCenter = new Qt3DCore::QTransform;
1092 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
1093 connect( camera, &Qt3DRender::QCamera::viewCenterChanged, this, [trCameraViewCenter, camera] { trCameraViewCenter->setTranslation( camera->viewCenter() ); } );
1094
1095 Qt3DExtras::QPhongMaterial *materialCameraViewCenter = new Qt3DExtras::QPhongMaterial;
1096 materialCameraViewCenter->setAmbient( Qt::red );
1097 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
1098
1099 Qt3DExtras::QSphereMesh *rendererCameraViewCenter = new Qt3DExtras::QSphereMesh;
1100 rendererCameraViewCenter->setRadius( 10 );
1101 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
1102
1103 mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() );
1104 mEntityCameraViewCenter->setParent( this );
1105
1106 connect( &mMap, &Qgs3DMapSettings::showCameraViewCenterChanged, this, [this] { mEntityCameraViewCenter->setEnabled( mMap.showCameraViewCenter() ); } );
1107}
1108
1109void Qgs3DMapScene::setSceneState( Qgs3DMapScene::SceneState state )
1110{
1111 if ( mSceneState == state )
1112 return;
1113 mSceneState = state;
1114 emit sceneStateChanged();
1115}
1116
1117void Qgs3DMapScene::updateSceneState()
1118{
1119 if ( mTerrainUpdateScheduled )
1120 {
1121 setSceneState( Updating );
1122 return;
1123 }
1124
1125 for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
1126 {
1127 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
1128 {
1129 setSceneState( Updating );
1130 return;
1131 }
1132 }
1133
1134 setSceneState( Ready );
1135}
1136
1137void Qgs3DMapScene::onSkyboxSettingsChanged()
1138{
1139 QgsSkyboxSettings skyboxSettings = mMap.skyboxSettings();
1140 if ( mSkybox )
1141 {
1142 mSkybox->deleteLater();
1143 mSkybox = nullptr;
1144 }
1145
1146 mEngine->setFrustumCullingEnabled( !mMap.isSkyboxEnabled() );
1147
1148 if ( mMap.isSkyboxEnabled() )
1149 {
1150 QMap<QString, QString> faces;
1151 switch ( skyboxSettings.skyboxType() )
1152 {
1154 faces = skyboxSettings.cubeMapFacesPaths();
1155 mSkybox = new QgsCubeFacesSkyboxEntity( 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 );
1156 break;
1158 mSkybox = new QgsPanoramicSkyboxEntity( skyboxSettings.panoramicTexturePath(), this );
1159 break;
1160 }
1161 }
1162}
1163
1164void Qgs3DMapScene::onShadowSettingsChanged()
1165{
1166 mEngine->frameGraph()->updateShadowSettings( mMap.shadowSettings(), mMap.lightSources() );
1167}
1168
1169void Qgs3DMapScene::onAmbientOcclusionSettingsChanged()
1170{
1171 mEngine->frameGraph()->updateAmbientOcclusionSettings( mMap.ambientOcclusionSettings() );
1172}
1173
1174void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
1175{
1176 mEngine->frameGraph()->updateDebugShadowMapSettings( mMap );
1177}
1178
1179void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
1180{
1181 mEngine->frameGraph()->updateDebugDepthMapSettings( mMap );
1182}
1183
1184void Qgs3DMapScene::onDebugOverlayEnabledChanged()
1185{
1186 mEngine->frameGraph()->setDebugOverlayEnabled( mMap.isDebugOverlayEnabled() );
1187 mEngine->renderSettings()->setRenderPolicy( mMap.isDebugOverlayEnabled() ? Qt3DRender::QRenderSettings::Always : Qt3DRender::QRenderSettings::OnDemand );
1188}
1189
1190void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
1191{
1192 mEngine->frameGraph()->updateEyeDomeSettings( mMap );
1193}
1194
1195void Qgs3DMapScene::onShowMapOverlayChanged()
1196{
1197 const QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
1198 update2DMapOverlay( extent2D );
1199}
1200
1201void Qgs3DMapScene::onCameraMovementSpeedChanged()
1202{
1203 mCameraController->setCameraMovementSpeed( mMap.cameraMovementSpeed() );
1204}
1205
1206void Qgs3DMapScene::onCameraNavigationModeChanged()
1207{
1208 mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() );
1209}
1210
1212{
1213 QVector<QString> notParsedLayers;
1214 Qgs3DSceneExporter exporter;
1215
1216 exporter.setTerrainResolution( exportSettings.terrrainResolution() );
1217 exporter.setSmoothEdges( exportSettings.smoothEdges() );
1218 exporter.setExportNormals( exportSettings.exportNormals() );
1219 exporter.setExportTextures( exportSettings.exportTextures() );
1220 exporter.setTerrainTextureResolution( exportSettings.terrainTextureResolution() );
1221 exporter.setScale( exportSettings.scale() );
1222 exporter.setTerrainExportEnabled( exportSettings.terrainExportEnabled() );
1223
1224 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1225 {
1226 QgsMapLayer *layer = it.key();
1227 Qt3DCore::QEntity *rootEntity = it.value();
1228 Qgis::LayerType layerType = layer->type();
1229 switch ( layerType )
1230 {
1232 if ( !exporter.parseVectorLayerEntity( rootEntity, qobject_cast<QgsVectorLayer *>( layer ) ) )
1233 notParsedLayers.push_back( layer->name() );
1234 break;
1243 notParsedLayers.push_back( layer->name() );
1244 break;
1245 }
1246 }
1247
1248 if ( mTerrain )
1249 exporter.parseTerrain( mTerrain, "Terrain" );
1250
1251 const bool sceneSaved = exporter.save( exportSettings.sceneName(), exportSettings.sceneFolderPath() );
1252 if ( !sceneSaved )
1253 {
1254 return false;
1255 }
1256
1257 if ( !notParsedLayers.empty() )
1258 {
1259 QString message = tr( "The following layers were not exported:" ) + "\n";
1260 for ( const QString &layerName : notParsedLayers )
1261 message += layerName + "\n";
1262 QgsMessageOutput::showMessage( tr( "3D exporter warning" ), message, Qgis::StringFormat::PlainText );
1263 }
1264
1265 return true;
1266}
1267
1268QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
1269{
1270 QVector<const QgsChunkNode *> chunks;
1271 if ( !mLayerEntities.contains( layer ) )
1272 return chunks;
1273 if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[layer] ) )
1274 {
1275 const QList<QgsChunkNode *> activeNodes = c->activeNodes();
1276 for ( QgsChunkNode *n : activeNodes )
1277 chunks.push_back( n );
1278 }
1279 return chunks;
1280}
1281
1283{
1284 return mMap.extent();
1285}
1286
1287QgsDoubleRange Qgs3DMapScene::elevationRange( const bool ignoreTerrain ) const
1288{
1289 double zMin = std::numeric_limits<double>::max();
1290 double zMax = std::numeric_limits<double>::lowest();
1291 if ( mMap.terrainRenderingEnabled() && mTerrain && !ignoreTerrain )
1292 {
1293 const QgsBox3D box3D = mTerrain->rootNode()->box3D();
1294 zMin = std::min( zMin, box3D.zMinimum() );
1295 zMax = std::max( zMax, box3D.zMaximum() );
1296 }
1297
1298 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); it++ )
1299 {
1300 QgsMapLayer *layer = it.key();
1301 switch ( layer->type() )
1302 {
1304 {
1305 QgsPointCloudLayer *pcl = qobject_cast<QgsPointCloudLayer *>( layer );
1306 QgsDoubleRange zRange = pcl->elevationProperties()->calculateZRange( pcl );
1307 zMin = std::min( zMin, zRange.lower() );
1308 zMax = std::max( zMax, zRange.upper() );
1309 break;
1310 }
1312 {
1313 QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( layer );
1314 QgsAbstract3DRenderer *renderer3D = meshLayer->renderer3D();
1315 if ( renderer3D )
1316 {
1317 QgsMeshLayer3DRenderer *meshLayerRenderer = static_cast<QgsMeshLayer3DRenderer *>( renderer3D );
1318 const int verticalGroupDatasetIndex = meshLayerRenderer->symbol()->verticalDatasetGroupIndex();
1319 const QgsMeshDatasetGroupMetadata verticalGroupMetadata = meshLayer->datasetGroupMetadata( verticalGroupDatasetIndex );
1320 const double verticalScale = meshLayerRenderer->symbol()->verticalScale();
1321 zMin = std::min( zMin, verticalGroupMetadata.minimum() * verticalScale );
1322 zMax = std::max( zMax, verticalGroupMetadata.maximum() * verticalScale );
1323 }
1324 break;
1325 }
1327 {
1328 QgsTiledSceneLayer *sceneLayer = qobject_cast<QgsTiledSceneLayer *>( layer );
1329 const QgsDoubleRange zRange = sceneLayer->elevationProperties()->calculateZRange( sceneLayer );
1330 if ( !zRange.isInfinite() && !zRange.isEmpty() )
1331 {
1332 zMin = std::min( zMin, zRange.lower() );
1333 zMax = std::max( zMax, zRange.upper() );
1334 }
1335 break;
1336 }
1343 break;
1344 }
1345 }
1346 const QgsDoubleRange zRange( std::min( zMin, std::numeric_limits<double>::max() ), std::max( zMax, std::numeric_limits<double>::lowest() ) );
1347 return zRange.isEmpty() ? QgsDoubleRange() : zRange;
1348}
1349
1350QMap<QString, Qgs3DMapScene *> Qgs3DMapScene::openScenes()
1351{
1352 return sOpenScenesFunction();
1353}
1354
1355void Qgs3DMapScene::addCameraRotationCenterEntity( QgsCameraController *controller )
1356{
1357 mEntityRotationCenter = new Qt3DCore::QEntity;
1358
1359 Qt3DCore::QTransform *trRotationCenter = new Qt3DCore::QTransform;
1360 mEntityRotationCenter->addComponent( trRotationCenter );
1361 Qt3DExtras::QPhongMaterial *materialRotationCenter = new Qt3DExtras::QPhongMaterial;
1362 materialRotationCenter->setAmbient( Qt::blue );
1363 mEntityRotationCenter->addComponent( materialRotationCenter );
1364 Qt3DExtras::QSphereMesh *rendererRotationCenter = new Qt3DExtras::QSphereMesh;
1365 rendererRotationCenter->setRadius( 10 );
1366 mEntityRotationCenter->addComponent( rendererRotationCenter );
1367 mEntityRotationCenter->setEnabled( false );
1368 mEntityRotationCenter->setParent( this );
1369
1370 connect( controller, &QgsCameraController::cameraRotationCenterChanged, this, [trRotationCenter]( QVector3D center ) { trRotationCenter->setTranslation( center ); } );
1371
1372 connect( &mMap, &Qgs3DMapSettings::showCameraRotationCenterChanged, this, [this] { mEntityRotationCenter->setEnabled( mMap.showCameraRotationCenter() ); } );
1373}
1374
1375void Qgs3DMapScene::on3DAxisSettingsChanged()
1376{
1377 if ( m3DAxis )
1378 {
1379 m3DAxis->onAxisSettingsChanged();
1380 }
1381 else
1382 {
1383 if ( QgsWindow3DEngine *engine = dynamic_cast<QgsWindow3DEngine *>( mEngine ) )
1384 {
1385 m3DAxis = new Qgs3DAxis( static_cast<Qgs3DMapCanvas *>( engine->window() ), engine->root(), this, mCameraController, &mMap );
1386 }
1387 }
1388}
1389
1390void Qgs3DMapScene::onOriginChanged()
1391{
1392 const QList<QgsGeoTransform *> geoTransforms = findChildren<QgsGeoTransform *>();
1393 for ( QgsGeoTransform *transform : geoTransforms )
1394 {
1395 transform->setOrigin( mMap.origin() );
1396 }
1397
1398 const QList<QgsGeoTransform *> rubberBandGeoTransforms = mEngine->frameGraph()->rubberBandsRootEntity()->findChildren<QgsGeoTransform *>();
1399 for ( QgsGeoTransform *transform : rubberBandGeoTransforms )
1400 {
1401 transform->setOrigin( mMap.origin() );
1402 }
1403
1404 const QgsVector3D oldOrigin = mCameraController->origin();
1405 mCameraController->setOrigin( mMap.origin() );
1406
1407 if ( !mClipPlanesEquations.isEmpty() )
1408 {
1409 // how the math works - for a plane defined as (a,b,c,d), only "d" changes when
1410 // moving the origin - the plane normal vector (a,b,c) stays the same.
1411 // - line equation for old shift: a * (x - x0) + b * (y - y0) + c * (z - z0) + d0 = 0
1412 // - line equation for new shift: a * (x - x1) + b * (y - y1) + c * (z - z1) + d1 = 0
1413 // - we solve for d1:
1414 // d1 = a * (x1 - x0) + b * (y1 - y0) + c * (z1 - z0) + d0
1415
1416 QList<QVector4D> newPlanes;
1417 QgsVector3D originShift = mMap.origin() - oldOrigin;
1418 for ( QVector4D plane : std::as_const( mClipPlanesEquations ) )
1419 {
1420 plane.setW( originShift.x() * plane.x() + originShift.y() * plane.y() + originShift.z() * plane.z() + plane.w() );
1421 newPlanes.append( plane );
1422 }
1423 enableClipping( newPlanes );
1424 }
1425}
1426
1427void Qgs3DMapScene::handleClippingOnEntity( QEntity *entity ) const
1428{
1429 if ( mClipPlanesEquations.isEmpty() ) // no clip plane equations, disable clipping
1430 {
1431 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1432 {
1433 material->disableClipping();
1434 }
1435 }
1436 else // enable clipping
1437 {
1438 for ( QgsMaterial *material : entity->componentsOfType<QgsMaterial>() )
1439 {
1440 material->enableClipping( mClipPlanesEquations );
1441 }
1442 }
1443
1444 // recursive call
1445 // enable or disable clipping on the children accordingly
1446 for ( QObject *child : entity->children() )
1447 {
1448 Qt3DCore::QEntity *childEntity = qobject_cast<Qt3DCore::QEntity *>( child );
1449 if ( childEntity )
1450 {
1451 handleClippingOnEntity( childEntity );
1452 }
1453 }
1454}
1455
1456void Qgs3DMapScene::handleClippingOnAllEntities() const
1457{
1458 // Need to loop mLayerEntities instead of mSceneEntities to handle entities
1459 // which do no inherit from Qgs3DMapSceneEntity. For example, mesh entities.
1460 for ( auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
1461 {
1462 handleClippingOnEntity( it.value() );
1463 }
1464 if ( mTerrain )
1465 {
1466 handleClippingOnEntity( mTerrain );
1467 }
1468 if ( mGlobe )
1469 {
1470 handleClippingOnEntity( mGlobe );
1471 }
1472}
1473
1475{
1476 if ( clipPlaneEquations.size() > mMaxClipPlanes )
1477 {
1478 QgsDebugMsgLevel( u"Qgs3DMapScene::enableClipping: it is not possible to use more than %1 clipping planes."_s.arg( mMaxClipPlanes ), 2 );
1479 }
1480 mClipPlanesEquations = clipPlaneEquations.mid( 0, mMaxClipPlanes );
1481
1482 // enable the clip planes on the framegraph
1483 mEngine->frameGraph()->addClipPlanes( clipPlaneEquations.size() );
1484
1485 // Enable the clip planes for the material of each entity.
1486 handleClippingOnAllEntities();
1487}
1488
1490{
1491 mClipPlanesEquations.clear();
1492
1493 // disable the clip planes on the framegraph
1494 mEngine->frameGraph()->removeClipPlanes();
1495
1496 // Disable the clip planes for the material of each entity.
1497 handleClippingOnAllEntities();
1498}
1499
1500void Qgs3DMapScene::onStopUpdatesChanged()
1501{
1502 mSceneUpdatesEnabled = !mMap.stopUpdates();
1503}
1504
1505void Qgs3DMapScene::schedule2DMapOverlayUpdate()
1506{
1507 // Start the overlay update timer if overlay is active and not already running
1508 if ( mMap.is2DMapOverlayEnabled() && mOverlayUpdateTimer && !mOverlayUpdateTimer->isActive() )
1509 {
1510 mOverlayUpdateTimer->start();
1511 }
1512}
1513
1514void Qgs3DMapScene::applyPendingOverlayUpdate()
1515{
1516 if ( mMap.is2DMapOverlayEnabled() )
1517 {
1518 const QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
1519 update2DMapOverlay( extent2D );
1520 }
1521}
@ Model
Model.
Definition qgis.h:4261
@ 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:4299
@ Local
Local scene based on a projected CRS.
Definition qgis.h:4298
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.
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.
Qt3DRender::QCameraLens::ProjectionType projectionType() const
Returns the camera lens' projection type.
void debugDepthMapSettingsChanged()
Emitted when depth map debugging has changed.
void backgroundColorChanged()
Emitted when the background color has changed.
void showCameraRotationCenterChanged()
Emitted when the flag whether camera's rotation center is shown has changed.
void cameraNavigationModeChanged()
Emitted when the camera navigation mode was changed.
void shadowSettingsChanged()
Emitted when shadow rendering settings are changed.
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 skyboxSettingsChanged()
Emitted when skybox settings are changed.
void projectionTypeChanged()
Emitted when the camera lens projection type changes.
void stopUpdatesChanged()
Emitted when the flag whether to keep updating scene has changed.
void lightSourcesChanged()
Emitted when any of the light source settings in the map changes.
void showLightSourceOriginsChanged()
Emitted when the flag whether light source origins are shown has changed.
void terrainSettingsChanged()
Emitted when the terrain settings are changed.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is enabled or disabled.
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
void 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.
Entity that handles the exporting of 3D scenes.
bool save(const QString &sceneName, const QString &sceneFolderPath, int precision=6) const
Saves the scene to a .obj file Returns false if the operation failed.
void setExportTextures(bool exportTextures)
Sets whether the textures will be exported.
void parseTerrain(QgsTerrainEntity *terrain, const QString &layer)
Creates terrain export objects from the terrain entity.
void setTerrainResolution(int resolution)
Sets the terrain resolution.
void setTerrainTextureResolution(int resolution)
Sets the terrain texture resolution.
bool parseVectorLayerEntity(Qt3DCore::QEntity *entity, QgsVectorLayer *layer)
Creates necessary export objects from entity if it represents valid vector layer entity Returns false...
void setScale(float scale)
Sets the scale of the exported 3D model.
void setExportNormals(bool exportNormals)
Sets whether the normals will be exported.
void setSmoothEdges(bool smoothEdges)
Sets whether the triangles will look smooth.
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 QSize size() const =0
Returns size of the engine's rendering area in pixels.
Base class for all renderers that participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass).
virtual Qt3DCore::QEntity * createEntity(Qgs3DMapSettings *map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
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.
float distance() const
Returns distance of the camera from the point it is looking at.
void cameraChanged()
Emitted when camera has been updated.
void cameraRotationCenterChanged(QVector3D position)
Emitted when the camera rotation center changes.
QgsRange which stores a range of double values.
Definition qgsrange.h: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.
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.
QgsSkyboxEntity::SkyboxType skyboxType() const
Returns the type of the skybox.
QString panoramicTexturePath() const
Returns the panoramic texture path of a skybox of type "Panormaic skybox".
void remoteSourceFetched(const QString &url)
Emitted when the cache has finished retrieving a 3D model from a remote url.
void setLayer(QgsTiledSceneLayer *layer)
Sets tiled scene layer associated with the renderer.
Represents a map layer supporting display of tiled scene objects.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h: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:6986
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59