28#include <QtConcurrentRun>
33size_t qHash(
const QgsChunkNodeId &n )
48 return bounds.
width() > 1e5 || bounds.
height() > 1e5 || bounds.
depth() > 1e5;
53QgsTiledSceneChunkLoader::QgsTiledSceneChunkLoader( QgsChunkNode *node,
const QgsTiledSceneIndex &index,
const QgsTiledSceneChunkLoaderFactory &factory,
double zValueScale,
double zValueOffset )
54 : QgsChunkLoader( node )
58 mFutureWatcher =
new QFutureWatcher<void>(
this );
59 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
63 const QgsChunkNodeId tileId = node->tileId();
64 const QFuture<void> future = QtConcurrent::run( [
this, tileId, zValueScale, zValueOffset, boundsTransform]
71 if ( hasLargeBounds( tile, boundsTransform ) )
74 QString uri = tile.
resources().value( QStringLiteral(
"content" ) ).toString();
82 uri = tile.
baseUrl().resolved( uri ).toString();
83 QByteArray content = mFactory.mIndex.retrieveContent( uri );
84 if ( content.isEmpty() )
91 QgsGltf3DUtils::EntityTransform entityTransform;
93 entityTransform.sceneOriginTargetCrs = mFactory.mRenderContext.origin();
94 entityTransform.ecefToTargetCrs = &mFactory.mBoundsTransform;
95 entityTransform.zValueScale = zValueScale;
96 entityTransform.zValueOffset = zValueOffset;
97 entityTransform.gltfUpAxis =
static_cast< Qgis::Axis >( tile.
metadata().value( QStringLiteral(
"gltfUpAxis" ),
static_cast< int >(
Qgis::Axis::Y ) ).toInt() );
99 const QString &format = tile.
metadata().value( QStringLiteral(
"contentFormat" ) ).value<QString>();
101 if ( format == QLatin1String(
"quantizedmesh" ) )
106 qmTile.removeDegenerateTriangles();
107 tinygltf::Model model = qmTile.toGltf(
true, 100 );
108 mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, uri, &errors );
112 errors.append( QStringLiteral(
"Failed to parse tile from '%1'" ).arg( uri ) );
115 else if ( format ==
"cesiumtiles" )
118 if ( tileContent.
gltf.isEmpty() )
120 entityTransform.tileTransform.translate( tileContent.
rtcCenter );
121 mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.
gltf, entityTransform, uri, &errors );
126 if ( !errors.isEmpty() )
136 mFutureWatcher->setFuture( future );
139QgsTiledSceneChunkLoader::~QgsTiledSceneChunkLoader()
141 if ( !mFutureWatcher->isFinished() )
143 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
144 mFutureWatcher->waitForFinished();
148Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *parent )
151 mEntity->setParent( parent );
158 : mRenderContext( context )
160 , mZValueScale( zValueScale )
161 , mZValueOffset( zValueOffset )
166QgsChunkLoader *QgsTiledSceneChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
168 return new QgsTiledSceneChunkLoader( node, mIndex, *
this, mZValueScale, mZValueOffset );
174 const QgsBox3D b = b0 - sceneOriginTargetCrs;
178QgsChunkNode *QgsTiledSceneChunkLoaderFactory::nodeForTile(
const QgsTiledSceneTile &t,
const QgsChunkNodeId &nodeId, QgsChunkNode *parent )
const
180 QgsChunkNode *node =
nullptr;
181 if ( hasLargeBounds( t, mBoundsTransform ) )
184 QgsVector3D v0 = mRenderContext.mapToWorldCoordinates(
QgsVector3D( mRenderContext.extent().xMinimum(), mRenderContext.extent().yMinimum(), -100 ) );
185 QgsVector3D v1 = mRenderContext.mapToWorldCoordinates(
QgsVector3D( mRenderContext.extent().xMaximum(), mRenderContext.extent().yMaximum(), +100 ) );
186 QgsAABB aabb( v0.
x(), v0.
y(), v0.
z(), v1.
x(), v1.
y(), v1.
z() );
188 node =
new QgsChunkNode( nodeId, aabb, err, parent );
195 const QgsAABB aabb = aabbConvert( box, mRenderContext.origin() );
196 node =
new QgsChunkNode( nodeId, aabb, t.
geometricError(), parent );
204QgsChunkNode *QgsTiledSceneChunkLoaderFactory::createRootNode()
const
207 return nodeForTile( t, QgsChunkNodeId( t.
id() ),
nullptr );
211QVector<QgsChunkNode *> QgsTiledSceneChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
213 QVector<QgsChunkNode *> children;
214 const long long indexTileId = node->tileId().uniqueId;
219 const QVector< long long > childIds = mIndex.childTileIds( indexTileId );
220 for (
long long childId : childIds )
222 const QgsChunkNodeId chId( childId );
228 QgsChunkNode *nChild = nodeForTile( t, chId, node );
229 children.append( nChild );
234bool QgsTiledSceneChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
236 long long nodeId = node->tileId().uniqueId;
237 if ( mFutureHierarchyFetches.contains( nodeId ) || mPendingHierarchyFetches.contains( nodeId ) )
242 mFutureHierarchyFetches.insert( nodeId );
250 const QVector< long long > childIds = mIndex.childTileIds( nodeId );
251 for (
long long childId : childIds )
253 if ( mFutureHierarchyFetches.contains( childId ) || mPendingHierarchyFetches.contains( childId ) )
258 mFutureHierarchyFetches.insert( childId );
265void QgsTiledSceneChunkLoaderFactory::fetchHierarchyForNode(
long long nodeId, QgsChunkNode *origNode )
267 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
268 mFutureHierarchyFetches.remove( nodeId );
269 mPendingHierarchyFetches.insert( nodeId );
271 QFutureWatcher<void> *futureWatcher =
new QFutureWatcher<void>(
this );
272 connect( futureWatcher, &QFutureWatcher<void>::finished,
this, [
this, origNode, nodeId, futureWatcher]
274 mPendingHierarchyFetches.remove( nodeId );
275 emit childrenPrepared( origNode );
276 futureWatcher->deleteLater();
278 futureWatcher->setFuture( QtConcurrent::run( [
this, nodeId]
280 mIndex.fetchHierarchy( nodeId );
284void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
286 long long nodeId = node->tileId().uniqueId;
287 if ( mFutureHierarchyFetches.contains( nodeId ) )
289 fetchHierarchyForNode( nodeId, node );
297 const QVector< long long > childIds = mIndex.childTileIds( nodeId );
298 for (
long long childId : childIds )
300 if ( mFutureHierarchyFetches.contains( childId ) )
302 fetchHierarchyForNode( childId, node );
311 : QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, zValueScale, zValueOffset ), true )
314 setShowBoundingBoxes( showBoundingBoxes );
317QgsTiledSceneLayerChunkedEntity::~QgsTiledSceneLayerChunkedEntity()
323int QgsTiledSceneLayerChunkedEntity::pendingJobsCount()
const
325 return QgsChunkedEntity::pendingJobsCount() +
static_cast<QgsTiledSceneChunkLoaderFactory *
>( mChunkLoaderFactory )->mPendingHierarchyFetches.count();
328QVector<QgsRayCastingUtils::RayHit> QgsTiledSceneLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
338 QVector<QgsRayCastingUtils::RayHit> result;
340 QVector3D intersectionPoint;
341 QgsChunkNode *minNode =
nullptr;
342 int minTriangleIndex = -1;
344 const QList<QgsChunkNode *> active = activeNodes();
345 for ( QgsChunkNode *node : active )
350 if ( node->entity() &&
351 ( minDist < 0 || node->bbox().distanceFromPoint( ray.origin() ) < minDist ) &&
352 QgsRayCastingUtils::rayBoxIntersection( ray, node->bbox() ) )
357 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
358 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
360 QVector3D nodeIntPoint;
361 int triangleIndex = -1;
362 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, QMatrix4x4(), nodeIntPoint, triangleIndex );
368 float dist = ( ray.origin() - nodeIntPoint ).length();
369 if ( minDist < 0 || dist < minDist )
373 minTriangleIndex = triangleIndex;
374 intersectionPoint = nodeIntPoint;
386 vm[
"node_id"] = tile.
id();
388 vm[
"node_content"] = tile.
resources().value( QStringLiteral(
"content" ) );
389 vm[
"triangle_index"] = minTriangleIndex;
391 result.append( hit );
394 QgsDebugMsgLevel( QStringLiteral(
"Active Nodes: %1, checked nodes: %2, hits found: %3" ).arg( nodesAll ).arg( nodeUsed ).arg( hits ), 2 );
@ NeedFetching
Tile has children, but they are not yet available and must be fetched.
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 QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
A 3-dimensional box composed of x, y, z coordinates.
double yMaximum() const
Returns the maximum y value.
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 xMinimum() const
Returns the minimum x value.
double zMaximum() const
Returns the maximum z value.
double xMaximum() const
Returns the maximum x value.
double width() const
Returns the width of the box.
double zMinimum() const
Returns the minimum z value.
double yMinimum() const
Returns the minimum y 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.
bool isNull() const
Returns true if the box is a null box.
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...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
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.