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>
59static Qt3DCore::QEntity *makeGlobeMesh(
double lonMin,
double lonMax,
double latMin,
double latMax,
int lonSliceCount,
int latSliceCount,
const QgsCoordinateTransform &globeCrsToLatLon, QImage textureQImage, QString textureDebugText )
61 double lonRange = lonMax - lonMin;
62 double latRange = latMax - latMin;
63 double lonStep = lonRange / ( double ) ( lonSliceCount - 1 );
64 double latStep = latRange / ( double ) ( latSliceCount - 1 );
66 std::vector<double> x, y, z;
67 int pointCount = latSliceCount * lonSliceCount;
68 x.reserve( pointCount );
69 y.reserve( pointCount );
70 z.reserve( pointCount );
72 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount; ++latSliceIndex )
74 double lat = latSliceIndex * latStep + latMin;
75 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount; ++lonSliceIndex )
77 double lon = lonSliceIndex * lonStep + lonMin;
89 QgsVector3D meshOriginLatLon( ( lonMin + lonMax ) / 2, ( latMin + latMax ) / 2, 0 );
92 int stride = ( 3 + 2 + 3 ) *
sizeof(
float );
94 QByteArray bufferBytes;
95 bufferBytes.resize( stride * pointCount );
96 float *fptr = (
float * ) bufferBytes.data();
97 for (
int i = 0; i < ( int ) pointCount; ++i )
99 *fptr++ =
static_cast<float>( x[i] - meshOrigin.
x() );
100 *fptr++ =
static_cast<float>( y[i] - meshOrigin.
y() );
101 *fptr++ =
static_cast<float>( z[i] - meshOrigin.
z() );
103 int vi = i / lonSliceCount;
104 int ui = i % lonSliceCount;
105 float v =
static_cast<float>( vi ) /
static_cast<float>( latSliceCount - 1 );
106 float u =
static_cast<float>( ui ) /
static_cast<float>( lonSliceCount - 1 );
110 QVector3D n = QVector3D(
static_cast<float>( x[i] ),
static_cast<float>( y[i] ),
static_cast<float>( z[i] ) ).normalized();
116 int faces = ( lonSliceCount - 1 ) * ( latSliceCount - 1 ) * 2;
117 int indices = faces * 3;
119 QByteArray indexBytes;
120 indexBytes.resize( indices *
static_cast<int>(
sizeof( ushort ) ) );
122 quint16 *indexPtr =
reinterpret_cast<quint16 *
>( indexBytes.data() );
123 for (
int latSliceIndex = 0; latSliceIndex < latSliceCount - 1; ++latSliceIndex )
125 int latSliceStartIndex = latSliceIndex * lonSliceCount;
126 int nextLatSliceStartIndex = lonSliceCount + latSliceStartIndex;
127 for (
int lonSliceIndex = 0; lonSliceIndex < lonSliceCount - 1; ++lonSliceIndex )
129 indexPtr[0] = latSliceStartIndex + lonSliceIndex;
130 indexPtr[1] = lonSliceIndex + latSliceStartIndex + 1;
131 indexPtr[2] = nextLatSliceStartIndex + lonSliceIndex;
133 indexPtr[3] = nextLatSliceStartIndex + lonSliceIndex;
134 indexPtr[4] = lonSliceIndex + latSliceStartIndex + 1;
135 indexPtr[5] = lonSliceIndex + nextLatSliceStartIndex + 1;
141 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
144 vertexBuffer->setData( bufferBytes );
147 indexBuffer->setData( indexBytes );
150 positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
151 positionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
152 positionAttribute->setVertexSize( 3 );
153 positionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
154 positionAttribute->setBuffer( vertexBuffer );
155 positionAttribute->setByteStride( stride );
156 positionAttribute->setCount( pointCount );
159 texCoordAttribute->setName( Qt3DQAttribute::defaultTextureCoordinateAttributeName() );
160 texCoordAttribute->setVertexBaseType( Qt3DQAttribute::Float );
161 texCoordAttribute->setVertexSize( 2 );
162 texCoordAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
163 texCoordAttribute->setBuffer( vertexBuffer );
164 texCoordAttribute->setByteStride( stride );
165 texCoordAttribute->setByteOffset( 3 *
sizeof(
float ) );
166 texCoordAttribute->setCount( pointCount );
169 normalAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
170 normalAttribute->setVertexBaseType( Qt3DQAttribute::Float );
171 normalAttribute->setVertexSize( 3 );
172 normalAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
173 normalAttribute->setBuffer( vertexBuffer );
174 normalAttribute->setByteStride( stride );
175 normalAttribute->setByteOffset( 5 *
sizeof(
float ) );
176 normalAttribute->setCount( pointCount );
179 indexAttribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
180 indexAttribute->setVertexBaseType( Qt3DQAttribute::UnsignedShort );
181 indexAttribute->setBuffer( indexBuffer );
182 indexAttribute->setCount( faces * 3 );
185 geometry->addAttribute( positionAttribute );
186 geometry->addAttribute( texCoordAttribute );
187 geometry->addAttribute( normalAttribute );
188 geometry->addAttribute( indexAttribute );
190 Qt3DRender::QGeometryRenderer *geomRenderer =
new Qt3DRender::QGeometryRenderer( entity );
191 geomRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
192 geomRenderer->setVertexCount( faces * 3 );
193 geomRenderer->setGeometry( geometry );
195 QgsTerrainTextureImage *textureImage =
new QgsTerrainTextureImage( textureQImage,
QgsRectangle( lonMin, latMin, lonMax, latMax ), textureDebugText, entity );
197 Qt3DRender::QTexture2D *texture =
new Qt3DRender::QTexture2D( entity );
198 texture->addTextureImage( textureImage );
199 texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
200 texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
202 Qt3DExtras::QTextureMaterial *material =
new Qt3DExtras::QTextureMaterial( entity );
203 material->setTexture( texture );
205 QgsGeoTransform *geoTransform =
new QgsGeoTransform( entity );
206 geoTransform->setGeoTranslation( meshOrigin );
208 entity->addComponent( material );
209 entity->addComponent( geomRenderer );
210 entity->addComponent( geoTransform );
215static void globeNodeIdToLatLon( QgsChunkNodeId n,
double &latMin,
double &latMax,
double &lonMin,
double &lonMax )
217 if ( n == QgsChunkNodeId( 0, 0, 0, 0 ) )
226 double tileSize = 180.0 / std::pow( 2.0, n.d - 1 );
227 lonMin = n.x * tileSize - 180.0;
228 latMin = n.y * tileSize - 90.0;
229 lonMax = lonMin + tileSize;
230 latMax = latMin + tileSize;
236 double latMin, latMax, lonMin, lonMax;
237 globeNodeIdToLatLon( n, latMin, latMax, lonMin, lonMax );
239 Q_ASSERT( latMax - latMin <= 90 && lonMax - lonMin <= 90 );
241 QVector<double> x, y, z;
243 x.reserve( pointCount );
244 y.reserve( pointCount );
245 z.reserve( pointCount );
247 x.push_back( lonMin );
248 y.push_back( latMin );
250 x.push_back( lonMin );
251 y.push_back( latMax );
253 x.push_back( lonMax );
254 y.push_back( latMin );
256 x.push_back( lonMax );
257 y.push_back( latMax );
263 box.combineWith( x[2], y[2], z[2] );
264 box.combineWith( x[3], y[3], z[3] );
272class QgsGlobeChunkLoader :
public QgsChunkLoader
275 QgsGlobeChunkLoader( QgsChunkNode *node, QgsTerrainTextureGenerator *textureGenerator,
const QgsCoordinateTransform &globeCrsToLatLon )
276 : QgsChunkLoader( node )
277 , mTextureGenerator( textureGenerator )
278 , mGlobeCrsToLatLon( globeCrsToLatLon )
280 connect( mTextureGenerator, &QgsTerrainTextureGenerator::tileReady,
this, [=](
int job,
const QImage &img ) {
288 double latMin, latMax, lonMin, lonMax;
289 globeNodeIdToLatLon( node->tileId(), latMin, latMax, lonMin, lonMax );
291 mJobId = mTextureGenerator->render( extent, node->tileId(), node->tileId().text() );
294 Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent )
override
296 if ( mNode->tileId() == QgsChunkNodeId( 0, 0, 0, 0 ) )
298 return new Qt3DCore::QEntity( parent );
301 double latMin, latMax, lonMin, lonMax;
302 globeNodeIdToLatLon( mNode->tileId(), latMin, latMax, lonMin, lonMax );
306 int d = mNode->tileId().d;
317 Qt3DCore::QEntity *e = makeGlobeMesh( lonMin, lonMax, latMin, latMax, slices, slices, mGlobeCrsToLatLon, mTexture, mNode->tileId().text() );
318 e->setParent( parent );
323 QgsTerrainTextureGenerator *mTextureGenerator;
333class QgsGlobeChunkLoaderFactory :
public QgsChunkLoaderFactory
337 : mMapSettings( mapSettings )
339 mTextureGenerator =
new QgsTerrainTextureGenerator( *mapSettings );
351 ~QgsGlobeChunkLoaderFactory()
353 delete mTextureGenerator;
356 QgsChunkLoader *createChunkLoader( QgsChunkNode *node )
const override
358 return new QgsGlobeChunkLoader( node, mTextureGenerator, mGlobeCrsToLatLon );
361 QgsChunkNode *createRootNode()
const override
363 QgsBox3D rootNodeBox3D( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, +mRadiusY, +mRadiusZ );
365 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, 999'999 );
369 QVector<QgsChunkNode *> createChildren( QgsChunkNode *node )
const override
371 QVector<QgsChunkNode *> children;
372 if ( node->tileId().d == 0 )
376 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
378 QgsBox3D boxWest( -mRadiusX, -mRadiusY, -mRadiusZ, +mRadiusX, 0, +mRadiusZ );
379 QgsBox3D boxEast( -mRadiusX, 0, -mRadiusY, +mRadiusX, +mRadiusY, +mRadiusZ );
382 QgsChunkNode *west =
new QgsChunkNode( QgsChunkNodeId( 1, 0, 0, 0 ), boxWest, error, node );
383 QgsChunkNode *east =
new QgsChunkNode( QgsChunkNodeId( 1, 1, 0, 0 ), boxEast, error, node );
384 children << west << east;
386 else if ( node->error() > mMapSettings->terrainSettings()->maximumGroundError() )
388 QgsChunkNodeId nid = node->tileId();
390 double latMin, latMax, lonMin, lonMax;
391 globeNodeIdToLatLon( nid, latMin, latMax, lonMin, lonMax );
392 QgsChunkNodeId cid1( nid.d + 1, nid.x * 2, nid.y * 2 );
393 QgsChunkNodeId cid2( nid.d + 1, nid.x * 2 + 1, nid.y * 2 );
394 QgsChunkNodeId cid3( nid.d + 1, nid.x * 2, nid.y * 2 + 1 );
395 QgsChunkNodeId cid4( nid.d + 1, nid.x * 2 + 1, nid.y * 2 + 1 );
397 double d1 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin + ( lonMax - lonMin ) / 2, latMin ) );
398 double d2 = mDistanceArea.measureLine(
QgsPointXY( lonMin, latMin ),
QgsPointXY( lonMin, latMin + ( latMax - latMin ) / 2 ) );
399 float error =
static_cast<float>( std::max( d1, d2 ) ) /
static_cast<float>( mMapSettings->terrainSettings()->mapTileResolution() );
401 children <<
new QgsChunkNode( cid1, globeNodeIdToBox3D( cid1, mGlobeCrsToLatLon ), error, node )
402 <<
new QgsChunkNode( cid2, globeNodeIdToBox3D( cid2, mGlobeCrsToLatLon ), error, node )
403 <<
new QgsChunkNode( cid3, globeNodeIdToBox3D( cid3, mGlobeCrsToLatLon ), error, node )
404 <<
new QgsChunkNode( cid4, globeNodeIdToBox3D( cid4, mGlobeCrsToLatLon ), error, node );
412 QgsTerrainTextureGenerator *mTextureGenerator =
nullptr;
415 double mRadiusX, mRadiusY, mRadiusZ;
423 : QgsChunkedEntity( mapSettings, mapSettings->terrainSettings()->maximumScreenError(), new QgsGlobeChunkLoaderFactory( mapSettings ), true )
427QgsGlobeEntity::~QgsGlobeEntity()
438 QVector3D intersectionPoint;
439 const QList<QgsChunkNode *> active = activeNodes();
440 for ( QgsChunkNode *node : active )
444 if ( node->entity() && ( minDist < 0 || nodeBbox.
distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
446 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
447 Q_ASSERT( nodeGeoTransform );
448 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
449 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
451 QVector3D nodeIntPoint;
452 int triangleIndex = -1;
453 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, nodeGeoTransform->matrix(), nodeIntPoint, triangleIndex );
456 float dist = ( ray.origin() - nodeIntPoint ).length();
457 if ( minDist < 0 || dist < minDist )
460 intersectionPoint = nodeIntPoint;
467 QVector<QgsRayCastingUtils::RayHit> result;
@ Reverse
Reverse/inverse transform (from destination to source)
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.
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.