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