21#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
22#include <Qt3DRender/QAttribute>
23#include <Qt3DRender/QBuffer>
24#include <Qt3DRender/QGeometry>
30#include <Qt3DCore/QAttribute>
31#include <Qt3DCore/QBuffer>
32#include <Qt3DCore/QGeometry>
39#include <Qt3DCore/QEntity>
40#include <Qt3DRender/QGeometryRenderer>
41#include <Qt3DRender/QTexture>
42#include <Qt3DRender/QTextureImage>
43#include <Qt3DExtras/QTextureMaterial>
60static Qt3DCore::QEntity *makeGlobeMesh(
double lonMin,
double lonMax,
double latMin,
double latMax,
int lonSliceCount,
int latSliceCount,
const QgsCoordinateTransform &globeCrsToLatLon, QImage textureQImage, QString textureDebugText )
62 double lonRange = lonMax - lonMin;
63 double latRange = latMax - latMin;
64 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
65 double latStep = latRange / ( double ) ( latSliceCount - 1 );
67 std::vector<double> x, y, z;
68 int pointCount = latSliceCount * lonSliceCount;
69 x.reserve( pointCount );
70 y.reserve( pointCount );
71 z.reserve( pointCount );
73 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
75 double lat = latSliceIndex * latStep + latMin;
76 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
78 double lon = lonSliceIndex * lonStep + lonMin;
90 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
93 int stride = ( 3 + 2 + 3 ) *
sizeof(
float );
95 QByteArray bufferBytes;
96 bufferBytes.resize( stride * pointCount );
97 float *fptr = (
float * ) bufferBytes.data();
98 for (
int i = 0; i < ( int ) pointCount; ++i )
100 *fptr++ =
static_cast<float>( x[i] - meshOrigin.
x() );
101 *fptr++ =
static_cast<float>( y[i] - meshOrigin.
y() );
102 *fptr++ =
static_cast<float>( z[i] - meshOrigin.
z() );
104 int vi = i / lonSliceCount;
105 int ui = i % lonSliceCount;
106 float v =
static_cast<float>( vi ) /
static_cast<float>( latSliceCount - 1 );
107 float u =
static_cast<float>( ui ) /
static_cast<float>( lonSliceCount - 1 );
111 QVector3D n = QVector3D(
static_cast<float>( x[i] ),
static_cast<float>( y[i] ),
static_cast<float>( z[i] ) ).normalized();
117 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
118 int indices = faces * 3;
120 QByteArray indexBytes;
121 indexBytes.resize( indices *
static_cast<int>(
sizeof( ushort ) ) );
123 quint16 *indexPtr =
reinterpret_cast<quint16 *
>( indexBytes.data() );
124 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
126 int latSliceStartIndex = latSliceIndex * lonSliceCount;
127 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
128 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
130 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
131 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
132 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
134 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
135 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
136 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
142 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
145 vertexBuffer->setData( bufferBytes );
148 indexBuffer->setData( indexBytes );
151 positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
152 positionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
153 positionAttribute->setVertexSize( 3 );
154 positionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
155 positionAttribute->setBuffer( vertexBuffer );
156 positionAttribute->setByteStride( stride );
157 positionAttribute->setCount( pointCount );
160 texCoordAttribute->setName( Qt3DQAttribute::defaultTextureCoordinateAttributeName() );
161 texCoordAttribute->setVertexBaseType( Qt3DQAttribute::Float );
162 texCoordAttribute->setVertexSize( 2 );
163 texCoordAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
164 texCoordAttribute->setBuffer( vertexBuffer );
165 texCoordAttribute->setByteStride( stride );
166 texCoordAttribute->setByteOffset( 3 *
sizeof(
float ) );
167 texCoordAttribute->setCount( pointCount );
170 normalAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
171 normalAttribute->setVertexBaseType( Qt3DQAttribute::Float );
172 normalAttribute->setVertexSize( 3 );
173 normalAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
174 normalAttribute->setBuffer( vertexBuffer );
175 normalAttribute->setByteStride( stride );
176 normalAttribute->setByteOffset( 5 *
sizeof(
float ) );
177 normalAttribute->setCount( pointCount );
180 indexAttribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
181 indexAttribute->setVertexBaseType( Qt3DQAttribute::UnsignedShort );
182 indexAttribute->setBuffer( indexBuffer );
183 indexAttribute->setCount( faces * 3 );
186 geometry->addAttribute( positionAttribute );
187 geometry->addAttribute( texCoordAttribute );
188 geometry->addAttribute( normalAttribute );
189 geometry->addAttribute( indexAttribute );
191 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer( entity );
192 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
193 geomRenderer->setVertexCount( faces * 3 );
194 geomRenderer->setGeometry( geometry );
196 QgsTerrainTextureImage *textureImage =
new QgsTerrainTextureImage( textureQImage,
QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
198 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D( entity );
199 texture->addTextureImage( textureImage );
200 texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
201 texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
203 Qt3DExtras::QTextureMaterial *material =
new Qt3DExtras::QTextureMaterial( entity );
204 material->setTexture( texture );
206 QgsGeoTransform *geoTransform =
new QgsGeoTransform( entity );
207 geoTransform->setGeoTranslation( meshOrigin );
209 entity->addComponent( material );
210 entity->addComponent( geomRenderer );
211 entity->addComponent( geoTransform );
216static void globeNodeIdToLatLon( QgsChunkNodeId n,
double &latMin,
double &latMax,
double &lonMin,
double &lonMax )
218 if ( n == QgsChunkNodeId( 0, 0, 0, 0 ) )
227 double tileSize = 180.0 / std::pow( 2.0, n.d - 1 );
228 lonMin = n.x * tileSize - 180.0;
229 latMin = n.y * tileSize - 90.0;
230 lonMax = lonMin + tileSize;
231 latMax = latMin + tileSize;
237 double latMin, latMax, lonMin, lonMax;
238 globeNodeIdToLatLon( n, latMin, latMax, lonMin, lonMax );
240 Q_ASSERT( latMax - latMin <= 90 && lonMax - lonMin <= 90 );
242 QVector<double> x, y, z;
244 x.reserve( pointCount );
245 y.reserve( pointCount );
246 z.reserve( pointCount );
248 x.push_back( lonMin );
249 y.push_back( latMin );
251 x.push_back( lonMin );
252 y.push_back( latMax );
254 x.push_back( lonMax );
255 y.push_back( latMin );
257 x.push_back( lonMax );
258 y.push_back( latMax );
264 box.combineWith( x[2], y[2], z[2] );
265 box.combineWith( x[3], y[3], z[3] );
273class QgsGlobeChunkLoader :
public QgsChunkLoader
276 QgsGlobeChunkLoader( QgsChunkNode *node, QgsTerrainTextureGenerator *textureGenerator,
const QgsCoordinateTransform &globeCrsToLatLon )
277 : QgsChunkLoader( node )
278 , mTextureGenerator( textureGenerator )
279 , mGlobeCrsToLatLon( globeCrsToLatLon )
281 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [=](
int job,
const QImage &img ) {
289 double latMin, latMax, lonMin, lonMax;
290 globeNodeIdToLatLon( node->tileId(), latMin, latMax, lonMin, lonMax );
292 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
295 Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent )
override
297 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
299 return new Qt3DCore::QEntity( parent );
302 double latMin, latMax, lonMin, lonMax;
303 globeNodeIdToLatLon( mNode->tileId(), latMin, latMax, lonMin, lonMax );
307 int d = mNode->tileId().d;
318 Qt3DCore::QEntity *e = makeGlobeMesh( lonMin, lonMax, latMin, latMax, slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text() );
319 e->setParent( parent );
324 QgsTerrainTextureGenerator *mTextureGenerator;
334class QgsGlobeChunkLoaderFactory :
public QgsChunkLoaderFactory
338 : mMapSettings( mapSettings )
340 mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings );
352 ~QgsGlobeChunkLoaderFactory()
354 delete mTextureGenerator;
357 QgsChunkLoader *createChunkLoader( QgsChunkNode *node )
const override
359 return new QgsGlobeChunkLoader( node, mTextureGenerator, mGlobeCrsToLatLon );
362 QgsChunkNode *createRootNode()
const override
364 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
366 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
370 QVector<QgsChunkNode *> createChildren( QgsChunkNode *node )
const override
372 QVector<QgsChunkNode *> children;
373 if ( node->tileId().d == 0 )
377 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
379 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
380 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
383 QgsChunkNode *west =
new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
384 QgsChunkNode *east =
new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
385 children << west << east;
387 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
389 QgsChunkNodeId nid = node->tileId();
391 double latMin, latMax, lonMin, lonMax;
392 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
393 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
394 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
395 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
396 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
398 double d1 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
399 double d2 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
400 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
402 children <<
new QgsChunkNode( cid1, globeNodeIdToBox3D( cid1, mGlobeCrsToLatLon ), error, node )
403 <<
new QgsChunkNode( cid2, globeNodeIdToBox3D( cid2, mGlobeCrsToLatLon ), error, node )
404 <<
new QgsChunkNode( cid3, globeNodeIdToBox3D( cid3, mGlobeCrsToLatLon ), error, node )
405 <<
new QgsChunkNode( cid4, globeNodeIdToBox3D( cid4, mGlobeCrsToLatLon ), error, node );
413 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
416 double mRadiusX, mRadiusY, mRadiusZ;
424class QgsGlobeMapUpdateJob :
public QgsChunkQueueJob
427 QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
428 : QgsChunkQueueJob( node )
429 , mTextureGenerator( textureGenerator )
432 QVector<Qt3DExtras::QTextureMaterial *> materials = node->entity()->componentsOfType<Qt3DExtras::QTextureMaterial>();
433 Q_ASSERT( materials.count() == 1 );
434 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
435 Q_ASSERT( texImages.count() == 1 );
436 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
437 Q_ASSERT( terrainTexImage );
439 connect( textureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [=](
int jobId,
const QImage &image ) {
440 if ( mJobId == jobId )
442 terrainTexImage->setImage( image );
447 mJobId = textureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
450 void cancel()
override
453 mTextureGenerator->cancelJob( mJobId );
457 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
466class QgsGlobeMapUpdateJobFactory :
public QgsChunkQueueJobFactory
471 mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings );
474 QgsChunkQueueJob *createJob( QgsChunkNode *chunk )
override
476 return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk );
480 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
488 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
499 connectToLayersRepaintRequest();
501 mUpdateJobFactory.reset(
new QgsGlobeMapUpdateJobFactory( mapSettings ) );
504QgsGlobeEntity::~QgsGlobeEntity()
515 QVector3D intersectionPoint;
516 const QList<QgsChunkNode *> active = activeNodes();
517 for ( QgsChunkNode *node : active )
521 if ( node->entity() && ( minDist < 0 || nodeBbox.
distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
523 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
524 Q_ASSERT( nodeGeoTransform );
525 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
526 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
528 QVector3D nodeIntPoint;
529 int triangleIndex = -1;
530 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, nodeGeoTransform->matrix(), nodeIntPoint, triangleIndex );
533 float dist = ( ray.origin() - nodeIntPoint ).length();
534 if ( minDist < 0 || dist < minDist )
537 intersectionPoint = nodeIntPoint;
544 QVector<QgsRayCastingUtils::RayHit> result;
553void QgsGlobeEntity::invalidateMapImages()
555 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral(
"3D" ), QStringLiteral(
"Invalidate textures" ) );
559 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
563 QList<QgsChunkNode *> inactiveNodes;
564 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
565 for ( QgsChunkNode *node : descendants )
567 if ( !node->entity() )
569 if ( mActiveNodes.contains( node ) )
571 if ( !node->parent() )
573 inactiveNodes << node;
576 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
578 setNeedsUpdate(
true );
581void QgsGlobeEntity::onLayersChanged()
583 connectToLayersRepaintRequest();
584 invalidateMapImages();
587void QgsGlobeEntity::connectToLayersRepaintRequest()
589 for (
QgsMapLayer *layer : std::as_const( mLayers ) )
594 mLayers = mMapSettings->layers();
596 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.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
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 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.
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
Helper struct to store ray casting parameters.
Helper struct to store ray casting results.