17#include "moc_qgspointcloudlayerchunkloader_p.cpp"
33#include <QtConcurrent>
34#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
35#include <Qt3DRender/QAttribute>
37#include <Qt3DCore/QAttribute>
39#include <Qt3DRender/QTechnique>
40#include <Qt3DRender/QShaderProgram>
41#include <Qt3DRender/QGraphicsApiFilter>
49QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
51 : QgsChunkLoader( node )
53 , mContext( factory->mRenderContext, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
59 const QgsChunkNodeId nodeId = node->tileId();
62 Q_ASSERT( pc->
hasNode( pcNode ) );
64 QgsDebugMsgLevel( QStringLiteral(
"loading entity %1" ).arg( node->tileId().text() ), 2 );
66 if ( mContext.symbol()->symbolType() == QLatin1String(
"single-color" ) )
67 mHandler.reset(
new QgsSingleColorPointCloud3DSymbolHandler() );
68 else if ( mContext.symbol()->symbolType() == QLatin1String(
"color-ramp" ) )
69 mHandler.reset(
new QgsColorRampPointCloud3DSymbolHandler() );
70 else if ( mContext.symbol()->symbolType() == QLatin1String(
"rgb" ) )
71 mHandler.reset(
new QgsRGBPointCloud3DSymbolHandler() );
72 else if ( mContext.symbol()->symbolType() == QLatin1String(
"classification" ) )
74 mHandler.reset(
new QgsClassificationPointCloud3DSymbolHandler() );
82 mFutureWatcher =
new QFutureWatcher<void>(
this );
83 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
85 const QgsBox3D box3D = node->box3D();
86 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, box3D,
this]
88 const QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"PC chunk load" ) );
90 if ( mContext.isCanceled() )
96 mHandler->processNode( pc, pcNode, mContext );
98 if ( mContext.isCanceled() )
104 if ( mContext.symbol()->renderAsTriangles() )
105 mHandler->triangulate( pc, pcNode, mContext, box3D );
109 mFutureWatcher->setFuture( future );
112QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
114 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
116 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
117 mContext.cancelRendering();
118 mFutureWatcher->waitForFinished();
122void QgsPointCloudLayerChunkLoader::cancel()
124 mContext.cancelRendering();
127Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
130 const QgsChunkNodeId nodeId = mNode->tileId();
132 Q_ASSERT( pc->
hasNode( pcNode ) );
134 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
135 mHandler->finalize( entity, mContext );
143 double zValueScale,
double zValueOffset,
int pointBudget )
144 : mRenderContext( context )
145 , mCoordinateTransform( coordinateTransform )
146 , mPointCloudIndex( pc )
147 , mZValueScale( zValueScale )
148 , mZValueOffset( zValueOffset )
149 , mPointBudget( pointBudget )
151 mSymbol.reset( symbol );
160 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
164QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
166 const QgsChunkNodeId
id = node->tileId();
170 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
173int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
175 const QgsChunkNodeId
id = node->tileId();
177 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
178 return mPointCloudIndex->nodePointCount( n );
184 QgsVector3D extentMin3D(
static_cast<double>( nodeBounds.
xMin() ) * scale.
x() + offset.
x(),
185 static_cast<double>( nodeBounds.
yMin() ) * scale.
y() + offset.
y(),
186 (
static_cast<double>( nodeBounds.
zMin() ) * scale.
z() + offset.
z() ) * zValueScale + zValueOffset );
187 QgsVector3D extentMax3D(
static_cast<double>( nodeBounds.
xMax() ) * scale.
x() + offset.
x(),
188 static_cast<double>( nodeBounds.
yMax() ) * scale.
y() + offset.
y(),
189 (
static_cast<double>( nodeBounds.
zMax() ) * scale.
z() + offset.
z() ) * zValueScale + zValueOffset );
194 extentMin3D = extentTransform.
transform( extentMin3D );
195 extentMax3D = extentTransform.
transform( extentMax3D );
199 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
201 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(),
202 extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
206QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
209 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mPointCloudIndex->offset(), mPointCloudIndex->scale(), mCoordinateTransform, mZValueOffset, mZValueScale );
212 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
217QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
219 QVector<QgsChunkNode *> children;
220 const QgsChunkNodeId nodeId = node->tileId();
221 const float childError = node->error() / 2;
223 for (
int i = 0; i < 8; ++i )
225 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
226 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
228 if ( !mPointCloudIndex->hasNode(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
230 if ( !mExtent.isEmpty() &&
231 !mPointCloudIndex->nodeMapExtent(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
235 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mPointCloudIndex->offset(), mPointCloudIndex->scale(), mCoordinateTransform, mZValueOffset, mZValueScale );
237 QgsChunkNode *child =
new QgsChunkNode( childId, childBox3D, childError, node );
249 float maximumScreenSpaceError,
bool showBoundingBoxes,
250 double zValueScale,
double zValueOffset,
252 : QgsChunkedEntity( map,
253 maximumScreenSpaceError,
254 new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
256 setShowBoundingBoxes( showBoundingBoxes );
259QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
265QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
267 QVector<QgsRayCastingUtils::RayHit> result;
268 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
271 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
272 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
273 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
282 const double pointSize = symbol->
pointSize();
287 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
290 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
291 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
300 double minDist = -1.;
301 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
302 for ( QgsChunkNode *node : activeNodes )
304 const QgsChunkNodeId
id = node->tileId();
311 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
314 std::unique_ptr<QgsPointCloudBlock> block( index->
nodeData( n, request ) );
321 const char *ptr = block->data();
324 int xOffset = 0, yOffset = 0, zOffset = 0;
328 for (
int i = 0; i < block->pointCount(); ++i )
331 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
336 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
344 const QgsVector3D v1 = projPoint - adjustedRayOrigin ;
347 if ( angle > limitAngle )
350 const double dist = rayOriginMapCoords.distance( point );
352 if ( minDist < 0 || dist < minDist )
363 pointAttr[ QStringLiteral(
"X" ) ] = x;
364 pointAttr[ QStringLiteral(
"Y" ) ] = y;
365 pointAttr[ QStringLiteral(
"Z" ) ] = z;
367 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
371 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)
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.
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.