QGIS API Documentation 4.1.0-Master (9af12b5a203)
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 = new 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 delete mTextureGenerator;
353}
354
355QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
356{
357 return new QgsGlobeChunkLoader( node, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mTextureGenerator, mGlobeCrsToLatLon );
358}
359
360QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode() const
361{
362 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
363 // use very high error to force immediate switch to level 1 (two hemispheres)
364 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
365 return node;
366}
367
368QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
369{
370 QVector<QgsChunkNode *> children;
371 if ( node->tileId().d == 0 )
372 {
373 double d1 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 90, 0 ) );
374 double d2 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 0, 90 ) );
375 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
376
377 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
378 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
379
380 // two children: western and eastern hemisphere
381 QgsChunkNode *west = new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
382 QgsChunkNode *east = new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
383 children << west << east;
384 }
385 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
386 {
387 QgsChunkNodeId nid = node->tileId();
388
389 double latMin, latMax, lonMin, lonMax;
390 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
391 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
392 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
393 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
394 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
395
396 double d1 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
397 double d2 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
398 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
399
400 children
401 << new QgsChunkNode( cid1, globeNodeIdToBox3D( cid1, mGlobeCrsToLatLon ), error, node )
402 << new QgsChunkNode( cid2, globeNodeIdToBox3D( cid2, mGlobeCrsToLatLon ), error, node )
403 << new QgsChunkNode( cid3, globeNodeIdToBox3D( cid3, mGlobeCrsToLatLon ), error, node )
404 << new QgsChunkNode( cid4, globeNodeIdToBox3D( cid4, mGlobeCrsToLatLon ), error, node );
405 }
406
407 return children;
408}
409
410// ---------------
411
412
413QgsGlobeMapUpdateJob::QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
414 : QgsChunkQueueJob( node )
415 , mTextureGenerator( textureGenerator )
416{}
417
418void QgsGlobeMapUpdateJob::start()
419{
420 QgsChunkNode *node = chunk();
421
422 // extract our terrain texture image from the 3D entity
423 QVector<QgsGlobeMaterial *> materials = node->entity()->componentsOfType<QgsGlobeMaterial>();
424 Q_ASSERT( materials.count() == 1 );
425 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
426 Q_ASSERT( texImages.count() == 1 );
427 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
428 Q_ASSERT( terrainTexImage );
429
430 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, [this, terrainTexImage]( int jobId, const QImage &image ) {
431 if ( mJobId == jobId )
432 {
433 terrainTexImage->setImage( image );
434 mJobId = -1;
435 emit finished();
436 }
437 } );
438 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
439}
440
441void QgsGlobeMapUpdateJob::cancel()
442{
443 if ( mJobId != -1 )
444 mTextureGenerator->cancelJob( mJobId );
445}
446
447
448// ---------------
449
450
452class QgsGlobeMapUpdateJobFactory : public QgsChunkQueueJobFactory
453{
454 public:
455 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings ) { mTextureGenerator = new QgsTerrainTextureGenerator( *mapSettings ); }
456
457 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override { return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk ); }
458
459 private:
460 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
461};
462
463
464// ---------------
465
466
467QgsGlobeEntity::QgsGlobeEntity( Qgs3DMapSettings *mapSettings )
468 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
469{
470 mLayerWatcher.reset( new QgsLayerStyleWatcher( mapSettings ) );
471 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged, this, &QgsGlobeEntity::invalidateMapImages );
472
473 connect( mapSettings, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, [this, mapSettings] { setShowBoundingBoxes( mapSettings->showTerrainBoundingBoxes() ); } );
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
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2833
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.
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging).
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.