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>
46#include "moc_qgsglobechunkedentity.cpp"
48using namespace Qt::StringLiterals;
52static Qt3DCore::QEntity *makeGlobeMesh(
61 QString textureDebugText,
65 double lonRange = lonMax - lonMin;
66 double latRange = latMax - latMin;
67 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
68 double latStep = latRange / ( double ) ( latSliceCount - 1 );
70 std::vector<double> x, y, z;
71 int pointCount = latSliceCount * lonSliceCount;
72 x.reserve( pointCount );
73 y.reserve( pointCount );
74 z.reserve( pointCount );
76 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
78 double lat = latSliceIndex * latStep + latMin;
79 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
81 double lon = lonSliceIndex * lonStep + lonMin;
93 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
96 int stride = ( 3 + 2 + 3 ) *
sizeof(
float );
98 QByteArray bufferBytes;
99 bufferBytes.resize( stride * pointCount );
100 float *fptr = (
float * ) bufferBytes.data();
101 for (
int i = 0; i < ( int ) pointCount; ++i )
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() );
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 );
114 QVector3D n = QVector3D(
static_cast<float>( x[i] ),
static_cast<float>( y[i] ),
static_cast<float>( z[i] ) ).normalized();
120 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
121 int indices = faces * 3;
123 QByteArray indexBytes;
124 indexBytes.resize( indices *
static_cast<int>(
sizeof( ushort ) ) );
126 quint16 *indexPtr =
reinterpret_cast<quint16 *
>( indexBytes.data() );
127 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
129 int latSliceStartIndex = latSliceIndex * lonSliceCount;
130 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
131 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
133 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
134 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
135 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
137 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
138 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
139 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
145 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
147 Qt3DCore::QBuffer *vertexBuffer =
new Qt3DCore::QBuffer( entity );
148 vertexBuffer->setData( bufferBytes );
150 Qt3DCore::QBuffer *indexBuffer =
new Qt3DCore::QBuffer( entity );
151 indexBuffer->setData( indexBytes );
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 );
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 );
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 );
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 );
188 Qt3DCore::QGeometry *geometry =
new Qt3DCore::QGeometry( entity );
189 geometry->addAttribute( positionAttribute );
190 geometry->addAttribute( texCoordAttribute );
191 geometry->addAttribute( normalAttribute );
192 geometry->addAttribute( indexAttribute );
194 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer( entity );
195 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
196 geomRenderer->setVertexCount( faces * 3 );
197 geomRenderer->setGeometry( geometry );
199 QgsTerrainTextureImage *textureImage =
new QgsTerrainTextureImage( textureQImage,
QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
201 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D( entity );
203 texture->addTextureImage( textureImage );
204 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
206 QgsGlobeMaterial *material =
new QgsGlobeMaterial( entity );
207 material->setTexture( texture );
209 QgsGeoTransform *geoTransform =
new QgsGeoTransform( entity );
210 geoTransform->setGeoTranslation( meshOrigin );
212 entity->addComponent( material );
213 entity->addComponent( geomRenderer );
214 entity->addComponent( geoTransform );
219static void globeNodeIdToLatLon( QgsChunkNodeId n,
double &latMin,
double &latMax,
double &lonMin,
double &lonMax )
221 if ( n == QgsChunkNodeId( 0, 0, 0, 0 ) )
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;
240 double latMin, latMax, lonMin, lonMax;
241 globeNodeIdToLatLon( n, latMin, latMax, lonMin, lonMax );
243 Q_ASSERT( latMax - latMin <= 90 && lonMax - lonMin <= 90 );
245 QVector<double> x, y, z;
247 x.reserve( pointCount );
248 y.reserve( pointCount );
249 z.reserve( pointCount );
251 x.push_back( lonMin );
252 y.push_back( latMin );
254 x.push_back( lonMin );
255 y.push_back( latMax );
257 x.push_back( lonMax );
258 y.push_back( latMin );
260 x.push_back( lonMax );
261 y.push_back( latMax );
267 box.combineWith( x[2], y[2], z[2] );
268 box.combineWith( x[3], y[3], z[3] );
277 : QgsChunkLoader( node )
278 , mRenderContext( context )
279 , mTextureGenerator( textureGenerator )
280 , mGlobeCrsToLatLon( globeCrsToLatLon )
283void QgsGlobeChunkLoader::start()
285 QgsChunkNode *node = chunk();
287 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this](
int job,
const QImage &img ) {
295 double latMin, latMax, lonMin, lonMax;
296 globeNodeIdToLatLon( node->tileId(), latMin, latMax, lonMin, lonMax );
298 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
301Qt3DCore::QEntity *QgsGlobeChunkLoader::createEntity( Qt3DCore::QEntity *parent )
303 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
305 return new Qt3DCore::QEntity( parent );
308 double latMin, latMax, lonMin, lonMax;
309 globeNodeIdToLatLon( mNode->tileId(), latMin, latMax, lonMin, lonMax );
313 int d = mNode->tileId().d;
326 Qt3DCore::QEntity *e = makeGlobeMesh( lonMin, lonMax, latMin, latMax, slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text(), materialContext );
327 e->setParent( parent );
335QgsGlobeChunkLoaderFactory::QgsGlobeChunkLoaderFactory(
Qgs3DMapSettings *mapSettings )
336 : mMapSettings( mapSettings )
338 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *mapSettings );
350QgsGlobeChunkLoaderFactory::~QgsGlobeChunkLoaderFactory()
353QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
358QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode()
const
360 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
362 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
366QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
368 QVector<QgsChunkNode *> children;
369 if ( node->tileId().d == 0 )
373 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
375 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
376 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
379 QgsChunkNode *west =
new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
380 QgsChunkNode *east =
new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
381 children << west << east;
383 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
385 QgsChunkNodeId nid = node->tileId();
387 double latMin, latMax, lonMin, lonMax;
388 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
389 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
390 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
391 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
392 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
394 double d1 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
395 double d2 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
396 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
399 <<
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 )
416void QgsGlobeMapUpdateJob::start()
418 QgsChunkNode *node = chunk();
421 QVector<QgsGlobeMaterial *> materials = node->entity()->componentsOfType<QgsGlobeMaterial>();
422 Q_ASSERT( materials.count() == 1 );
423 QVector<Qt3DRender::QAbstractTextureImage *> texImages = materials[0]->texture()->textureImages();
424 Q_ASSERT( texImages.count() == 1 );
425 QgsTerrainTextureImage *terrainTexImage = qobject_cast<QgsTerrainTextureImage *>( texImages[0] );
426 Q_ASSERT( terrainTexImage );
428 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this, terrainTexImage](
int jobId,
const QImage &image ) {
429 if ( mJobId == jobId )
431 terrainTexImage->setImage( image );
436 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
439void QgsGlobeMapUpdateJob::cancel()
442 mTextureGenerator->cancelJob( mJobId );
450class QgsGlobeMapUpdateJobFactory :
public QgsChunkQueueJobFactory
453 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings ) { mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings ); }
455 QgsChunkQueueJob *createJob( QgsChunkNode *chunk )
override {
return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk ); }
458 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
466 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
469 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged,
this, &QgsGlobeEntity::invalidateMapImages );
479 mUpdateJobFactory = std::make_unique<QgsGlobeMapUpdateJobFactory>( mapSettings );
482QgsGlobeEntity::~QgsGlobeEntity()
491 QVector3D intersectionPoint;
492 const QList<QgsChunkNode *> active = activeNodes();
493 for ( QgsChunkNode *node : active )
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 )
504 QVector3D nodeIntPoint;
505 int triangleIndex = -1;
509 float dist = ( ray.
origin() - nodeIntPoint ).length();
510 if ( minDist < 0 || dist < minDist )
513 intersectionPoint = nodeIntPoint;
525 hit.
setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
529void QgsGlobeEntity::invalidateMapImages()
531 QgsEventTracing::addEvent( QgsEventTracing::Instant, u
"3D"_s, u
"Invalidate textures"_s );
535 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
539 QList<QgsChunkNode *> inactiveNodes;
540 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
541 for ( QgsChunkNode *node : descendants )
543 if ( !node->entity() )
545 if ( mActiveNodes.contains( node ) )
547 if ( !node->parent() )
549 inactiveNodes << node;
552 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
554 setNeedsUpdate(
true );
@ ShowTerrainBoundingBoxes
Displays bounding boxes of terrain tiles.
@ 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.
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.
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.
Context settings for a material.
static QgsMaterialContext fromRenderContext(const Qgs3DRenderContext &context)
Constructs a material context from the settings in a 3D render context.
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.
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.