32#include <QtConcurrent>
33#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
34#include <Qt3DRender/QAttribute>
36#include <Qt3DCore/QAttribute>
38#include <Qt3DRender/QTechnique>
39#include <Qt3DRender/QShaderProgram>
40#include <Qt3DRender/QGraphicsApiFilter>
48QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
50 : QgsChunkLoader( node )
52 , 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 QgsAABB bbox = node->bbox();
85 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, bbox,
this]
87 const QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"PC chunk load" ) );
89 if ( mContext.isCanceled() )
95 mHandler->processNode( pc, pcNode, mContext );
97 if ( mContext.isCanceled() )
103 if ( mContext.symbol()->renderAsTriangles() )
104 mHandler->triangulate( pc, pcNode, mContext, bbox );
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 double zValueScale,
double zValueOffset,
int pointBudget )
143 : mRenderContext( context )
144 , mCoordinateTransform( coordinateTransform )
145 , mPointCloudIndex( pc )
146 , mZValueScale( zValueScale )
147 , mZValueOffset( zValueOffset )
148 , mPointBudget( pointBudget )
150 mSymbol.reset( symbol );
159 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
163QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
165 const QgsChunkNodeId
id = node->tileId();
169 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
172int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
174 const QgsChunkNodeId
id = node->tileId();
176 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
177 return mPointCloudIndex->nodePointCount( n );
182QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
184 const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds(
IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mRenderContext, mCoordinateTransform, mZValueOffset );
186 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
191QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
193 QVector<QgsChunkNode *> children;
194 const QgsChunkNodeId nodeId = node->tileId();
195 const QgsAABB bbox = node->bbox();
196 const float childError = node->error() / 2;
199 for (
int i = 0; i < 8; ++i )
201 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
202 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
204 if ( !mPointCloudIndex->hasNode(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
206 if ( !mExtent.isEmpty() &&
207 !mPointCloudIndex->nodeMapExtent(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
213 const float chXMin = dx ? xc : bbox.
xMin;
214 const float chXMax = dx ? bbox.
xMax : xc;
216 const float chZMin = !dy ? zc : bbox.
zMin;
217 const float chZMax = !dy ? bbox.
zMax : zc;
218 const float chYMin = dz ? yc : bbox.
yMin;
219 const float chYMax = dz ? bbox.
yMax : yc;
220 QgsChunkNode *child =
new QgsChunkNode( childId,
QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
232 QgsVector3D extentMin3D( nodeBounds.
xMin() * scale.
x() + offset.
x(), nodeBounds.
yMin() * scale.
y() + offset.
y(), nodeBounds.
zMin() * scale.
z() + offset.
z() + zValueOffset );
233 QgsVector3D extentMax3D( nodeBounds.
xMax() * scale.
x() + offset.
x(), nodeBounds.
yMax() * scale.
y() + offset.
y(), nodeBounds.
zMax() * scale.
z() + offset.
z() + zValueOffset );
238 extentMin3D = extentTransform.
transform( extentMin3D );
239 extentMax3D = extentTransform.
transform( extentMax3D );
243 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
247 QgsAABB rootBbox( worldExtentMin3D.
x(), worldExtentMin3D.
y(), worldExtentMin3D.
z(),
248 worldExtentMax3D.
x(), worldExtentMax3D.
y(), worldExtentMax3D.
z() );
255 float maximumScreenSpaceError,
bool showBoundingBoxes,
256 double zValueScale,
double zValueOffset,
258 : QgsChunkedEntity( map,
259 maximumScreenSpaceError,
260 new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
262 setShowBoundingBoxes( showBoundingBoxes );
265QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
271QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
273 QVector<QgsRayCastingUtils::RayHit> result;
274 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
277 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
278 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
279 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
288 const double pointSize = symbol->
pointSize();
293 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
296 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
297 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
306 double minDist = -1.;
307 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
308 for ( QgsChunkNode *node : activeNodes )
310 const QgsChunkNodeId
id = node->tileId();
316 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, node->bbox() ) )
319 std::unique_ptr<QgsPointCloudBlock> block( index->
nodeData( n, request ) );
326 const char *ptr = block->data();
329 int xOffset = 0, yOffset = 0, zOffset = 0;
333 for (
int i = 0; i < block->pointCount(); ++i )
336 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
341 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
349 const QgsVector3D v1 = projPoint - adjustedRayOrigin ;
352 if ( angle > limitAngle )
355 const double dist = rayOriginMapCoords.distance( point );
357 if ( minDist < 0 || dist < minDist )
368 pointAttr[ QStringLiteral(
"X" ) ] = x;
369 pointAttr[ QStringLiteral(
"Y" ) ] = y;
370 pointAttr[ QStringLiteral(
"Z" ) ] = z;
372 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
376 result.append( hit );
Represents a indexed point cloud node in octree.
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)
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
static QgsVector3D mapToWorldCoordinates(const QgsVector3D &mapCoords, const QgsVector3D &origin)
Converts map coordinates to 3D world coordinates (applies offset and turns (x,y,z) into (x,...
float xCenter() const
Returns center in X axis.
float yCenter() const
Returns center in Y axis.
float zCenter() const
Returns center in Z axis.
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.
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.
Represents packaged data bounds.
qint64 xMin() const
Returns x min.
qint64 zMin() const
Returns z min.
qint64 yMax() const
Returns y max.
qint64 xMax() const
Returns x max.
qint64 zMax() const
Returns z max.
qint64 yMin() const
Returns y min.
Represents a indexed point clouds data in octree.
virtual bool hasNode(const IndexedPointCloudNode &n) const
Returns whether the octree contain given node.
virtual std::unique_ptr< QgsPointCloudBlock > nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
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.