17#include "moc_qgstiledscenechunkloader_p.cpp"
30#include <QtConcurrentRun>
35size_t qHash(
const QgsChunkNodeId &n )
50 return bounds.
width() > 1e5 || bounds.
height() > 1e5 || bounds.
depth() > 1e5;
55QgsTiledSceneChunkLoader::QgsTiledSceneChunkLoader( QgsChunkNode *node,
const QgsTiledSceneIndex &index,
const QgsTiledSceneChunkLoaderFactory &factory,
double zValueScale,
double zValueOffset )
56 : QgsChunkLoader( node )
60 mFutureWatcher =
new QFutureWatcher<void>(
this );
61 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
65 const QgsChunkNodeId tileId = node->tileId();
66 const QFuture<void> future = QtConcurrent::run( [
this, tileId, zValueScale, zValueOffset, boundsTransform]
73 if ( hasLargeBounds( tile, boundsTransform ) )
76 QString uri = tile.
resources().value( QStringLiteral(
"content" ) ).toString();
84 uri = tile.
baseUrl().resolved( uri ).toString();
85 QByteArray content = mFactory.mIndex.retrieveContent( uri );
86 if ( content.isEmpty() )
93 QgsGltf3DUtils::EntityTransform entityTransform;
95 entityTransform.sceneOriginTargetCrs = mFactory.mRenderContext.origin();
96 entityTransform.ecefToTargetCrs = &mFactory.mBoundsTransform;
97 entityTransform.zValueScale = zValueScale;
98 entityTransform.zValueOffset = zValueOffset;
99 entityTransform.gltfUpAxis =
static_cast< Qgis::Axis >( tile.
metadata().value( QStringLiteral(
"gltfUpAxis" ),
static_cast< int >(
Qgis::Axis::Y ) ).toInt() );
101 const QString &format = tile.
metadata().value( QStringLiteral(
"contentFormat" ) ).value<QString>();
103 if ( format == QLatin1String(
"quantizedmesh" ) )
108 qmTile.removeDegenerateTriangles();
109 tinygltf::Model model = qmTile.toGltf(
true, 100 );
110 mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, uri, &errors );
114 errors.append( QStringLiteral(
"Failed to parse tile from '%1'" ).arg( uri ) );
117 else if ( format ==
"cesiumtiles" )
120 if ( tileContent.
gltf.isEmpty() )
122 entityTransform.tileTransform.translate( tileContent.
rtcCenter );
123 mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.
gltf, entityTransform, uri, &errors );
128 if ( !errors.isEmpty() )
138 mFutureWatcher->setFuture( future );
141QgsTiledSceneChunkLoader::~QgsTiledSceneChunkLoader()
143 if ( !mFutureWatcher->isFinished() )
145 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
146 mFutureWatcher->waitForFinished();
150Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *parent )
153 mEntity->setParent( parent );
160 : mRenderContext( context )
162 , mZValueScale( zValueScale )
163 , mZValueOffset( zValueOffset )
168QgsChunkLoader *QgsTiledSceneChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
170 return new QgsTiledSceneChunkLoader( node, mIndex, *
this, mZValueScale, mZValueOffset );
173QgsChunkNode *QgsTiledSceneChunkLoaderFactory::nodeForTile(
const QgsTiledSceneTile &t,
const QgsChunkNodeId &nodeId, QgsChunkNode *parent )
const
175 QgsChunkNode *node =
nullptr;
176 if ( hasLargeBounds( t, mBoundsTransform ) )
179 QgsVector3D v0( mRenderContext.extent().xMinimum(), mRenderContext.extent().yMinimum(), -100 );
180 QgsVector3D v1( mRenderContext.extent().xMaximum(), mRenderContext.extent().yMaximum(), +100 );
183 node =
new QgsChunkNode( nodeId, box3D, err, parent );
190 node =
new QgsChunkNode( nodeId, box, t.
geometricError(), parent );
198QgsChunkNode *QgsTiledSceneChunkLoaderFactory::createRootNode()
const
201 return nodeForTile( t, QgsChunkNodeId( t.
id() ),
nullptr );
205QVector<QgsChunkNode *> QgsTiledSceneChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
207 QVector<QgsChunkNode *> children;
208 const long long indexTileId = node->tileId().uniqueId;
213 const QVector< long long > childIds = mIndex.childTileIds( indexTileId );
214 for (
long long childId : childIds )
216 const QgsChunkNodeId chId( childId );
223 if ( t.
metadata()[
"contentFormat"] == QStringLiteral(
"cesiumtiles" )
224 && hasLargeBounds( t, mBoundsTransform ) )
230 const QgsPointXY c = mRenderContext.extent().center();
233 const double *half = obb.
halfAxes();
236 half[0], half[3], half[6], 0,
237 half[1], half[4], half[7], 0,
238 half[2], half[5], half[8], 0,
240 QVector3D aaa = rot.inverted().map( ecef2.
toVector3D() );
241 if ( aaa.x() > 1 || aaa.y() > 1 || aaa.z() > 1 ||
242 aaa.x() < -1 || aaa.y() < -1 || aaa.z() < -1 )
251 QgsChunkNode *nChild = nodeForTile( t, chId, node );
252 children.append( nChild );
257bool QgsTiledSceneChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
259 long long nodeId = node->tileId().uniqueId;
260 if ( mFutureHierarchyFetches.contains( nodeId ) || mPendingHierarchyFetches.contains( nodeId ) )
265 mFutureHierarchyFetches.insert( nodeId );
273 const QVector< long long > childIds = mIndex.childTileIds( nodeId );
274 for (
long long childId : childIds )
276 if ( mFutureHierarchyFetches.contains( childId ) || mPendingHierarchyFetches.contains( childId ) )
281 mFutureHierarchyFetches.insert( childId );
288void QgsTiledSceneChunkLoaderFactory::fetchHierarchyForNode(
long long nodeId, QgsChunkNode *origNode )
290 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
291 mFutureHierarchyFetches.remove( nodeId );
292 mPendingHierarchyFetches.insert( nodeId );
294 QFutureWatcher<void> *futureWatcher =
new QFutureWatcher<void>(
this );
295 connect( futureWatcher, &QFutureWatcher<void>::finished,
this, [
this, origNode, nodeId, futureWatcher]
297 mPendingHierarchyFetches.remove( nodeId );
298 emit childrenPrepared( origNode );
299 futureWatcher->deleteLater();
301 futureWatcher->setFuture( QtConcurrent::run( [
this, nodeId]
303 mIndex.fetchHierarchy( nodeId );
307void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
309 long long nodeId = node->tileId().uniqueId;
310 if ( mFutureHierarchyFetches.contains( nodeId ) )
312 fetchHierarchyForNode( nodeId, node );
320 const QVector< long long > childIds = mIndex.childTileIds( nodeId );
321 for (
long long childId : childIds )
323 if ( mFutureHierarchyFetches.contains( childId ) )
325 fetchHierarchyForNode( childId, node );
334 : QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, zValueScale, zValueOffset ), true )
337 setShowBoundingBoxes( showBoundingBoxes );
340QgsTiledSceneLayerChunkedEntity::~QgsTiledSceneLayerChunkedEntity()
346int QgsTiledSceneLayerChunkedEntity::pendingJobsCount()
const
348 return QgsChunkedEntity::pendingJobsCount() +
static_cast<QgsTiledSceneChunkLoaderFactory *
>( mChunkLoaderFactory )->mPendingHierarchyFetches.count();
351QVector<QgsRayCastingUtils::RayHit> QgsTiledSceneLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
361 QVector<QgsRayCastingUtils::RayHit> result;
363 QVector3D intersectionPoint;
364 QgsChunkNode *minNode =
nullptr;
365 int minTriangleIndex = -1;
367 const QList<QgsChunkNode *> active = activeNodes();
368 for ( QgsChunkNode *node : active )
376 if ( node->entity() &&
378 QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
383 const QList<Qt3DRender::QGeometryRenderer *> rendLst = node->entity()->findChildren<Qt3DRender::QGeometryRenderer *>();
384 for ( Qt3DRender::QGeometryRenderer *rend : rendLst )
386 QVector3D nodeIntPoint;
387 int triangleIndex = -1;
388 bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, QMatrix4x4(), nodeIntPoint, triangleIndex );
394 float dist = ( ray.origin() - nodeIntPoint ).length();
395 if ( minDist < 0 || dist < minDist )
399 minTriangleIndex = triangleIndex;
400 intersectionPoint = nodeIntPoint;
412 vm[ QStringLiteral(
"node_id" ) ] = tile.
id();
414 vm[ QStringLiteral(
"node_content" ) ] = tile.
resources().value( QStringLiteral(
"content" ) );
415 vm[ QStringLiteral(
"triangle_index" ) ] = minTriangleIndex;
417 result.append( hit );
420 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.
@ 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.