38#include <Qt3DCore/QAttribute>
39#include <Qt3DRender/QGraphicsApiFilter>
40#include <Qt3DRender/QShaderProgram>
41#include <Qt3DRender/QTechnique>
42#include <QtConcurrentRun>
44#include "moc_qgspointcloudlayerchunkloader_p.cpp"
46using namespace Qt::StringLiterals;
53QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
54 const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr<QgsPointCloud3DSymbol> symbol,
const QgsCoordinateTransform &coordinateTransform,
double zValueScale,
double zValueOffset
56 : QgsChunkLoader( node )
58 , mContext( factory->mRenderContext, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
61void QgsPointCloudLayerChunkLoader::start()
63 QgsChunkNode *node = chunk();
67 const QgsChunkNodeId nodeId = node->tileId();
70 Q_ASSERT( pc.
hasNode( pcNode ) );
74 if ( mContext.symbol()->symbolType() ==
"single-color"_L1 )
75 mHandler = std::make_unique<QgsSingleColorPointCloud3DSymbolHandler>();
76 else if ( mContext.symbol()->symbolType() ==
"color-ramp"_L1 )
77 mHandler = std::make_unique<QgsColorRampPointCloud3DSymbolHandler>();
78 else if ( mContext.symbol()->symbolType() ==
"rgb"_L1 )
79 mHandler = std::make_unique<QgsRGBPointCloud3DSymbolHandler>();
80 else if ( mContext.symbol()->symbolType() ==
"classification"_L1 )
82 mHandler = std::make_unique<QgsClassificationPointCloud3DSymbolHandler>();
90 mFutureWatcher =
new QFutureWatcher<void>(
this );
91 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
93 const QgsBox3D box3D = node->box3D();
94 const QFuture<void> future = QtConcurrent::run( [pc = std::move( pc ), pcNode, box3D,
this] {
95 const QgsEventTracing::ScopedEvent e( u
"3D"_s, u
"PC chunk load"_s );
97 if ( mContext.isCanceled() )
104 mHandler->processNode( pc2, pcNode, mContext );
106 if ( mContext.isCanceled() )
112 if ( mContext.symbol()->renderAsTriangles() )
113 mHandler->triangulate( pc2, pcNode, mContext, box3D );
117 mFutureWatcher->setFuture( future );
120QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
122 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
124 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
125 mContext.cancelRendering();
126 mFutureWatcher->waitForFinished();
130void QgsPointCloudLayerChunkLoader::cancel()
132 mContext.cancelRendering();
135Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
138 const QgsChunkNodeId nodeId = mNode->tileId();
140 Q_ASSERT( pc.
hasNode( pcNode ) );
142 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
143 mHandler->finalize( entity, mContext );
150QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory(
153 : mRenderContext( context )
154 , mCoordinateTransform( coordinateTransform )
155 , mPointCloudIndex( std::move( pc ) )
156 , mZValueScale( zValueScale )
157 , mZValueOffset( zValueOffset )
158 , mPointBudget( pointBudget )
160 mSymbol.reset( symbol );
166 mExtent = mCoordinateTransform.transformBoundingBox( mRenderContext.extent(), Qgis::TransformDirection::Reverse );
176QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
178 const QgsChunkNodeId
id = node->tileId();
182 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
185int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
187 const QgsChunkNodeId
id = node->tileId();
189 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
190 return mPointCloudIndex.getNode( n ).pointCount();
193bool QgsPointCloudLayerChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
195 const QgsChunkNodeId
id = node->tileId();
197 if ( mFutureHierarchyFetches.contains( n ) || mPendingHierarchyFetches.contains( n ) )
202 if ( mPointCloudIndex.needsHierarchyFetching( n ) )
204 mFutureHierarchyFetches.insert( n );
211void QgsPointCloudLayerChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
213 const QgsChunkNodeId
id = node->tileId();
215 if ( mFutureHierarchyFetches.contains( n ) )
217 fetchHierarchyForNode( n, node );
221void QgsPointCloudLayerChunkLoaderFactory::fetchHierarchyForNode(
const QgsPointCloudNodeId &nodeId, QgsChunkNode *origNode )
223 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
224 mFutureHierarchyFetches.remove( nodeId );
225 mPendingHierarchyFetches.insert( nodeId );
227 QFutureWatcher<void> *futureWatcher =
new QFutureWatcher<void>(
this );
228 connect( futureWatcher, &QFutureWatcher<void>::finished,
this, [
this, origNode, nodeId, futureWatcher] {
229 mPendingHierarchyFetches.remove( nodeId );
230 emit childrenPrepared( origNode );
231 futureWatcher->deleteLater();
233 futureWatcher->setFuture( QtConcurrent::run( [
this, nodeId] {
237 ( void ) mPointCloudIndex.hasNode( nodeId );
238 const QVector<QgsPointCloudNodeId> children = nodeId.
childrenNodes();
241 ( void ) mPointCloudIndex.hasNode( child );
243 const QVector<QgsPointCloudNodeId> grandchildren = child.childrenNodes();
246 ( void ) mPointCloudIndex.hasNode( grandChild );
261 extentMin3D = extentTransform.
transform( extentMin3D );
262 extentMax3D = extentTransform.
transform( extentMax3D );
266 QgsDebugError( u
"Error transforming node bounds coordinate"_s );
268 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
272QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
274 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
276 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
278 const float error = pcNode.
error();
279 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
284QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
286 QVector<QgsChunkNode *> children;
287 const QgsChunkNodeId nodeId = node->tileId();
288 const float childError = node->error() / 2;
291 const QVector<QgsPointCloudNodeId> childrenPcIds = pcId.childrenNodes();
294 const QgsChunkNodeId childId( childPcId.d(), childPcId.x(), childPcId.y(), childPcId.z() );
295 if ( !mPointCloudIndex.hasNode( childPcId ) )
302 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
304 QgsChunkNode *child =
new QgsChunkNode( childId, childBox3D, childError, node );
314static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode,
QgsPointCloudNodeId nodeId )
317 QVector<QgsPointCloudNodeId> parentIds;
318 while ( nodeId.
d() > 0 )
325 QgsChunkNode *chunk = rootNode;
326 while ( !parentIds.empty() )
329 QgsChunkNodeId childNodeId( p.
d(), p.
x(), p.
y(), p.
z() );
331 if ( !chunk->hasChildrenPopulated() )
334 QgsChunkNode *chunkChild =
nullptr;
335 QgsChunkNode *
const *children = chunk->children();
336 for (
int i = 0; i < chunk->childCount(); ++i )
338 if ( children[i]->tileId() == childNodeId )
340 chunkChild = children[i];
344 Q_ASSERT( chunkChild );
351QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity(
354 const int indexPosition,
357 float maximumScreenSpaceError,
358 bool showBoundingBoxes,
363 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, resolveIndex( pcl, indexPosition ), symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
365 , mIndexPosition( indexPosition )
367 setShowBoundingBoxes( showBoundingBoxes );
371 mChunkUpdaterFactory = std::make_unique<QgsChunkUpdaterFactory>( mChunkLoaderFactory );
373 if ( ( pcl->
isVpc() && indexPosition >= 0 ) || ( !pcl->
isVpc() ) )
378 if ( indexPosition != mIndexPosition )
381 QgsChunkNode *node = findChunkNodeFromNodeId( mRootNode, n );
384 updateNodes( QList<QgsChunkNode *>() << node, mChunkUpdaterFactory.get() );
391QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
399 if ( indexPosition >= 0 && pcl->
isVpc() )
400 return pcl->
subIndexes().at( indexPosition ).index();
401 else if ( indexPosition == -1 && !pcl->
isVpc() )
403 else if ( indexPosition < -1 && pcl->isVpc() )
405 const int ovId = -indexPosition - 2;
412void QgsPointCloudLayerChunkedEntity::updateIndex()
414 if ( mLayer->isVpc() )
416 if ( mIndexPosition >= 0 )
417 static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->subIndexes().at( mIndexPosition ).index();
420 static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
423QList<QgsRayCastHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRay3D &ray,
const QgsRayCastContext &context )
const
425 QList<QgsRayCastHit> result;
426 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
434 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.
origin() );
436 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
445 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
446 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
455 double minDist = -1.;
456 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
457 for ( QgsChunkNode *node : activeNodes )
459 const QgsChunkNodeId
id = node->tileId();
469 std::unique_ptr<QgsPointCloudBlock> block( index.
nodeData( n, request ) );
476 const char *ptr = block->data();
479 int xOffset = 0, yOffset = 0, zOffset = 0;
483 for (
int i = 0; i < block->pointCount(); ++i )
486 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
491 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
500 const double d1 = projPoint.
distance( adjustedRayOrigin );
501 const double d2 = projPoint.
distance( point );
502 const double angle = std::atan2( d2, d1 );
504 if ( angle > limitAngle )
507 const double dist = rayOriginMapCoords.distance( point );
509 if ( minDist < 0 || dist < minDist )
520 pointAttr[u
"X"_s] = x;
521 pointAttr[u
"Y"_s] = y;
522 pointAttr[u
"Z"_s] = z;
530 result.append( hit );
@ Geocentric
Geocentric CRS.
@ Additive
When tile is refined its content should be used alongside its children simultaneously.
@ Replacement
When tile is refined then its children should be used in place of itself.
Rendering context for preparation of 3D entities.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
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.
A 3-dimensional box composed of x, y, z coordinates.
double yMaximum() const
Returns the maximum y 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.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
double zMinimum() const
Returns the minimum z value.
double yMinimum() const
Returns the minimum y value.
3D symbol that draws point cloud geometries as 3D objects using classification of the dataset.
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
Qgis::CrsType type() const
Returns the type of the CRS.
Custom exception class for Coordinate Reference System related exceptions.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
3D symbol that draws point cloud geometries as 3D objects.
A collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
static QVariantMap getAttributeMap(const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection)
Retrieves all the attributes of a point.
DataType type() const
Returns the data type.
Smart pointer for QgsAbstractPointCloudIndex.
std::unique_ptr< QgsPointCloudBlock > nodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns node data block.
bool hasNode(QgsPointCloudNodeId id) const
Returns whether the octree contain given node.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Represents a map layer supporting display of point clouds.
void chunkAttributeValuesChanged(QgsPointCloudNodeId n, const int position)
Emitted when a node gets some attribute values of some points changed.
QgsPointCloudIndex index() const
Returns the point cloud index associated with the layer.
bool isVpc() const
Returns whether the layer has a virtual point cloud data provider or not.
QVector< QgsPointCloudIndex > overviews() const
Returns a list of all overview point cloud indexes associated with the layer (only if the layer has a...
QVector< QgsPointCloudSubIndex > subIndexes() const
Returns point cloud indexes associated with the layer (only if the layer has a virtual point cloud da...
bool supportsEditing() const override
Returns whether the layer supports editing or not.
Represents an indexed point cloud node's position in octree.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
QVector< QgsPointCloudNodeId > childrenNodes() const
Returns the node's 8 direct child nodes.
Keeps metadata for an indexed point cloud node.
float error() const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
A representation of a ray in 3D.
QVector3D origin() const
Returns the origin of the ray.
QVector3D direction() const
Returns the direction of the ray see setDirection().
Responsible for defining parameters of the ray casting operations in 3D map canvases.
float angleThreshold() const
Sets an angle threshold in degrees for ray intersections, effectively turning a ray into a cone.
bool singleResult() const
Returns whether to fetch only the closest hit for each layer or entity type.
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.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double distance(const QgsVector3D &other) const
Returns the distance with the other QgsVector3D.
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
double x() const
Returns X coordinate.
void normalize()
Normalizes the current vector in place.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
bool rayBoxIntersection(const QgsRay3D &ray, const QgsAABB &nodeBbox)
Tests whether an axis aligned box is intersected by a ray.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)