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        QgsGeoTransform *nodeGeoTransform = node->entity()->findChild<QgsGeoTransform *>();
 
  395        Q_ASSERT( nodeGeoTransform );
 
  396        bool success = QgsRayCastingUtils::rayMeshIntersection( rend, ray, nodeGeoTransform->matrix(), nodeIntPoint, triangleIndex );
 
  402          float dist = ( ray.origin() - nodeIntPoint ).length();
 
  403          if ( minDist < 0 || dist < minDist )
 
  407            minTriangleIndex = triangleIndex;
 
  408            intersectionPoint = nodeIntPoint;
 
  420    vm[QStringLiteral( 
"node_id" )] = tile.
id();
 
  422    vm[QStringLiteral( 
"node_content" )] = tile.
resources().value( QStringLiteral( 
"content" ) );
 
  423    vm[QStringLiteral( 
"triangle_index" )] = minTriangleIndex;
 
  425    result.append( hit );
 
  428  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).
 
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.
 
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.