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>
50#include "moc_qgsglobechunkedentity.cpp"
52using namespace Qt::StringLiterals;
56static Qt3DCore::QEntity *makeGlobeMesh(
65 QString textureDebugText,
69 double lonRange = lonMax - lonMin;
70 double latRange = latMax - latMin;
71 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
72 double latStep = latRange / ( double ) ( latSliceCount - 1 );
74 std::vector<double> x, y, z;
75 int pointCount = latSliceCount * lonSliceCount;
76 x.reserve( pointCount );
77 y.reserve( pointCount );
78 z.reserve( pointCount );
80 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
82 double lat = latSliceIndex * latStep + latMin;
83 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
85 double lon = lonSliceIndex * lonStep + lonMin;
97 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
100 int stride = ( 3 + 2 + 3 ) *
sizeof(
float );
102 QByteArray bufferBytes;
103 bufferBytes.resize( stride * pointCount );
104 float *fptr = (
float * ) bufferBytes.data();
105 for (
int i = 0; i < ( int ) pointCount; ++i )
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() );
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 );
118 QVector3D n = QVector3D(
static_cast<float>( x[i] ),
static_cast<float>( y[i] ),
static_cast<float>( z[i] ) ).normalized();
124 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
125 int indices = faces * 3;
127 QByteArray indexBytes;
128 indexBytes.resize( indices *
static_cast<int>(
sizeof( ushort ) ) );
130 quint16 *indexPtr =
reinterpret_cast<quint16 *
>( indexBytes.data() );
131 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
133 int latSliceStartIndex = latSliceIndex * lonSliceCount;
134 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
135 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
137 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
138 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
139 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
141 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
142 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
143 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
149 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
151 Qt3DCore::QBuffer *vertexBuffer =
new Qt3DCore::QBuffer( entity );
152 vertexBuffer->setData( bufferBytes );
154 Qt3DCore::QBuffer *indexBuffer =
new Qt3DCore::QBuffer( entity );
155 indexBuffer->setData( indexBytes );
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 );
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 );
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 );
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 );
192 Qt3DCore::QGeometry *geometry =
new Qt3DCore::QGeometry( entity );
193 geometry->addAttribute( positionAttribute );
194 geometry->addAttribute( texCoordAttribute );
195 geometry->addAttribute( normalAttribute );
196 geometry->addAttribute( indexAttribute );
198 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer( entity );
199 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
200 geomRenderer->setVertexCount( faces * 3 );
201 geomRenderer->setGeometry( geometry );
203 QgsTerrainTextureImage *textureImage =
new QgsTerrainTextureImage( textureQImage,
QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
205 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D( entity );
207 texture->addTextureImage( textureImage );
208 texture->setFormat( Qt3DRender::QAbstractTexture::SRGB8_Alpha8 );
210 QgsGlobeMaterial *material =
new QgsGlobeMaterial( entity );
211 material->setTexture( texture );
213 QgsGeoTransform *geoTransform =
new QgsGeoTransform( entity );
214 geoTransform->setGeoTranslation( meshOrigin );
216 entity->addComponent( material );
217 entity->addComponent( geomRenderer );
218 entity->addComponent( geoTransform );
227 : mMapSettings( mapSettings )
229 mTextureGenerator = std::make_unique<QgsTerrainTextureGenerator>( *mapSettings );
236 mRadius = QgsGlobeUtils::ellipsoidRadius( mGlobeCrsToLatLon );
239QgsGlobeChunkLoader::~QgsGlobeChunkLoader()
242QFuture<QgsChunkLoaderResult> QgsGlobeChunkLoader::loadChunk( QgsChunkNode *node )
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 ) )
251 return new Qt3DCore::QEntity( parent );
256 int d = node->tileId().d;
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 );
276QFuture<QgsChunkLoaderResult> QgsGlobeChunkLoader::updateChunk( QgsChunkNode *node )
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 );
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();
296QgsChunkNode *QgsGlobeChunkLoader::createRootNode()
const
298 const QgsChunkNodeId rootId( 0, 0, 0, 0 );
299 const QgsBox3D rootNodeBox3D( -mRadius.x(), -mRadius.y(), -mRadius.z(), mRadius.x(), mRadius.y(), mRadius.z() );
301 QgsChunkNode *node =
new QgsChunkNode( rootId, rootNodeBox3D, 999'999 );
305QFuture<QVector<QgsChunkNode *>> QgsGlobeChunkLoader::createChildren( QgsChunkNode *node )
307 QVector<QgsChunkNode *> children;
308 if ( node->tileId().d == 0 )
312 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
314 const QgsChunkNodeId westId( 1, 0, 0, 0 );
315 const QgsChunkNodeId eastId( 1, 1, 0, 0 );
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;
322 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
324 QgsChunkNodeId nid = node->tileId();
326 const QgsRectangle extent = QgsGlobeUtils::nodeIdToLonLatRect( nid );
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 );
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() );
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 );
345 return QtFuture::makeReadyValueFuture( children );
353 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoader( mapSettings ), true )
356 connect( mLayerWatcher.get(), &QgsLayerStyleWatcher::styleChanged,
this, &QgsGlobeEntity::invalidateMapImages );
367QgsGlobeEntity::~QgsGlobeEntity()
376 QVector3D intersectionPoint;
377 const QList<QgsChunkNode *> active = activeNodes();
378 for ( QgsChunkNode *node : active )
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 )
389 QVector3D nodeIntPoint;
390 int triangleIndex = -1;
394 float dist = ( ray.
origin() - nodeIntPoint ).length();
395 if ( minDist < 0 || dist < minDist )
398 intersectionPoint = nodeIntPoint;
410 hit.
setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
414void QgsGlobeEntity::invalidateMapImages()
416 QgsEventTracing::addEvent( QgsEventTracing::Instant, u
"3D"_s, u
"Invalidate textures"_s );
420 updateNodes( mActiveNodes );
424 QList<QgsChunkNode *> inactiveNodes;
425 const QList<QgsChunkNode *> descendants = mRootNode->descendants();
426 for ( QgsChunkNode *node : descendants )
428 if ( !node->entity() )
430 if ( mActiveNodes.contains( node ) )
432 if ( !node->parent() )
434 inactiveNodes << node;
437 updateNodes( inactiveNodes );
439 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.
#define QGIS_CHECK_MAIN_THREAD_ACCESS
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.