QGIS API Documentation 4.3.0-Master (0cfde48c85b)
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"
28#include "qgsglobeutils_p.h"
30#include "qgsray3d.h"
31#include "qgsraycastcontext.h"
32#include "qgsraycastingutils.h"
35
36#include <QByteArray>
37#include <QImage>
38#include <QString>
39#include <Qt3DCore/QAttribute>
40#include <Qt3DCore/QBuffer>
41#include <Qt3DCore/QEntity>
42#include <Qt3DCore/QGeometry>
43#include <Qt3DRender/QGeometryRenderer>
44#include <Qt3DRender/QTexture>
45#include <Qt3DRender/QTextureImage>
46
47#include "moc_qgsglobechunkedentity.cpp"
48
49using namespace Qt::StringLiterals;
50
52
53static Qt3DCore::QEntity *makeGlobeMesh(
54 double lonMin,
55 double lonMax,
56 double latMin,
57 double latMax,
58 int lonSliceCount,
59 int latSliceCount,
60 const QgsCoordinateTransform &globeCrsToLatLon,
61 QImage textureQImage,
62 QString textureDebugText,
63 const QgsMaterialContext &context
64)
65{
66 double lonRange = lonMax - lonMin;
67 double latRange = latMax - latMin;
68 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
69 double latStep = latRange / ( double ) ( latSliceCount - 1 );
70
71 std::vector<double> x, y, z;
72 int pointCount = latSliceCount * lonSliceCount;
73 x.reserve( pointCount );
74 y.reserve( pointCount );
75 z.reserve( pointCount );
76
77 for ( int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
78 {
79 double lat = latSliceIndex * latStep + latMin;
80 for ( int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
81 {
82 double lon = lonSliceIndex * lonStep + lonMin;
83 x.push_back( lon );
84 y.push_back( lat );
85 z.push_back( 0 );
86 }
87 }
88
89 globeCrsToLatLon.transformCoords( pointCount, x.data(), y.data(), z.data(), Qgis::TransformDirection::Reverse );
90
91 // estimate origin of coordinates for this tile, to make the relative coordinates
92 // small to avoid numerical precision issues when rendering
93 // (avoids mesh jumping around when zoomed in very close to it)
94 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
95 QgsVector3D meshOrigin = globeCrsToLatLon.transform( meshOriginLatLon, Qgis::TransformDirection::Reverse );
96
97 int stride = ( 3 + 2 + 3 ) * sizeof( float );
98
99 QByteArray bufferBytes;
100 bufferBytes.resize( stride * pointCount );
101 float *fptr = ( float * ) bufferBytes.data();
102 for ( int i = 0; i < ( int ) pointCount; ++i )
103 {
104 *fptr++ = static_cast<float>( x[i] - meshOrigin.x() );
105 *fptr++ = static_cast<float>( y[i] - meshOrigin.y() );
106 *fptr++ = static_cast<float>( z[i] - meshOrigin.z() );
107
108 int vi = i / lonSliceCount;
109 int ui = i % lonSliceCount;
110 float v = static_cast<float>( vi ) / static_cast<float>( latSliceCount - 1 );
111 float u = static_cast<float>( ui ) / static_cast<float>( lonSliceCount - 1 );
112 *fptr++ = u;
113 *fptr++ = 1 - v;
114
115 QVector3D n = QVector3D( static_cast<float>( x[i] ), static_cast<float>( y[i] ), static_cast<float>( z[i] ) ).normalized();
116 *fptr++ = n.x();
117 *fptr++ = n.y();
118 *fptr++ = n.z();
119 }
120
121 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
122 int indices = faces * 3;
123
124 QByteArray indexBytes;
125 indexBytes.resize( indices * static_cast<int>( sizeof( ushort ) ) );
126
127 quint16 *indexPtr = reinterpret_cast<quint16 *>( indexBytes.data() );
128 for ( int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
129 {
130 int latSliceStartIndex = latSliceIndex * lonSliceCount;
131 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
132 for ( int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
133 {
134 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
135 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
136 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
137
138 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
139 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
140 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
141
142 indexPtr += 6;
143 }
144 }
145
146 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
147
148 Qt3DCore::QBuffer *vertexBuffer = new Qt3DCore::QBuffer( entity );
149 vertexBuffer->setData( bufferBytes );
150
151 Qt3DCore::QBuffer *indexBuffer = new Qt3DCore::QBuffer( entity );
152 indexBuffer->setData( indexBytes );
153
154 Qt3DCore::QAttribute *positionAttribute = new Qt3DCore::QAttribute( entity );
155 positionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
156 positionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
157 positionAttribute->setVertexSize( 3 );
158 positionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
159 positionAttribute->setBuffer( vertexBuffer );
160 positionAttribute->setByteStride( stride );
161 positionAttribute->setCount( pointCount );
162
163 Qt3DCore::QAttribute *texCoordAttribute = new Qt3DCore::QAttribute( entity );
164 texCoordAttribute->setName( Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName() );
165 texCoordAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
166 texCoordAttribute->setVertexSize( 2 );
167 texCoordAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
168 texCoordAttribute->setBuffer( vertexBuffer );
169 texCoordAttribute->setByteStride( stride );
170 texCoordAttribute->setByteOffset( 3 * sizeof( float ) );
171 texCoordAttribute->setCount( pointCount );
172
173 Qt3DCore::QAttribute *normalAttribute = new Qt3DCore::QAttribute( entity );
174 normalAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
175 normalAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
176 normalAttribute->setVertexSize( 3 );
177 normalAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
178 normalAttribute->setBuffer( vertexBuffer );
179 normalAttribute->setByteStride( stride );
180 normalAttribute->setByteOffset( 5 * sizeof( float ) );
181 normalAttribute->setCount( pointCount );
182
183 Qt3DCore::QAttribute *indexAttribute = new Qt3DCore::QAttribute( entity );
184 indexAttribute->setAttributeType( Qt3DCore::QAttribute::IndexAttribute );
185 indexAttribute->setVertexBaseType( Qt3DCore::QAttribute::UnsignedShort );
186 indexAttribute->setBuffer( indexBuffer );
187 indexAttribute->setCount( faces * 3 );
188
189 Qt3DCore::QGeometry *geometry = new Qt3DCore::QGeometry( entity );
190 geometry->addAttribute( positionAttribute );
191 geometry->addAttribute( texCoordAttribute );
192 geometry->addAttribute( normalAttribute );
193 geometry->addAttribute( indexAttribute );
194
195 Qt3DRender::QGeometryRenderer *geomRenderer = new Qt3DRender::QGeometryRenderer( entity );
196 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
197 geomRenderer->setVertexCount( faces * 3 );
198 geomRenderer->setGeometry( geometry );
199
200 QgsTerrainTextureImage *textureImage = new QgsTerrainTextureImage( textureQImage, QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
201
202 Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D( entity );
203 Qgs3DUtils::setTextureFiltering( texture, context );
204 texture->addTextureImage( textureImage );
205 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
206
207 QgsGlobeMaterial *material = new QgsGlobeMaterial( entity );
208 material->setTexture( texture );
209
210 QgsGeoTransform *geoTransform = new QgsGeoTransform( entity );
211 geoTransform->setGeoTranslation( meshOrigin );
212
213 entity->addComponent( material );
214 entity->addComponent( geomRenderer );
215 entity->addComponent( geoTransform );
216 return entity;
217}
218
219
220// ---------------
221
222
223QgsGlobeChunkLoader::QgsGlobeChunkLoader( QgsChunkNode *node, const Qgs3DRenderContext &context, QgsTerrainTextureGenerator *textureGenerator, const QgsCoordinateTransform &globeCrsToLatLon )
224 : QgsChunkLoader( node )
225 , mRenderContext( context )
226 , mTextureGenerator( textureGenerator )
227 , mGlobeCrsToLatLon( globeCrsToLatLon )
228{}
229
230void QgsGlobeChunkLoader::start()
231{
232 QgsChunkNode *node = chunk();
233
234 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, [this]( int job, const QImage &img ) {
235 if ( job == mJobId )
236 {
237 mTexture = img;
238 emit finished();
239 }
240 } );
241
242 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( node->tileId() );
243 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
244}
245
246Qt3DCore::QEntity *QgsGlobeChunkLoader::createEntity( Qt3DCore::QEntity *parent )
247{
248 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
249 {
250 return new Qt3DCore::QEntity( parent );
251 }
252
253 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( mNode->tileId() );
254
255 // This is quite ad-hoc estimation how many slices we need. It could
256 // be improved by basing the calculation on sagitta
257 int d = mNode->tileId().d;
258 int slices;
259 if ( d <= 4 )
260 slices = 19;
261 else if ( d <= 8 )
262 slices = 9;
263 else if ( d <= 12 )
264 slices = 5;
265 else
266 slices = 2;
267
268 QgsMaterialContext materialContext = QgsMaterialContext::fromRenderContext( mRenderContext );
269
270 Qt3DCore::QEntity *e = makeGlobeMesh( extent.xMinimum(), extent.xMaximum(), extent.yMinimum(), extent.yMaximum(), slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text(), materialContext );
271 e->setParent( parent );
272 return e;
273}
274
275
276// ---------------
277
278
279QgsGlobeChunkLoaderFactory::QgsGlobeChunkLoaderFactory( Qgs3DMapSettings *mapSettings )
280 : mMapSettings( mapSettings )
281{
282 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *mapSettings );
283
284 // it does not matter what kind of ellipsoid is used, this is for rough estimates
285 mDistanceArea.setEllipsoid( mapSettings->crs().ellipsoidAcronym() );
286
287 mGlobeCrsToLatLon = QgsCoordinateTransform( mapSettings->crs(), mapSettings->crs().toGeographicCrs(), mapSettings->transformContext() );
288
289 mRadius = QgsGlobeUtils::ellipsoidRadius( mGlobeCrsToLatLon );
290}
291
292QgsGlobeChunkLoaderFactory::~QgsGlobeChunkLoaderFactory()
293{}
294
295QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
296{
297 return new QgsGlobeChunkLoader( node, Qgs3DRenderContext::fromMapSettings( mMapSettings ), mTextureGenerator.get(), mGlobeCrsToLatLon );
298}
299
300QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode() const
301{
302 const QgsChunkNodeId rootId( 0, 0, 0, 0 );
303 const QgsBox3D rootNodeBox3D( -mRadius.x(), -mRadius.y(), -mRadius.z(), mRadius.x(), mRadius.y(), mRadius.z() );
304 // use very high error to force immediate switch to level 1 (two hemispheres)
305 QgsChunkNode *node = new QgsChunkNode( rootId, rootNodeBox3D, 999'999 );
306 return node;
307}
308
309QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
310{
311 QVector<QgsChunkNode *> children;
312 if ( node->tileId().d == 0 )
313 {
314 double d1 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 90, 0 ) );
315 double d2 = mDistanceArea.measureLine( QgsPointXY( 0, 0 ), QgsPointXY( 0, 90 ) );
316 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
317
318 const QgsChunkNodeId westId( 1, 0, 0, 0 );
319 const QgsChunkNodeId eastId( 1, 1, 0, 0 );
320
321 // two children: western and eastern hemisphere
322 QgsChunkNode *west = new QgsChunkNode( westId, QgsBox3D( -mRadius.x(), -mRadius.y(), -mRadius.z(), mRadius.x(), 0, mRadius.z() ), error, node );
323 QgsChunkNode *east = new QgsChunkNode( eastId, QgsBox3D( -mRadius.x(), 0, -mRadius.z(), mRadius.x(), mRadius.y(), mRadius.z() ), error, node );
324 children << west << east;
325 }
326 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
327 {
328 QgsChunkNodeId nid = node->tileId();
329
330 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( nid );
331 const double lonMin = extent.xMinimum(), lonMax = extent.xMaximum();
332 const double latMin = extent.yMinimum(), latMax = extent.yMaximum();
333 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
334 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
335 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
336 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
337
338 double d1 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
339 double d2 = mDistanceArea.measureLine( QgsPointXY( lonMin, latMin ), QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
340 float error = static_cast<float>( std::max( d1, d2 ) ) / static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
341
342 children
343 << new QgsChunkNode( cid1, QgsGlobeUtils::nodeIdToBox3D( cid1, mGlobeCrsToLatLon ), error, node )
344 << new QgsChunkNode( cid2, QgsGlobeUtils::nodeIdToBox3D( cid2, mGlobeCrsToLatLon ), error, node )
345 << new QgsChunkNode( cid3, QgsGlobeUtils::nodeIdToBox3D( cid3, mGlobeCrsToLatLon ), error, node )
346 << new QgsChunkNode( cid4, QgsGlobeUtils::nodeIdToBox3D( cid4, mGlobeCrsToLatLon ), error, node );
347 }
348
349 return children;
350}
351
352// ---------------
353
354
355QgsGlobeMapUpdateJob::QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
356 : QgsChunkQueueJob( node )
357 , mTextureGenerator( textureGenerator )
358{}
359
360void QgsGlobeMapUpdateJob::start()
361{
362 QgsChunkNode *node = chunk();
363
364 // extract our terrain texture image from the 3D entity
365 QVector<QgsGlobeMaterial *> materials = node->entity()->componentsOfType<QgsGlobeMaterial>();
366 Q_ASSERT( materials.count() == 1 );
367 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
368 Q_ASSERT( texImages.count() == 1 );
369 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
370 Q_ASSERT( terrainTexImage );
371
372 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady, this, [this, terrainTexImage]( int jobId, const QImage &image ) {
373 if ( mJobId == jobId )
374 {
375 terrainTexImage->setImage( image );
376 mJobId = -1;
377 emit finished();
378 }
379 } );
380 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
381}
382
383void QgsGlobeMapUpdateJob::cancel()
384{
385 if ( mJobId != -1 )
386 mTextureGenerator->cancelJob( mJobId );
387}
388
389
390// ---------------
391
392
394class QgsGlobeMapUpdateJobFactory : public QgsChunkQueueJobFactory
395{
396 public:
397 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings ) { mTextureGenerator = new QgsTerrainTextureGenerator( *mapSettings ); }
398
399 QgsChunkQueueJob *createJob( QgsChunkNode *chunk ) override { return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk ); }
400
401 private:
402 QgsTerrainTextureGenerator *mTextureGenerator = nullptr;
403};
404
405
406// ---------------
407
408
409QgsGlobeEntity::QgsGlobeEntity( Qgs3DMapSettings *mapSettings )
410 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
411{
412 mLayerWatcher = make_qobject_unique<QgsLayerStyleWatcher>( mapSettings );
413 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged, this, &QgsGlobeEntity::invalidateMapImages );
414
415 connect( mapSettings, &Qgs3DMapSettings::showTerrainBoundingBoxesChanged, this, [this, mapSettings] {
416 setShowBoundingBoxes( mapSettings->debugFlags().testFlag( Qgis::Map3DDebugFlag::ShowTerrainBoundingBoxes ) );
417 } );
418 connect( mapSettings, &Qgs3DMapSettings::showTerrainTilesInfoChanged, this, &QgsGlobeEntity::invalidateMapImages );
419 connect( mapSettings, &Qgs3DMapSettings::showLabelsChanged, this, &QgsGlobeEntity::invalidateMapImages );
420 connect( mapSettings, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsGlobeEntity::invalidateMapImages );
421 connect( mapSettings, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsGlobeEntity::invalidateMapImages );
422
423 mUpdateJobFactory = std::make_unique<QgsGlobeMapUpdateJobFactory>( mapSettings );
424}
425
426QgsGlobeEntity::~QgsGlobeEntity()
427{
428 // cancel / wait for jobs
429 cancelActiveJobs();
430}
431
432QList<QgsRayCastHit> QgsGlobeEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
433{
434 float minDist = -1;
435 QVector3D intersectionPoint;
436 const QList<QgsChunkNode *> active = activeNodes();
437 for ( QgsChunkNode *node : active )
438 {
439 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
440
441 if ( node->entity() && ( minDist < 0 || nodeBbox.distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
442 {
443 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
444 Q_ASSERT( nodeGeoTransform );
445 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
446 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
447 {
448 QVector3D nodeIntPoint;
449 int triangleIndex = -1;
450 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, context.maximumDistance(), nodeGeoTransform->matrix(), nodeIntPoint, triangleIndex );
451 if ( success )
452 {
453 float dist = ( ray.origin() - nodeIntPoint ).length();
454 if ( minDist < 0 || dist < minDist )
455 {
456 minDist = dist;
457 intersectionPoint = nodeIntPoint;
458 }
459 }
460 }
461 }
462 }
463
464 if ( minDist < 0 )
465 return {};
466
467 QgsRayCastHit hit;
468 hit.setDistance( minDist );
469 hit.setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
470 return { hit };
471}
472
473void QgsGlobeEntity::invalidateMapImages()
474{
475 QgsEventTracing::addEvent( QgsEventTracing::Instant, u"3D"_s, u"Invalidate textures"_s );
476
477 // handle active nodes
478
479 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
480
481 // handle inactive nodes afterwards
482
483 QList<QgsChunkNode *> inactiveNodes;
484 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
485 for ( QgsChunkNode *node : descendants )
486 {
487 if ( !node->entity() )
488 continue;
489 if ( mActiveNodes.contains( node ) )
490 continue;
491 if ( !node->parent() )
492 continue; // skip root node because it is not proper QEntity with data
493 inactiveNodes << node;
494 }
495
496 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
497
498 setNeedsUpdate( true );
499}
500
501
@ ShowTerrainBoundingBoxes
Displays bounding boxes of terrain tiles.
Definition qgis.h:4399
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2884
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.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
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.