QGIS API Documentation 4.3.0-Master (18b5e825726)
Loading...
Searching...
No Matches
qgsglobechunkedentity.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsglobechunkedentity.cpp
3 --------------------------------------
4 Date : March 2025
5 Copyright : (C) 2025 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
17
18#include <memory>
19
20#include "qgs3dmapsettings.h"
21#include "qgs3dutils.h"
24#include "qgsdistancearea.h"
25#include "qgseventtracing.h"
26#include "qgsgeotransform.h"
27#include "qgsglobematerial.h"
29#include "qgsray3d.h"
30#include "qgsraycastcontext.h"
31#include "qgsraycastingutils.h"
34
35#include <QByteArray>
36#include <QImage>
37#include <QString>
38#include <Qt3DCore/QAttribute>
39#include <Qt3DCore/QBuffer>
40#include <Qt3DCore/QEntity>
41#include <Qt3DCore/QGeometry>
42#include <Qt3DRender/QGeometryRenderer>
43#include <Qt3DRender/QTexture>
44#include <Qt3DRender/QTextureImage>
45
46#include "moc_qgsglobechunkedentity.cpp"
47
48using namespace Qt::StringLiterals;
49
51
52static Qt3DCore::QEntity *makeGlobeMesh(
53 double lonMin,
54 double lonMax,
55 double latMin,
56 double latMax,
57 int lonSliceCount,
58 int latSliceCount,
59 const QgsCoordinateTransform &globeCrsToLatLon,
60 QImage textureQImage,
61 QString textureDebugText,
62 const QgsMaterialContext &context
63)
64{
65 double lonRange = lonMax - lonMin;
66 double latRange = latMax - latMin;
67 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
68 double latStep = latRange / ( double ) ( latSliceCount - 1 );
69
70 std::vector<double> x, y, z;
71 int pointCount = latSliceCount * lonSliceCount;
72 x.reserve( pointCount );
73 y.reserve( pointCount );
74 z.reserve( pointCount );
75
76 for ( int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
77 {
78 double lat = latSliceIndex * latStep + latMin;
79 for ( int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
80 {
81 double lon = lonSliceIndex * lonStep + lonMin;
82 x.push_back( lon );
83 y.push_back( lat );
84 z.push_back( 0 );
85 }
86 }
87
88 globeCrsToLatLon.transformCoords( pointCount, x.data(), y.data(), z.data(), Qgis::TransformDirection::Reverse );
89
90 // estimate origin of coordinates for this tile, to make the relative coordinates
91 // small to avoid numerical precision issues when rendering
92 // (avoids mesh jumping around when zoomed in very close to it)
93 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
94 QgsVector3D meshOrigin = globeCrsToLatLon.transform( meshOriginLatLon, Qgis::TransformDirection::Reverse );
95
96 int stride = ( 3 + 2 + 3 ) * sizeof( float );
97
98 QByteArray bufferBytes;
99 bufferBytes.resize( stride * pointCount );
100 float *fptr = ( float * ) bufferBytes.data();
101 for ( int i = 0; i < ( int ) pointCount; ++i )
102 {
103 *fptr++ = static_cast<float>( x[i] - meshOrigin.x() );
104 *fptr++ = static_cast<float>( y[i] - meshOrigin.y() );
105 *fptr++ = static_cast<float>( z[i] - meshOrigin.z() );
106
107 int vi = i / lonSliceCount;
108 int ui = i % lonSliceCount;
109 float v = static_cast<float>( vi ) / static_cast<float>( latSliceCount - 1 );
110 float u = static_cast<float>( ui ) / static_cast<float>( lonSliceCount - 1 );
111 *fptr++ = u;
112 *fptr++ = 1 - v;
113
114 QVector3D n = QVector3D( static_cast<float>( x[i] ), static_cast<float>( y[i] ), static_cast<float>( z[i] ) ).normalized();
115 *fptr++ = n.x();
116 *fptr++ = n.y();
117 *fptr++ = n.z();
118 }
119
120 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
121 int indices = faces * 3;
122
123 QByteArray indexBytes;
124 indexBytes.resize( indices * static_cast<int>( sizeof( ushort ) ) );
125
126 quint16 *indexPtr = reinterpret_cast<quint16 *>( indexBytes.data() );
127 for ( int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
128 {
129 int latSliceStartIndex = latSliceIndex * lonSliceCount;
130 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
131 for ( int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
132 {
133 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
134 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
135 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
136
137 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
138 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
139 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
140
141 indexPtr += 6;
142 }
143 }
144
145 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
146
147 Qt3DCore::QBuffer *vertexBuffer = new Qt3DCore::QBuffer( entity );
148 vertexBuffer->setData( bufferBytes );
149
150 Qt3DCore::QBuffer *indexBuffer = new Qt3DCore::QBuffer( entity );
151 indexBuffer->setData( indexBytes );
152
153 Qt3DCore::QAttribute *positionAttribute = new Qt3DCore::QAttribute( entity );
154 positionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
155 positionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
156 positionAttribute->setVertexSize( 3 );
157 positionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
158 positionAttribute->setBuffer( vertexBuffer );
159 positionAttribute->setByteStride( stride );
160 positionAttribute->setCount( pointCount );
161
162 Qt3DCore::QAttribute *texCoordAttribute = new Qt3DCore::QAttribute( entity );
163 texCoordAttribute->setName( Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName() );
164 texCoordAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
165 texCoordAttribute->setVertexSize( 2 );
166 texCoordAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
167 texCoordAttribute->setBuffer( vertexBuffer );
168 texCoordAttribute->setByteStride( stride );
169 texCoordAttribute->setByteOffset( 3 * sizeof( float ) );
170 texCoordAttribute->setCount( pointCount );
171
172 Qt3DCore::QAttribute *normalAttribute = new Qt3DCore::QAttribute( entity );
173 normalAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
174 normalAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
175 normalAttribute->setVertexSize( 3 );
176 normalAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
177 normalAttribute->setBuffer( vertexBuffer );
178 normalAttribute->setByteStride( stride );
179 normalAttribute->setByteOffset( 5 * sizeof( float ) );
180 normalAttribute->setCount( pointCount );
181
182 Qt3DCore::QAttribute *indexAttribute = new Qt3DCore::QAttribute( entity );
183 indexAttribute->setAttributeType( Qt3DCore::QAttribute::IndexAttribute );
184 indexAttribute->setVertexBaseType( Qt3DCore::QAttribute::UnsignedShort );
185 indexAttribute->setBuffer( indexBuffer );
186 indexAttribute->setCount( faces * 3 );
187
188 Qt3DCore::QGeometry *geometry = new Qt3DCore::QGeometry( entity );
189 geometry->addAttribute( positionAttribute );
190 geometry->addAttribute( texCoordAttribute );
191 geometry->addAttribute( normalAttribute );
192 geometry->addAttribute( indexAttribute );
193
194 Qt3DRender::QGeometryRenderer *geomRenderer = new Qt3DRender::QGeometryRenderer( entity );
195 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
196 geomRenderer->setVertexCount( faces * 3 );
197 geomRenderer->setGeometry( geometry );
198
199 QgsTerrainTextureImage *textureImage = new QgsTerrainTextureImage( textureQImage, QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
200
201 Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D( entity );
202 Qgs3DUtils::setTextureFiltering( texture, context );
203 texture->addTextureImage( textureImage );
204 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
205
206 QgsGlobeMaterial *material = new QgsGlobeMaterial( entity );
207 material->setTexture( texture );
208
209 QgsGeoTransform *geoTransform = new QgsGeoTransform( entity );
210 geoTransform->setGeoTranslation( meshOrigin );
211
212 entity->addComponent( material );
213 entity->addComponent( geomRenderer );
214 entity->addComponent( geoTransform );
215 return entity;
216}
217
218
219static void globeNodeIdToLatLon( QgsChunkNodeId n, double &latMin, double &latMax, double &lonMin, double &lonMax )
220{
221 if ( n == QgsChunkNodeId( 0, 0, 0, 0 ) )
222 {
223 latMin = -90;
224 lonMin = -180;
225 latMax = 90;
226 lonMax = 180;
227 return;
228 }
229
230 double tileSize = 180.0 / std::pow( 2.0, n.d - 1 );
231 lonMin = n.x * tileSize - 180.0;
232 latMin = n.y * tileSize - 90.0;
233 lonMax = lonMin + tileSize;
234 latMax = latMin + tileSize;
235}
236
237
238static QgsBox3D globeNodeIdToBox3D( QgsChunkNodeId n, const QgsCoordinateTransform &globeCrsToLatLon )
239{
240 double latMin, latMax, lonMin, lonMax;
241 globeNodeIdToLatLon( n, latMin, latMax, lonMin, lonMax );
242
243 Q_ASSERT( latMax - latMin <= 90 && lonMax - lonMin <= 90 ); // for larger extents we would need more points than just corners
244
245 QVector<double> x, y, z;
246 int pointCount = 4;
247 x.reserve( pointCount );
248 y.reserve( pointCount );
249 z.reserve( pointCount );
250
251 x.push_back( lonMin );
252 y.push_back( latMin );
253 z.push_back( 0 );
254 x.push_back( lonMin );
255 y.push_back( latMax );
256 z.push_back( 0 );
257 x.push_back( lonMax );
258 y.push_back( latMin );
259 z.push_back( 0 );
260 x.push_back( lonMax );
261 y.push_back( latMax );
262 z.push_back( 0 );
263
264 globeCrsToLatLon.transformCoords( pointCount, x.data(), y.data(), z.data(), Qgis::TransformDirection::Reverse );
265
266 QgsBox3D box( QgsVector3D( x[0], y[0], z[0] ), QgsVector3D( x[1], y[1], z[1] ) );
267 box.combineWith( x[2], y[2], z[2] );
268 box.combineWith( x[3], y[3], z[3] );
269 return box;
270}
271
272
273// ---------------
274
275
276QgsGlobeChunkLoader::QgsGlobeChunkLoader( QgsChunkNode *node, const Qgs3DRenderContext &context, QgsTerrainTextureGenerator *textureGenerator, const QgsCoordinateTransform &globeCrsToLatLon )
277 : QgsChunkLoader( node )
278 , mRenderContext( context )
279 , mTextureGenerator( textureGenerator )
280 , mGlobeCrsToLatLon( globeCrsToLatLon )
281{}
282
283void QgsGlobeChunkLoader::start()
284{
285 QgsChunkNode *node = chunk();
286
287 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, [this]( int job, const QImage &img ) {
288 if ( job == mJobId )
289 {
290 mTexture = img;
291 emit finished();
292 }
293 } );
294
295 double latMin, latMax, lonMin, lonMax;
296 globeNodeIdToLatLon( node->tileId(), latMin, latMax, lonMin, lonMax );
297 QgsRectangle extent( lonMin, latMin, lonMax, latMax );
298 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
299}
300
301Qt3DCore::QEntity *QgsGlobeChunkLoader::createEntity( Qt3DCore::QEntity *parent )
302{
303 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
304 {
305 return new Qt3DCore::QEntity( parent );
306 }
307
308 double latMin, latMax, lonMin, lonMax;
309 globeNodeIdToLatLon( mNode->tileId(), latMin, latMax, lonMin, lonMax );
310
311 // This is quite ad-hoc estimation how many slices we need. It could
312 // be improved by basing the calculation on sagitta
313 int d = mNode->tileId().d;
314 int slices;
315 if ( d <= 4 )
316 slices = 19;
317 else if ( d <= 8 )
318 slices = 9;
319 else if ( d <= 12 )
320 slices = 5;
321 else
322 slices = 2;
323
324 QgsMaterialContext materialContext = QgsMaterialContext::fromRenderContext( mRenderContext );
325
326 Qt3DCore::QEntity *e = makeGlobeMesh( lonMin, lonMax, latMin, latMax, slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text(), materialContext );
327 e->setParent( parent );
328 return e;
329}
330
331
332// ---------------
333
334
335QgsGlobeChunkLoaderFactory::QgsGlobeChunkLoaderFactory( Qgs3DMapSettings *mapSettings )
336 : mMapSettings( mapSettings )
337{
338 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *mapSettings );
339
340 // it does not matter what kind of ellipsoid is used, this is for rough estimates
341 mDistanceArea.setEllipsoid( mapSettings->crs().ellipsoidAcronym() );
342
343 mGlobeCrsToLatLon = QgsCoordinateTransform( mapSettings->crs(), mapSettings->crs().toGeographicCrs(), mapSettings->transformContext() );
344
345 mRadiusX = mGlobeCrsToLatLon.transform( QgsVector3D( 0, 0, 0 ), Qgis::TransformDirection::Reverse ).x();
346 mRadiusY = mGlobeCrsToLatLon.transform( QgsVector3D( 90, 0, 0 ), Qgis::TransformDirection::Reverse ).y();
347 mRadiusZ = mGlobeCrsToLatLon.transform( QgsVector3D( 0, 90, 0 ), Qgis::TransformDirection::Reverse ).z();
348}
349
350QgsGlobeChunkLoaderFactory::~QgsGlobeChunkLoaderFactory()
351{}
352
353QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
354{
355 return new QgsGlobeChunkLoader( node, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mTextureGenerator.get(), mGlobeCrsToLatLon );
356}
357
358QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode() const
359{
360 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
361 // use very high error to force immediate switch to level 1 (two hemispheres)
362 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
363 return node;
364}
365
366QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
367{
368 QVector<QgsChunkNode *> children;
369 if ( node->tileId().d == 0 )
370 {
371 double d1 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 90, 0 ) );
372 double d2 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 0, 90 ) );
373 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
374
375 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
376 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
377
378 // two children: western and eastern hemisphere
379 QgsChunkNode *west = new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
380 QgsChunkNode *east = new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
381 children << west << east;
382 }
383 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
384 {
385 QgsChunkNodeId nid = node->tileId();
386
387 double latMin, latMax, lonMin, lonMax;
388 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
389 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
390 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
391 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
392 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
393
394 double d1 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
395 double d2 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
396 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
397
398 children
399 << new QgsChunkNode( cid1, globeNodeIdToBox3D( cid1, mGlobeCrsToLatLon ), error, node )
400 << new QgsChunkNode( cid2, globeNodeIdToBox3D( cid2, mGlobeCrsToLatLon ), error, node )
401 << new QgsChunkNode( cid3, globeNodeIdToBox3D( cid3, mGlobeCrsToLatLon ), error, node )
402 << new QgsChunkNode( cid4, globeNodeIdToBox3D( cid4, mGlobeCrsToLatLon ), error, node );
403 }
404
405 return children;
406}
407
408// ---------------
409
410
411QgsGlobeMapUpdateJob::QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
412 : QgsChunkQueueJob( node )
413 , mTextureGenerator( textureGenerator )
414{}
415
416void QgsGlobeMapUpdateJob::start()
417{
418 QgsChunkNode *node = chunk();
419
420 // extract our terrain texture image from the 3D entity
421 QVector<QgsGlobeMaterial *> materials = node->entity()->componentsOfType<QgsGlobeMaterial>();
422 Q_ASSERT( materials.count() == 1 );
423 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
424 Q_ASSERT( texImages.count() == 1 );
425 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
426 Q_ASSERT( terrainTexImage );
427
428 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, [this, terrainTexImage]( int jobId, const QImage &image ) {
429 if ( mJobId == jobId )
430 {
431 terrainTexImage->setImage( image );
432 mJobId = -1;
433 emit finished();
434 }
435 } );
436 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
437}
438
439void QgsGlobeMapUpdateJob::cancel()
440{
441 if ( mJobId != -1 )
442 mTextureGenerator->cancelJob( mJobId );
443}
444
445
446// ---------------
447
448
450class QgsGlobeMapUpdateJobFactory : public QgsChunkQueueJobFactory
451{
452 public:
453 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings ) { mTextureGenerator = new QgsTerrainTextureGenerator( *mapSettings ); }
454
455 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override { return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk ); }
456
457 private:
458 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
459};
460
461
462// ---------------
463
464
465QgsGlobeEntity::QgsGlobeEntity( Qgs3DMapSettings *mapSettings )
466 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
467{
468 mLayerWatcher = make_qobject_unique<QgsLayerStyleWatcher>( mapSettings );
469 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged, this, &QgsGlobeEntity::invalidateMapImages );
470
471 connect( mapSettings, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, [this, mapSettings] {
472 setShowBoundingBoxes( mapSettings->debugFlags().testFlag( Qgis::Map3DDebugFlag::ShowTerrainBoundingBoxes ) );
473 } );
474 connect( mapSettings, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsGlobeEntity::invalidateMapImages );
475 connect( mapSettings, &Qgs3DMapSettings::showLabelsChanged, this, &QgsGlobeEntity::invalidateMapImages );
476 connect( mapSettings, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsGlobeEntity::invalidateMapImages );
477 connect( mapSettings, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsGlobeEntity::invalidateMapImages );
478
479 mUpdateJobFactory = std::make_unique<QgsGlobeMapUpdateJobFactory>( mapSettings );
480}
481
482QgsGlobeEntity::~QgsGlobeEntity()
483{
484 // cancel / wait for jobs
485 cancelActiveJobs();
486}
487
488QList<QgsRayCastHit> QgsGlobeEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
489{
490 float minDist = -1;
491 QVector3D intersectionPoint;
492 const QList<QgsChunkNode *> active = activeNodes();
493 for ( QgsChunkNode *node : active )
494 {
495 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
496
497 if ( node->entity() && ( minDist < 0 || nodeBbox.distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
498 {
499 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
500 Q_ASSERT( nodeGeoTransform );
501 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
502 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
503 {
504 QVector3D nodeIntPoint;
505 int triangleIndex = -1;
506 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, context.maximumDistance(), nodeGeoTransform->matrix(), nodeIntPoint, triangleIndex );
507 if ( success )
508 {
509 float dist = ( ray.origin() - nodeIntPoint ).length();
510 if ( minDist < 0 || dist < minDist )
511 {
512 minDist = dist;
513 intersectionPoint = nodeIntPoint;
514 }
515 }
516 }
517 }
518 }
519
520 if ( minDist < 0 )
521 return {};
522
523 QgsRayCastHit hit;
524 hit.setDistance( minDist );
525 hit.setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
526 return { hit };
527}
528
529void QgsGlobeEntity::invalidateMapImages()
530{
531 QgsEventTracing::addEvent( QgsEventTracing::Instant, u"3D"_s, u"Invalidate textures"_s );
532
533 // handle active nodes
534
535 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
536
537 // handle inactive nodes afterwards
538
539 QList<QgsChunkNode *> inactiveNodes;
540 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
541 for ( QgsChunkNode *node : descendants )
542 {
543 if ( !node->entity() )
544 continue;
545 if ( mActiveNodes.contains( node ) )
546 continue;
547 if ( !node->parent() )
548 continue; // skip root node because it is not proper QEntity with data
549 inactiveNodes << node;
550 }
551
552 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
553
554 setNeedsUpdate( true );
555}
556
557
@ ShowTerrainBoundingBoxes
Displays bounding boxes of terrain tiles.
Definition qgis.h:4334
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2847
Definition of the world.
void backgroundColorChanged()
Emitted when the background color has changed.
void showTerrainBoundingBoxesChanged()
Emitted when the flag whether terrain's bounding boxes are shown has changed.
void terrainMapThemeChanged()
Emitted when terrain's map theme has changed.
Qgis::Map3DDebugFlags debugFlags() const
Returns the flags that control debugging options for the map.
void showLabelsChanged()
Emitted when the flag whether labels are displayed on terrain tiles has changed.
void showTerrainTilesInfoChanged()
Emitted when the flag whether terrain's tile info is shown has changed.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used in the 3D scene.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Rendering context for preparation of 3D entities.
static Qgs3DRenderContext fromMapSettings(const Qgs3DMapSettings *mapSettings)
Creates an initialized Qgs3DRenderContext instance from given Qgs3DMapSettings.
static void setTextureFiltering(Qt3DRender::QAbstractTexture *texture, const QgsMaterialContext &context)
Sets the default filtering options for a texture.
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.
Axis-aligned bounding box - in world coords.
Definition qgsaabb.h:33
float distanceFromPoint(float x, float y, float z) const
Returns shortest distance from the box to a point.
Definition qgsaabb.cpp:50
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
Handles coordinate transforms between two coordinate systems.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform an array of coordinates to the destination CRS.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Context settings for a material.
static QgsMaterialContext fromRenderContext(const Qgs3DRenderContext &context)
Constructs a material context from the settings in a 3D render context.
Represents a 2D point.
Definition qgspointxy.h:62
A representation of a ray in 3D.
Definition qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition qgsray3d.h:43
Responsible for defining parameters of the ray casting operations in 3D map canvases.
float maximumDistance() const
The maximum distance from ray origin to look for hits when casting a ray.
Contains details about the ray intersecting entities when ray casting in a 3D map canvas.
void setMapCoordinates(const QgsVector3D &point)
Sets the hit point position in 3d map coordinates.
void setDistance(double distance)
Sets the hit's distance from the ray's origin.
A rectangle specified with double values.
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
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
bool rayBoxIntersection(const QgsRay3D &ray, const QgsAABB &nodeBbox)
Tests whether an axis aligned box is intersected by a ray.
bool rayMeshIntersection(Qt3DRender::QGeometryRenderer *geometryRenderer, const QgsRay3D &r, float maxDist, const QMatrix4x4 &worldTransform, QVector3D &intPt, int &triangleIndex)
Tests whether a triangular mesh is intersected by a ray.
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.