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 );
160 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
165QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
167 const QgsChunkNodeId
id = node->tileId();
171 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
174int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
176 const QgsChunkNodeId
id = node->tileId();
178 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
179 return mPointCloudIndex.getNode( n ).pointCount();
191 extentMin3D = extentTransform.
transform( extentMin3D );
192 extentMax3D = extentTransform.
transform( extentMax3D );
196 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
198 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
202QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
204 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
206 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
208 const float error = pcNode.
error();
209 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
214QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
216 QVector<QgsChunkNode *> children;
217 const QgsChunkNodeId nodeId = node->tileId();
218 const float childError = node->error() / 2;
220 for (
int i = 0; i < 8; ++i )
222 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
223 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
225 if ( !mPointCloudIndex.hasNode( childPcId ) )
229 if ( !mExtent.isEmpty() && !childBounds.
intersects( mExtent ) )
232 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
234 QgsChunkNode *child =
new QgsChunkNode( childId, childBox3D, childError, node );
244static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode,
QgsPointCloudNodeId nodeId )
247 QVector<QgsPointCloudNodeId> parentIds;
248 while ( nodeId.
d() > 0 )
255 QgsChunkNode *chunk = rootNode;
256 while ( !parentIds.empty() )
259 QgsChunkNodeId childNodeId( p.
d(), p.
x(), p.
y(), p.
z() );
261 if ( !chunk->hasChildrenPopulated() )
264 QgsChunkNode *chunkChild =
nullptr;
265 QgsChunkNode *
const *children = chunk->children();
266 for (
int i = 0; i < chunk->childCount(); ++i )
268 if ( children[i]->tileId() == childNodeId )
270 chunkChild = children[i];
274 Q_ASSERT( chunkChild );
282 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, index, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
285 setShowBoundingBoxes( showBoundingBoxes );
293 mChunkUpdaterFactory.reset(
new QgsChunkUpdaterFactory( mChunkLoaderFactory ) );
296 QgsChunkNode *node = findChunkNodeFromNodeId( mRootNode, n );
299 updateNodes( QList<QgsChunkNode *>() << node, mChunkUpdaterFactory.get() );
305QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
311void QgsPointCloudLayerChunkedEntity::updateIndex()
313 static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
316QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
318 QVector<QgsRayCastingUtils::RayHit> result;
319 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
322 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
323 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
324 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
333 const double pointSize = symbol->
pointSize();
338 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
341 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
342 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
351 double minDist = -1.;
352 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
353 for ( QgsChunkNode *node : activeNodes )
355 const QgsChunkNodeId
id = node->tileId();
362 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
365 std::unique_ptr<QgsPointCloudBlock> block( index.
nodeData( n, request ) );
372 const char *ptr = block->data();
375 int xOffset = 0, yOffset = 0, zOffset = 0;
379 for (
int i = 0; i < block->pointCount(); ++i )
382 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
387 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
395 const QgsVector3D v1 = projPoint - adjustedRayOrigin;
398 if ( angle > limitAngle )
401 const double dist = rayOriginMapCoords.distance( point );
403 if ( minDist < 0 || dist < minDist )
414 pointAttr[QStringLiteral(
"X" )] = x;
415 pointAttr[QStringLiteral(
"Y" )] = y;
416 pointAttr[QStringLiteral(
"Z" )] = z;
418 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
422 result.append( hit );
Provides global constants and enumerations for use throughout the application.
@ Geocentric
Geocentric CRS.
@ Replacement
When tile is refined then its children should be used in place of itself.
@ 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.
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.
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.
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.
float pointSize() const
Returns the point size of the point cloud.
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(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 an indexed point cloud node's position in octree.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
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 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.
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.