21#include "moc_qgsglobechunkedentity.cpp"
23#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
24#include <Qt3DRender/QAttribute>
25#include <Qt3DRender/QBuffer>
26#include <Qt3DRender/QGeometry>
32#include <Qt3DCore/QAttribute>
33#include <Qt3DCore/QBuffer>
34#include <Qt3DCore/QGeometry>
41#include <Qt3DCore/QEntity>
42#include <Qt3DRender/QGeometryRenderer>
43#include <Qt3DRender/QTexture>
44#include <Qt3DRender/QTextureImage>
64static Qt3DCore::QEntity *makeGlobeMesh(
double lonMin,
double lonMax,
double latMin,
double latMax,
int lonSliceCount,
int latSliceCount,
const QgsCoordinateTransform &globeCrsToLatLon, QImage textureQImage, QString textureDebugText )
66 double lonRange = lonMax - lonMin;
67 double latRange = latMax - latMin;
68 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
69 double latStep = latRange / ( double ) ( latSliceCount - 1 );
71 std::vector<double> x, y, z;
72 int pointCount = latSliceCount * lonSliceCount;
73 x.reserve( pointCount );
74 y.reserve( pointCount );
75 z.reserve( pointCount );
77 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
79 double lat = latSliceIndex * latStep + latMin;
80 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
82 double lon = lonSliceIndex * lonStep + lonMin;
94 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
97 int stride = ( 3 + 2 + 3 ) *
sizeof(
float );
99 QByteArray bufferBytes;
100 bufferBytes.resize( stride * pointCount );
101 float *fptr = (
float * ) bufferBytes.data();
102 for (
int i = 0; i < ( int ) pointCount; ++i )
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() );
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 );
115 QVector3D n = QVector3D(
static_cast<float>( x[i] ),
static_cast<float>( y[i] ),
static_cast<float>( z[i] ) ).normalized();
121 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
122 int indices = faces * 3;
124 QByteArray indexBytes;
125 indexBytes.resize( indices *
static_cast<int>(
sizeof( ushort ) ) );
127 quint16 *indexPtr =
reinterpret_cast<quint16 *
>( indexBytes.data() );
128 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
130 int latSliceStartIndex = latSliceIndex * lonSliceCount;
131 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
132 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
134 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
135 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
136 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
138 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
139 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
140 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
146 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
149 vertexBuffer->setData( bufferBytes );
152 indexBuffer->setData( indexBytes );
155 positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
156 positionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
157 positionAttribute->setVertexSize( 3 );
158 positionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
159 positionAttribute->setBuffer( vertexBuffer );
160 positionAttribute->setByteStride( stride );
161 positionAttribute->setCount( pointCount );
164 texCoordAttribute->setName( Qt3DQAttribute::defaultTextureCoordinateAttributeName() );
165 texCoordAttribute->setVertexBaseType( Qt3DQAttribute::Float );
166 texCoordAttribute->setVertexSize( 2 );
167 texCoordAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
168 texCoordAttribute->setBuffer( vertexBuffer );
169 texCoordAttribute->setByteStride( stride );
170 texCoordAttribute->setByteOffset( 3 *
sizeof(
float ) );
171 texCoordAttribute->setCount( pointCount );
174 normalAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
175 normalAttribute->setVertexBaseType( Qt3DQAttribute::Float );
176 normalAttribute->setVertexSize( 3 );
177 normalAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
178 normalAttribute->setBuffer( vertexBuffer );
179 normalAttribute->setByteStride( stride );
180 normalAttribute->setByteOffset( 5 *
sizeof(
float ) );
181 normalAttribute->setCount( pointCount );
184 indexAttribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
185 indexAttribute->setVertexBaseType( Qt3DQAttribute::UnsignedShort );
186 indexAttribute->setBuffer( indexBuffer );
187 indexAttribute->setCount( faces * 3 );
190 geometry->addAttribute( positionAttribute );
191 geometry->addAttribute( texCoordAttribute );
192 geometry->addAttribute( normalAttribute );
193 geometry->addAttribute( indexAttribute );
195 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer( entity );
196 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
197 geomRenderer->setVertexCount( faces * 3 );
198 geomRenderer->setGeometry( geometry );
200 QgsTerrainTextureImage *textureImage =
new QgsTerrainTextureImage( textureQImage,
QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
202 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D( entity );
203 texture->addTextureImage( textureImage );
204 texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
205 texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
207 QgsGlobeMaterial *material =
new QgsGlobeMaterial( entity );
208 material->setTexture( texture );
210 QgsGeoTransform *geoTransform =
new QgsGeoTransform( entity );
211 geoTransform->setGeoTranslation( meshOrigin );
213 entity->addComponent( material );
214 entity->addComponent( geomRenderer );
215 entity->addComponent( geoTransform );
220static void globeNodeIdToLatLon( QgsChunkNodeId n,
double &latMin,
double &latMax,
double &lonMin,
double &lonMax )
222 if ( n == QgsChunkNodeId( 0, 0, 0, 0 ) )
231 double tileSize = 180.0 / std::pow( 2.0, n.d - 1 );
232 lonMin = n.x * tileSize - 180.0;
233 latMin = n.y * tileSize - 90.0;
234 lonMax = lonMin + tileSize;
235 latMax = latMin + tileSize;
241 double latMin, latMax, lonMin, lonMax;
242 globeNodeIdToLatLon( n, latMin, latMax, lonMin, lonMax );
244 Q_ASSERT( latMax - latMin <= 90 && lonMax - lonMin <= 90 );
246 QVector<double> x, y, z;
248 x.reserve( pointCount );
249 y.reserve( pointCount );
250 z.reserve( pointCount );
252 x.push_back( lonMin );
253 y.push_back( latMin );
255 x.push_back( lonMin );
256 y.push_back( latMax );
258 x.push_back( lonMax );
259 y.push_back( latMin );
261 x.push_back( lonMax );
262 y.push_back( latMax );
268 box.combineWith( x[2], y[2], z[2] );
269 box.combineWith( x[3], y[3], z[3] );
277QgsGlobeChunkLoader::QgsGlobeChunkLoader( QgsChunkNode *node, QgsTerrainTextureGenerator *textureGenerator,
const QgsCoordinateTransform &globeCrsToLatLon )
278 : QgsChunkLoader( node )
279 , mTextureGenerator( textureGenerator )
280 , mGlobeCrsToLatLon( globeCrsToLatLon )
284void QgsGlobeChunkLoader::start()
286 QgsChunkNode *node = chunk();
288 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this](
int job,
const QImage &img ) {
296 double latMin, latMax, lonMin, lonMax;
297 globeNodeIdToLatLon( node->tileId(), latMin, latMax, lonMin, lonMax );
299 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
302Qt3DCore::QEntity *QgsGlobeChunkLoader::createEntity( Qt3DCore::QEntity *parent )
304 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
306 return new Qt3DCore::QEntity( parent );
309 double latMin, latMax, lonMin, lonMax;
310 globeNodeIdToLatLon( mNode->tileId(), latMin, latMax, lonMin, lonMax );
314 int d = mNode->tileId().d;
325 Qt3DCore::QEntity *e = makeGlobeMesh( lonMin, lonMax, latMin, latMax, slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text() );
326 e->setParent( parent );
334QgsGlobeChunkLoaderFactory::QgsGlobeChunkLoaderFactory(
Qgs3DMapSettings *mapSettings )
335 : mMapSettings( mapSettings )
337 mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings );
349QgsGlobeChunkLoaderFactory::~QgsGlobeChunkLoaderFactory()
351 delete mTextureGenerator;
354QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
356 return new QgsGlobeChunkLoader( node, mTextureGenerator, mGlobeCrsToLatLon );
359QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode()
const
361 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
363 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
367QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
369 QVector<QgsChunkNode *> children;
370 if ( node->tileId().d == 0 )
374 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
376 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
377 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
380 QgsChunkNode *west =
new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
381 QgsChunkNode *east =
new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
382 children << west << east;
384 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
386 QgsChunkNodeId nid = node->tileId();
388 double latMin, latMax, lonMin, lonMax;
389 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
390 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
391 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
392 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
393 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
395 double d1 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
396 double d2 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
397 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
399 children <<
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 );
411QgsGlobeMapUpdateJob::QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
412 : QgsChunkQueueJob( node )
413 , mTextureGenerator( textureGenerator )
417void QgsGlobeMapUpdateJob::start()
419 QgsChunkNode *node = chunk();
422 QVector<QgsGlobeMaterial *> materials = node->entity()->componentsOfType<QgsGlobeMaterial>();
423 Q_ASSERT( materials.count() == 1 );
424 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
425 Q_ASSERT( texImages.count() == 1 );
426 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
427 Q_ASSERT( terrainTexImage );
429 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this, terrainTexImage](
int jobId,
const QImage &image ) {
430 if ( mJobId == jobId )
432 terrainTexImage->setImage( image );
437 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
440void QgsGlobeMapUpdateJob::cancel()
443 mTextureGenerator->cancelJob( mJobId );
451class QgsGlobeMapUpdateJobFactory :
public QgsChunkQueueJobFactory
454 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings )
456 mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings );
459 QgsChunkQueueJob *createJob( QgsChunkNode *chunk )
override
461 return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk );
465 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
473 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
484 connectToLayersRepaintRequest();
486 mUpdateJobFactory = std::make_unique<QgsGlobeMapUpdateJobFactory>( mapSettings );
489QgsGlobeEntity::~QgsGlobeEntity()
498 QVector3D intersectionPoint;
499 const QList<QgsChunkNode *> active = activeNodes();
500 for ( QgsChunkNode *node : active )
506 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
507 Q_ASSERT( nodeGeoTransform );
508 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
509 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
511 QVector3D nodeIntPoint;
512 int triangleIndex = -1;
516 float dist = ( ray.
origin() - nodeIntPoint ).length();
517 if ( minDist < 0 || dist < minDist )
520 intersectionPoint = nodeIntPoint;
532 hit.
setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
537void QgsGlobeEntity::invalidateMapImages()
539 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral(
"3D" ), QStringLiteral(
"Invalidate textures" ) );
543 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
547 QList<QgsChunkNode *> inactiveNodes;
548 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
549 for ( QgsChunkNode *node : descendants )
551 if ( !node->entity() )
553 if ( mActiveNodes.contains( node ) )
555 if ( !node->parent() )
557 inactiveNodes << node;
560 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
562 setNeedsUpdate(
true );
565void QgsGlobeEntity::onLayersChanged()
567 connectToLayersRepaintRequest();
568 invalidateMapImages();
571void QgsGlobeEntity::connectToLayersRepaintRequest()
573 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
578 mLayers = mMapSettings->layers();
580 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
@ Reverse
Reverse/inverse transform (from destination to source).
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 layersChanged()
Emitted when the list of map layers for 3d rendering 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...
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.
float distanceFromPoint(float x, float y, float z) const
Returns shortest distance from the box to a point.
A 3-dimensional box composed of x, y, z coordinates.
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.
Base class for all map layer types.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
A representation of a ray in 3D.
QVector3D origin() const
Returns the origin of the ray.
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...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
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.
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry