17#include "moc_qgspointcloudlayerchunkloader_p.cpp"
34#include <QtConcurrent>
35#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
36#include <Qt3DRender/QAttribute>
38#include <Qt3DCore/QAttribute>
40#include <Qt3DRender/QTechnique>
41#include <Qt3DRender/QShaderProgram>
42#include <Qt3DRender/QGraphicsApiFilter>
50QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr<QgsPointCloud3DSymbol> symbol,
const QgsCoordinateTransform &coordinateTransform,
double zValueScale,
double zValueOffset )
51 : QgsChunkLoader( node )
53 , mContext( factory->mRenderContext, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
58 const QgsChunkNodeId nodeId = node->tileId();
61 Q_ASSERT( pc.
hasNode( pcNode ) );
63 QgsDebugMsgLevel( QStringLiteral(
"loading entity %1" ).arg( node->tileId().text() ), 2 );
65 if ( mContext.symbol()->symbolType() == QLatin1String(
"single-color" ) )
66 mHandler.reset(
new QgsSingleColorPointCloud3DSymbolHandler() );
67 else if ( mContext.symbol()->symbolType() == QLatin1String(
"color-ramp" ) )
68 mHandler.reset(
new QgsColorRampPointCloud3DSymbolHandler() );
69 else if ( mContext.symbol()->symbolType() == QLatin1String(
"rgb" ) )
70 mHandler.reset(
new QgsRGBPointCloud3DSymbolHandler() );
71 else if ( mContext.symbol()->symbolType() == QLatin1String(
"classification" ) )
73 mHandler.reset(
new QgsClassificationPointCloud3DSymbolHandler() );
81 mFutureWatcher =
new QFutureWatcher<void>(
this );
82 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
84 const QgsBox3D box3D = node->box3D();
85 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, box3D,
this] {
86 const QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"PC chunk load" ) );
88 if ( mContext.isCanceled() )
95 mHandler->processNode( pc2, pcNode, mContext );
97 if ( mContext.isCanceled() )
103 if ( mContext.symbol()->renderAsTriangles() )
104 mHandler->triangulate( pc2, pcNode, mContext, box3D );
108 mFutureWatcher->setFuture( future );
111QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
113 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
115 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
116 mContext.cancelRendering();
117 mFutureWatcher->waitForFinished();
121void QgsPointCloudLayerChunkLoader::cancel()
123 mContext.cancelRendering();
126Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
129 const QgsChunkNodeId nodeId = mNode->tileId();
131 Q_ASSERT( pc.
hasNode( pcNode ) );
133 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
134 mHandler->finalize( entity, mContext );
142 : mRenderContext( context )
143 , mCoordinateTransform( coordinateTransform )
144 , mPointCloudIndex( pc )
145 , mZValueScale( zValueScale )
146 , mZValueOffset( zValueOffset )
147 , mPointBudget( pointBudget )
149 mSymbol.reset( symbol );
158 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
162QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
164 const QgsChunkNodeId
id = node->tileId();
168 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
171int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
173 const QgsChunkNodeId
id = node->tileId();
175 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
176 return mPointCloudIndex.getNode( n ).pointCount();
188 extentMin3D = extentTransform.
transform( extentMin3D );
189 extentMax3D = extentTransform.
transform( extentMax3D );
193 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
195 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
199QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
201 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
203 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
205 const float error = pcNode.
error();
206 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
211QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
213 QVector<QgsChunkNode *> children;
214 const QgsChunkNodeId nodeId = node->tileId();
215 const float childError = node->error() / 2;
217 for (
int i = 0; i < 8; ++i )
219 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
220 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
222 if ( !mPointCloudIndex.hasNode( childPcId ) )
226 if ( !mExtent.isEmpty() && !childBounds.
intersects( mExtent ) )
229 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
231 QgsChunkNode *child =
new QgsChunkNode( childId, childBox3D, childError, node );
241static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode,
QgsPointCloudNodeId nodeId )
244 QVector<QgsPointCloudNodeId> parentIds;
245 while ( nodeId.
d() > 0 )
252 QgsChunkNode *chunk = rootNode;
253 while ( !parentIds.empty() )
256 QgsChunkNodeId childNodeId( p.
d(), p.
x(), p.
y(), p.
z() );
258 QgsChunkNode *chunkChild =
nullptr;
259 QgsChunkNode *
const *children = chunk->children();
260 for (
int i = 0; i < chunk->childCount(); ++i )
262 if ( children[i]->tileId() == childNodeId )
264 chunkChild = children[i];
268 Q_ASSERT( chunkChild );
276 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, index, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
279 setShowBoundingBoxes( showBoundingBoxes );
287 mChunkUpdaterFactory.reset(
new QgsChunkUpdaterFactory( mChunkLoaderFactory ) );
290 QList<QgsChunkNode *> nodes;
291 nodes << findChunkNodeFromNodeId( mRootNode, n );
292 updateNodes( nodes, mChunkUpdaterFactory.get() );
297QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
303void QgsPointCloudLayerChunkedEntity::updateIndex()
305 static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
308QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
310 QVector<QgsRayCastingUtils::RayHit> result;
311 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
314 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
315 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
316 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
325 const double pointSize = symbol->
pointSize();
330 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
333 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
334 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
343 double minDist = -1.;
344 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
345 for ( QgsChunkNode *node : activeNodes )
347 const QgsChunkNodeId
id = node->tileId();
354 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
357 std::unique_ptr<QgsPointCloudBlock> block( index.
nodeData( n, request ) );
364 const char *ptr = block->data();
367 int xOffset = 0, yOffset = 0, zOffset = 0;
371 for (
int i = 0; i < block->pointCount(); ++i )
374 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
379 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
387 const QgsVector3D v1 = projPoint - adjustedRayOrigin;
390 if ( angle > limitAngle )
393 const double dist = rayOriginMapCoords.distance( point );
395 if ( minDist < 0 || dist < minDist )
406 pointAttr[QStringLiteral(
"X" )] = x;
407 pointAttr[QStringLiteral(
"Y" )] = y;
408 pointAttr[QStringLiteral(
"Z" )] = z;
410 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
414 result.append( hit );
The Qgis class provides global constants for use throughout the application.
@ Replacement
When tile is refined then its children should be used in place of itself.
@ Reverse
Reverse/inverse transform (from destination to source)
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.
A 3-dimensional box composed of x, y, z coordinates.
double yMaximum() const
Returns the maximum y value.
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
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 zMinimum() const
Returns the minimum z value.
double yMinimum() const
Returns the minimum y value.
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
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.
float pointSize() const
Returns the point size of the point cloud.
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(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
bool hasNode(const 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(const QgsPointCloudNodeId &n)
Emitted when a node gets some attribute values of some points changed.
bool supportsEditing() const override
Returns whether the layer supports editing or not.
Represents a indexed point cloud node's position in octree.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
Keeps metadata for 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.
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.
QVector3D toVector3D() const
Converts the current object to QVector3D.
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 length() const
Returns the length of the vector.
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)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Helper struct to store ray casting parameters.
QSize screenSize
QSize of the 3d engine window.
bool singleResult
If set to true, only the closest point cloud hit will be returned (other entities always return only ...
Helper struct to store ray casting results.