33#include <QtConcurrentRun>
35#include "moc_qgstiledscenechunkloader_p.cpp"
39size_t qHash(
const QgsChunkNodeId &n )
54 return bounds.
width() > 1e5 || bounds.
height() > 1e5 || bounds.
depth() > 1e5;
59QgsTiledSceneChunkLoader::QgsTiledSceneChunkLoader( QgsChunkNode *node,
const QgsTiledSceneIndex &index,
const QgsTiledSceneChunkLoaderFactory &factory,
double zValueScale,
double zValueOffset )
60 : QgsChunkLoader( node )
62 , mZValueScale( zValueScale )
63 , mZValueOffset( zValueOffset )
68void QgsTiledSceneChunkLoader::start()
70 QgsChunkNode *node = chunk();
72 mFutureWatcher =
new QFutureWatcher<void>(
this );
73 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
77 const QgsChunkNodeId tileId = node->tileId();
78 const QgsVector3D chunkOrigin = node->box3D().center();
80 const QFuture<void> future = QtConcurrent::run( [
this, tileId, boundsTransform, chunkOrigin, isGlobe] {
86 if ( !isGlobe && hasLargeBounds( tile, boundsTransform ) )
89 QString uri = tile.
resources().value( QStringLiteral(
"content" ) ).toString();
97 uri = tile.
baseUrl().resolved( uri ).toString();
98 QByteArray content = mFactory.mIndex.retrieveContent( uri );
99 if ( content.isEmpty() )
106 QgsGltf3DUtils::EntityTransform entityTransform;
108 entityTransform.chunkOriginTargetCrs = chunkOrigin;
109 entityTransform.ecefToTargetCrs = &mFactory.mBoundsTransform;
110 entityTransform.zValueScale = mZValueScale;
111 entityTransform.zValueOffset = mZValueOffset;
112 entityTransform.gltfUpAxis =
static_cast<Qgis::Axis>( tile.
metadata().value( QStringLiteral(
"gltfUpAxis" ),
static_cast<int>(
Qgis::Axis::Y ) ).toInt() );
114 const QString &format = tile.
metadata().value( QStringLiteral(
"contentFormat" ) ).value<QString>();
116 if ( format == QLatin1String(
"quantizedmesh" ) )
121 qmTile.removeDegenerateTriangles();
122 tinygltf::Model model = qmTile.toGltf(
true, 100 );
123 mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, uri, &errors );
127 errors.append( QStringLiteral(
"Failed to parse tile from '%1'" ).arg( uri ) );
130 else if ( format == QLatin1String(
"cesiumtiles" ) )
133 if ( tileContent.
gltf.isEmpty() )
135 entityTransform.tileTransform.translate( tileContent.
rtcCenter );
136 mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.
gltf, entityTransform, uri, &errors );
138 else if ( format == QLatin1String(
"draco" ) )
140 QgsGltfUtils::I3SNodeContext i3sContext;
141 i3sContext.initFromTile( tile, mFactory.mLayerCrs, mFactory.mBoundsTransform.sourceCrs(), mFactory.mRenderContext.transformContext() );
143 QString dracoLoadError;
144 tinygltf::Model model;
145 if ( !QgsGltfUtils::loadDracoModel( content, i3sContext, model, &dracoLoadError ) )
147 errors.append( dracoLoadError );
151 mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, QString(), &errors );
157 if ( !errors.isEmpty() )
164 QgsGeoTransform *transform =
new QgsGeoTransform;
165 transform->setGeoTranslation( chunkOrigin );
166 mEntity->addComponent( transform );
173 mFutureWatcher->setFuture( future );
176QgsTiledSceneChunkLoader::~QgsTiledSceneChunkLoader()
178 if ( !mFutureWatcher->isFinished() )
180 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
181 mFutureWatcher->waitForFinished();
185Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *parent )
188 mEntity->setParent( parent );
194QgsTiledSceneChunkLoaderFactory::QgsTiledSceneChunkLoaderFactory(
202 : mRenderContext( context )
204 , mZValueScale( zValueScale )
205 , mZValueOffset( zValueOffset )
206 , mLayerCrs( layerCrs )
211QgsChunkLoader *QgsTiledSceneChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
213 return new QgsTiledSceneChunkLoader( node, mIndex, *
this, mZValueScale, mZValueOffset );
216QgsChunkNode *QgsTiledSceneChunkLoaderFactory::nodeForTile(
const QgsTiledSceneTile &t,
const QgsChunkNodeId &nodeId, QgsChunkNode *parent )
const
218 QgsChunkNode *node =
nullptr;
222 QgsVector3D v0( mRenderContext.extent().xMinimum(), mRenderContext.extent().yMinimum(), -100 );
223 QgsVector3D v1( mRenderContext.extent().xMaximum(), mRenderContext.extent().yMaximum(), +100 );
226 node =
new QgsChunkNode( nodeId, box3D, err, parent );
233 node =
new QgsChunkNode( nodeId, box, t.
geometricError(), parent );
241QgsChunkNode *QgsTiledSceneChunkLoaderFactory::createRootNode()
const
244 return nodeForTile( t, QgsChunkNodeId( t.
id() ),
nullptr );
248QVector<QgsChunkNode *> QgsTiledSceneChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
250 QVector<QgsChunkNode *> children;
251 const long long indexTileId = node->tileId().uniqueId;
256 const QVector<long long> childIds = mIndex.childTileIds( indexTileId );
257 for (
long long childId : childIds )
259 const QgsChunkNodeId chId( childId );
266 if ( t.
metadata()[
"contentFormat"] == QStringLiteral(
"cesiumtiles" )
268 && hasLargeBounds( t, mBoundsTransform ) )
274 const QgsPointXY c = mRenderContext.extent().center();
277 const double *half = obb.
halfAxes();
280 half[0], half[3], half[6], 0,
281 half[1], half[4], half[7], 0,
282 half[2], half[5], half[8], 0,
285 QVector3D aaa = rot.inverted().map( ecef2.
toVector3D() );
286 if ( aaa.x() > 1 || aaa.y() > 1 || aaa.z() > 1 || aaa.x() < -1 || aaa.y() < -1 || aaa.z() < -1 )
295 QgsChunkNode *nChild = nodeForTile( t, chId, node );
296 children.append( nChild );
301bool QgsTiledSceneChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
303 long long nodeId = node->tileId().uniqueId;
304 if ( mFutureHierarchyFetches.contains( nodeId ) || mPendingHierarchyFetches.contains( nodeId ) )
309 mFutureHierarchyFetches.insert( nodeId );
317 const QVector<long long> childIds = mIndex.childTileIds( nodeId );
318 for (
long long childId : childIds )
320 if ( mFutureHierarchyFetches.contains( childId ) || mPendingHierarchyFetches.contains( childId ) )
325 mFutureHierarchyFetches.insert( childId );
332void QgsTiledSceneChunkLoaderFactory::fetchHierarchyForNode(
long long nodeId, QgsChunkNode *origNode )
334 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
335 mFutureHierarchyFetches.remove( nodeId );
336 mPendingHierarchyFetches.insert( nodeId );
338 QFutureWatcher<void> *futureWatcher =
new QFutureWatcher<void>(
this );
339 connect( futureWatcher, &QFutureWatcher<void>::finished,
this, [
this, origNode, nodeId, futureWatcher] {
340 mPendingHierarchyFetches.remove( nodeId );
341 emit childrenPrepared( origNode );
342 futureWatcher->deleteLater();
344 futureWatcher->setFuture( QtConcurrent::run( [
this, nodeId] {
345 mIndex.fetchHierarchy( nodeId );
349void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
351 long long nodeId = node->tileId().uniqueId;
352 if ( mFutureHierarchyFetches.contains( nodeId ) )
354 fetchHierarchyForNode( nodeId, node );
362 const QVector<long long> childIds = mIndex.childTileIds( nodeId );
363 for (
long long childId : childIds )
365 if ( mFutureHierarchyFetches.contains( childId ) )
367 fetchHierarchyForNode( childId, node );
375QgsTiledSceneLayerChunkedEntity::QgsTiledSceneLayerChunkedEntity(
380 double maximumScreenError,
381 bool showBoundingBoxes,
385 : QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, layerCrs, zValueScale, zValueOffset ), true )
388 setShowBoundingBoxes( showBoundingBoxes );
391QgsTiledSceneLayerChunkedEntity::~QgsTiledSceneLayerChunkedEntity()
397int QgsTiledSceneLayerChunkedEntity::pendingJobsCount()
const
399 return QgsChunkedEntity::pendingJobsCount() +
static_cast<QgsTiledSceneChunkLoaderFactory *
>( mChunkLoaderFactory )->mPendingHierarchyFetches.count();
402QList<QgsRayCastHit> QgsTiledSceneLayerChunkedEntity::rayIntersection(
const QgsRay3D &ray,
const QgsRayCastContext &context )
const
412 QList<QgsRayCastHit> result;
414 QVector3D intersectionPoint;
415 QgsChunkNode *minNode =
nullptr;
416 int minTriangleIndex = -1;
418 const QList<QgsChunkNode *> active = activeNodes();
419 for ( QgsChunkNode *node : active )
432 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
433 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
435 QVector3D nodeIntPoint;
436 int triangleIndex = -1;
437 QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
438 Q_ASSERT( nodeGeoTransform );
445 float dist = ( ray.
origin() - nodeIntPoint ).length();
446 if ( minDist < 0 || dist < minDist )
450 minTriangleIndex = triangleIndex;
451 intersectionPoint = nodeIntPoint;
463 vm[QStringLiteral(
"node_id" )] = tile.
id();
465 vm[QStringLiteral(
"node_content" )] = tile.
resources().value( QStringLiteral(
"content" ) );
466 vm[QStringLiteral(
"triangle_index" )] = minTriangleIndex;
470 hit.
setMapCoordinates( mMapSettings->worldToMapCoordinates( intersectionPoint ) );
472 result.append( hit );
475 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).
Rendering context for preparation of 3D entities.
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.
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.
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.
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.
Exception thrown on failure to parse Quantized Mesh tile (malformed data).
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 setProperties(const QVariantMap &attributes)
Sets the point cloud point attributes, empty map if hit was not on a point cloud point.
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.
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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
QVector3D toVector3D() const
Converts the current object to QVector3D.
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.
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.