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