17#include "moc_qgstiledscenechunkloader_p.cpp"
31#include <QtConcurrentRun>
36size_t qHash(
const QgsChunkNodeId &n )
51 return bounds.
width() > 1e5 || bounds.
height() > 1e5 || bounds.
depth() > 1e5;
56QgsTiledSceneChunkLoader::QgsTiledSceneChunkLoader( QgsChunkNode *node,
const QgsTiledSceneIndex &index,
const QgsTiledSceneChunkLoaderFactory &factory,
double zValueScale,
double zValueOffset )
57 : QgsChunkLoader( node )
61 mFutureWatcher =
new QFutureWatcher<void>(
this );
62 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
66 const QgsChunkNodeId tileId = node->tileId();
67 const QgsVector3D chunkOrigin = node->box3D().center();
69 const QFuture<void> future = QtConcurrent::run( [
this, tileId, zValueScale, zValueOffset, boundsTransform, chunkOrigin, isGlobe] {
75 if ( !isGlobe && hasLargeBounds( tile, boundsTransform ) )
78 QString uri = tile.
resources().value( QStringLiteral(
"content" ) ).toString();
86 uri = tile.
baseUrl().resolved( uri ).toString();
87 QByteArray content = mFactory.mIndex.retrieveContent( uri );
88 if ( content.isEmpty() )
95 QgsGltf3DUtils::EntityTransform entityTransform;
97 entityTransform.chunkOriginTargetCrs = chunkOrigin;
98 entityTransform.ecefToTargetCrs = &mFactory.mBoundsTransform;
99 entityTransform.zValueScale = zValueScale;
100 entityTransform.zValueOffset = zValueOffset;
101 entityTransform.gltfUpAxis =
static_cast<Qgis::Axis>( tile.
metadata().value( QStringLiteral(
"gltfUpAxis" ),
static_cast<int>(
Qgis::Axis::Y ) ).toInt() );
103 const QString &format = tile.
metadata().value( QStringLiteral(
"contentFormat" ) ).value<QString>();
105 if ( format == QLatin1String(
"quantizedmesh" ) )
110 qmTile.removeDegenerateTriangles();
111 tinygltf::Model model = qmTile.toGltf(
true, 100 );
112 mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, uri, &errors );
116 errors.append( QStringLiteral(
"Failed to parse tile from '%1'" ).arg( uri ) );
119 else if ( format ==
"cesiumtiles" )
122 if ( tileContent.
gltf.isEmpty() )
124 entityTransform.tileTransform.translate( tileContent.
rtcCenter );
125 mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.
gltf, entityTransform, uri, &errors );
131 if ( !errors.isEmpty() )
138 QgsGeoTransform *transform =
new QgsGeoTransform;
139 transform->setGeoTranslation( chunkOrigin );
140 mEntity->addComponent( transform );
147 mFutureWatcher->setFuture( future );
150QgsTiledSceneChunkLoader::~QgsTiledSceneChunkLoader()
152 if ( !mFutureWatcher->isFinished() )
154 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
155 mFutureWatcher->waitForFinished();
159Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *parent )
162 mEntity->setParent( parent );
169 : mRenderContext( context )
171 , mZValueScale( zValueScale )
172 , mZValueOffset( zValueOffset )
177QgsChunkLoader *QgsTiledSceneChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
179 return new QgsTiledSceneChunkLoader( node, mIndex, *
this, mZValueScale, mZValueOffset );
182QgsChunkNode *QgsTiledSceneChunkLoaderFactory::nodeForTile(
const QgsTiledSceneTile &t,
const QgsChunkNodeId &nodeId, QgsChunkNode *parent )
const
184 QgsChunkNode *node =
nullptr;
188 QgsVector3D v0( mRenderContext.extent().xMinimum(), mRenderContext.extent().yMinimum(), -100 );
189 QgsVector3D v1( mRenderContext.extent().xMaximum(), mRenderContext.extent().yMaximum(), +100 );
192 node =
new QgsChunkNode( nodeId, box3D, err, parent );
199 node =
new QgsChunkNode( nodeId, box, t.
geometricError(), parent );
207QgsChunkNode *QgsTiledSceneChunkLoaderFactory::createRootNode()
const
210 return nodeForTile( t, QgsChunkNodeId( t.
id() ),
nullptr );
214QVector<QgsChunkNode *> QgsTiledSceneChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
216 QVector<QgsChunkNode *> children;
217 const long long indexTileId = node->tileId().uniqueId;
222 const QVector<long long> childIds = mIndex.childTileIds( indexTileId );
223 for (
long long childId : childIds )
225 const QgsChunkNodeId chId( childId );
232 if ( t.
metadata()[
"contentFormat"] == QStringLiteral(
"cesiumtiles" )
234 && hasLargeBounds( t, mBoundsTransform ) )
240 const QgsPointXY c = mRenderContext.extent().center();
243 const double *half = obb.
halfAxes();
246 half[0], half[3], half[6], 0,
247 half[1], half[4], half[7], 0,
248 half[2], half[5], half[8], 0,
251 QVector3D aaa = rot.inverted().map( ecef2.
toVector3D() );
252 if ( aaa.x() > 1 || aaa.y() > 1 || aaa.z() > 1 || aaa.x() < -1 || aaa.y() < -1 || aaa.z() < -1 )
261 QgsChunkNode *nChild = nodeForTile( t, chId, node );
262 children.append( nChild );
267bool QgsTiledSceneChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
269 long long nodeId = node->tileId().uniqueId;
270 if ( mFutureHierarchyFetches.contains( nodeId ) || mPendingHierarchyFetches.contains( nodeId ) )
275 mFutureHierarchyFetches.insert( nodeId );
283 const QVector<long long> childIds = mIndex.childTileIds( nodeId );
284 for (
long long childId : childIds )
286 if ( mFutureHierarchyFetches.contains( childId ) || mPendingHierarchyFetches.contains( childId ) )
291 mFutureHierarchyFetches.insert( childId );
298void QgsTiledSceneChunkLoaderFactory::fetchHierarchyForNode(
long long nodeId, QgsChunkNode *origNode )
300 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
301 mFutureHierarchyFetches.remove( nodeId );
302 mPendingHierarchyFetches.insert( nodeId );
304 QFutureWatcher<void> *futureWatcher =
new QFutureWatcher<void>(
this );
305 connect( futureWatcher, &QFutureWatcher<void>::finished,
this, [
this, origNode, nodeId, futureWatcher] {
306 mPendingHierarchyFetches.remove( nodeId );
307 emit childrenPrepared( origNode );
308 futureWatcher->deleteLater();
310 futureWatcher->setFuture( QtConcurrent::run( [
this, nodeId] {
311 mIndex.fetchHierarchy( nodeId );
315void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
317 long long nodeId = node->tileId().uniqueId;
318 if ( mFutureHierarchyFetches.contains( nodeId ) )
320 fetchHierarchyForNode( nodeId, node );
328 const QVector<long long> childIds = mIndex.childTileIds( nodeId );
329 for (
long long childId : childIds )
331 if ( mFutureHierarchyFetches.contains( childId ) )
333 fetchHierarchyForNode( childId, node );
342 : QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, zValueScale, zValueOffset ), true )
345 setShowBoundingBoxes( showBoundingBoxes );
348QgsTiledSceneLayerChunkedEntity::~QgsTiledSceneLayerChunkedEntity()
354int QgsTiledSceneLayerChunkedEntity::pendingJobsCount()
const
356 return QgsChunkedEntity::pendingJobsCount() +
static_cast<QgsTiledSceneChunkLoaderFactory *
>( mChunkLoaderFactory )->mPendingHierarchyFetches.count();
359QVector<QgsRayCastingUtils::RayHit> QgsTiledSceneLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
369 QVector<QgsRayCastingUtils::RayHit> result;
371 QVector3D intersectionPoint;
372 QgsChunkNode *minNode =
nullptr;
373 int minTriangleIndex = -1;
375 const QList<QgsChunkNode *> active = activeNodes();
376 for ( QgsChunkNode *node : active )
384 if ( node->entity() && ( minDist < 0 || nodeBbox.
distanceFromPoint( ray.origin() ) < minDist ) && QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
389 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
390 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
392 QVector3D nodeIntPoint;
393 int triangleIndex = -1;
394 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, QMatrix4x4(), nodeIntPoint, triangleIndex );
400 float dist = ( ray.origin() - nodeIntPoint ).length();
401 if ( minDist < 0 || dist < minDist )
405 minTriangleIndex = triangleIndex;
406 intersectionPoint = nodeIntPoint;
418 vm[QStringLiteral(
"node_id" )] = tile.
id();
420 vm[QStringLiteral(
"node_content" )] = tile.
resources().value( QStringLiteral(
"content" ) );
421 vm[QStringLiteral(
"triangle_index" )] = minTriangleIndex;
423 result.append( hit );
426 QgsDebugMsgLevel( QStringLiteral(
"Active Nodes: %1, checked nodes: %2, hits found: %3" ).arg( nodesAll ).arg( nodeUsed ).arg( hits ), 2 );
@ Geocentric
Geocentric CRS.
@ NeedFetching
Tile has children, but they are not yet available and must be fetched.
@ Reverse
Reverse/inverse transform (from destination to source)
QgsCoordinateReferenceSystem crs() const
Returns the 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.
float distanceFromPoint(float x, float y, float z) const
Returns shortest distance from the box to a point.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
A 3-dimensional box composed of x, y, z coordinates.
void setZMinimum(double z)
Sets the minimum z value.
double depth() const
Returns the depth of the box.
void setZMaximum(double z)
Sets the maximum z value.
double zMaximum() const
Returns the maximum z value.
double width() const
Returns the width of the box.
double zMinimum() const
Returns the minimum z value.
double height() const
Returns the height of the box.
static TileContents extractGltfFromTileContent(const QByteArray &tileContent)
Parses tile content.
This class represents a coordinate reference system (CRS).
Qgis::DistanceUnit mapUnits
A simple 4x4 matrix implementation useful for transformation in 3D space.
Represents a oriented (rotated) box in 3 dimensions.
const double * halfAxes() const
Returns the half axes matrix;.
bool isNull() const
Returns true if the box is a null box.
QgsVector3D center() const
Returns the vector to the center of the box.
A class to represent a 2D point.
Exception thrown on failure to parse Quantized Mesh tile (malformed data)
QgsOrientedBox3D box() const
Returns the volume's oriented box.
QgsBox3D bounds(const QgsCoordinateTransform &transform=QgsCoordinateTransform(), Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Returns the axis aligned bounding box of the volume.
An index for tiled scene data providers.
Represents an individual tile from a tiled scene data source.
Qgis::TileRefinementProcess refinementProcess() const
Returns the tile's refinement process.
QVariantMap resources() const
Returns the resources attached to the tile.
const QgsTiledSceneBoundingVolume & boundingVolume() const
Returns the bounding volume for the tile.
QVariantMap metadata() const
Returns additional metadata attached to the tile.
long long id() const
Returns the tile's unique ID.
const QgsMatrix4x4 * transform() const
Returns the tile's transform.
double geometricError() const
Returns the tile's geometric error, which is the error, in meters, of the tile's simplified represent...
QUrl baseUrl() const
Returns the tile's base URL.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
QVector3D toVector3D() const
Converts the current object to QVector3D.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
uint qHash(const QVariant &variant)
Hash for QVariant.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Encapsulates the contents of a 3D tile.
QgsVector3D rtcCenter
Center position of relative-to-center coordinates (when used)
QByteArray gltf
GLTF binary content.
Helper struct to store ray casting parameters.
Helper struct to store ray casting results.