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>
47#include "moc_qgsglobechunkedentity.cpp"
49using namespace Qt::StringLiterals;
53static Qt3DCore::QEntity *makeGlobeMesh(
62 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;
148 Qt3DCore::QBuffer *vertexBuffer =
new Qt3DCore::QBuffer( entity );
149 vertexBuffer->setData( bufferBytes );
151 Qt3DCore::QBuffer *indexBuffer =
new Qt3DCore::QBuffer( entity );
152 indexBuffer->setData( indexBytes );
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 );
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 );
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 );
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 );
189 Qt3DCore::QGeometry *geometry =
new Qt3DCore::QGeometry( entity );
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 );
204 texture->addTextureImage( textureImage );
205 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
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 );
224 : QgsChunkLoader( node )
225 , mRenderContext( context )
226 , mTextureGenerator( textureGenerator )
227 , mGlobeCrsToLatLon( globeCrsToLatLon )
230void QgsGlobeChunkLoader::start()
232 QgsChunkNode *node = chunk();
234 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this](
int job,
const QImage &img ) {
242 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( node->tileId() );
243 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
246Qt3DCore::QEntity *QgsGlobeChunkLoader::createEntity( Qt3DCore::QEntity *parent )
248 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
250 return new Qt3DCore::QEntity( parent );
253 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( mNode->tileId() );
257 int d = mNode->tileId().d;
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 );
279QgsGlobeChunkLoaderFactory::QgsGlobeChunkLoaderFactory(
Qgs3DMapSettings *mapSettings )
280 : mMapSettings( mapSettings )
282 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *mapSettings );
289 mRadius = QgsGlobeUtils::ellipsoidRadius( mGlobeCrsToLatLon );
292QgsGlobeChunkLoaderFactory::~QgsGlobeChunkLoaderFactory()
295QgsChunkLoader *QgsGlobeChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
300QgsChunkNode *QgsGlobeChunkLoaderFactory::createRootNode()
const
302 const QgsChunkNodeId rootId( 0, 0, 0, 0 );
303 const QgsBox3D rootNodeBox3D( -mRadius.x(), -mRadius.y(), -mRadius.z(), mRadius.x(), mRadius.y(), mRadius.z() );
305 QgsChunkNode *node =
new QgsChunkNode( rootId, rootNodeBox3D, 999'999 );
309QVector<QgsChunkNode *> QgsGlobeChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
311 QVector<QgsChunkNode *> children;
312 if ( node->tileId().d == 0 )
316 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
318 const QgsChunkNodeId westId( 1, 0, 0, 0 );
319 const QgsChunkNodeId eastId( 1, 1, 0, 0 );
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;
326 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
328 QgsChunkNodeId nid = node->tileId();
330 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( nid );
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 );
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() );
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 );
355QgsGlobeMapUpdateJob::QgsGlobeMapUpdateJob( QgsTerrainTextureGenerator *textureGenerator, QgsChunkNode *node )
356 : QgsChunkQueueJob( node )
357 , mTextureGenerator( textureGenerator )
360void QgsGlobeMapUpdateJob::start()
362 QgsChunkNode *node = chunk();
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 );
372 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [
this, terrainTexImage](
int jobId,
const QImage &image ) {
373 if ( mJobId == jobId )
375 terrainTexImage->setImage( image );
380 mJobId = mTextureGenerator->render( terrainTexImage->imageExtent(), node->tileId(), terrainTexImage->imageDebugText() );
383void QgsGlobeMapUpdateJob::cancel()
386 mTextureGenerator->cancelJob( mJobId );
394class QgsGlobeMapUpdateJobFactory :
public QgsChunkQueueJobFactory
397 explicit QgsGlobeMapUpdateJobFactory( Qgs3DMapSettings *mapSettings ) { mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings ); }
399 QgsChunkQueueJob *createJob( QgsChunkNode *chunk )
override {
return new QgsGlobeMapUpdateJob( mTextureGenerator, chunk ); }
402 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
410 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
413 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged,
this, &QgsGlobeEntity::invalidateMapImages );
423 mUpdateJobFactory = std::make_unique<QgsGlobeMapUpdateJobFactory>( mapSettings );
426QgsGlobeEntity::~QgsGlobeEntity()
435 QVector3D intersectionPoint;
436 const QList<QgsChunkNode *> active = activeNodes();
437 for ( QgsChunkNode *node : active )
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 )
448 QVector3D nodeIntPoint;
449 int triangleIndex = -1;
453 float dist = ( ray.
origin() - nodeIntPoint ).length();
454 if ( minDist < 0 || dist < minDist )
457 intersectionPoint = nodeIntPoint;
469 hit.
setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
473void QgsGlobeEntity::invalidateMapImages()
475 QgsEventTracing::addEvent( QgsEventTracing::Instant, u
"3D"_s, u
"Invalidate textures"_s );
479 updateNodes( mActiveNodes, mUpdateJobFactory.get() );
483 QList<QgsChunkNode *> inactiveNodes;
484 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
485 for ( QgsChunkNode *node : descendants )
487 if ( !node->entity() )
489 if ( mActiveNodes.contains( node ) )
491 if ( !node->parent() )
493 inactiveNodes << node;
496 updateNodes( inactiveNodes, mUpdateJobFactory.get() );
498 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.