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->mMap, 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 );
96 if ( mContext.symbol()->renderAsTriangles() )
97 mHandler->triangulate( pc, pcNode, mContext, bbox );
101 mFutureWatcher->setFuture( future );
104QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
106 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
108 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
109 mContext.cancelRendering();
110 mFutureWatcher->waitForFinished();
114void QgsPointCloudLayerChunkLoader::cancel()
116 mContext.cancelRendering();
119Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
122 const QgsChunkNodeId nodeId = mNode->tileId();
124 Q_ASSERT( pc->
hasNode( pcNode ) );
126 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
127 mHandler->finalize( entity, mContext );
135 double zValueScale,
double zValueOffset,
int pointBudget )
137 , mCoordinateTransform( coordinateTransform )
138 , mPointCloudIndex( pc )
139 , mZValueScale( zValueScale )
140 , mZValueOffset( zValueOffset )
141 , mPointBudget( pointBudget )
143 mSymbol.reset( symbol );
152 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
156QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
158 const QgsChunkNodeId
id = node->tileId();
162 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
165int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
167 const QgsChunkNodeId
id = node->tileId();
169 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
170 return mPointCloudIndex->nodePointCount( n );
175QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
177 const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds(
IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
179 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
184QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
186 QVector<QgsChunkNode *> children;
187 const QgsChunkNodeId nodeId = node->tileId();
188 const QgsAABB bbox = node->bbox();
189 const float childError = node->error() / 2;
192 for (
int i = 0; i < 8; ++i )
194 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
195 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
197 if ( !mPointCloudIndex->hasNode(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
199 if ( !mExtent.isEmpty() &&
200 !mPointCloudIndex->nodeMapExtent(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
206 const float chXMin = dx ? xc : bbox.
xMin;
207 const float chXMax = dx ? bbox.
xMax : xc;
209 const float chZMin = !dy ? zc : bbox.
zMin;
210 const float chZMax = !dy ? bbox.
zMax : zc;
211 const float chYMin = dz ? yc : bbox.
yMin;
212 const float chYMax = dz ? bbox.
yMax : yc;
213 QgsChunkNode *child =
new QgsChunkNode( childId,
QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
225 QgsVector3D extentMin3D( nodeBounds.
xMin() * scale.
x() + offset.
x(), nodeBounds.
yMin() * scale.
y() + offset.
y(), nodeBounds.
zMin() * scale.
z() + offset.
z() + zValueOffset );
226 QgsVector3D extentMax3D( nodeBounds.
xMax() * scale.
x() + offset.
x(), nodeBounds.
yMax() * scale.
y() + offset.
y(), nodeBounds.
zMax() * scale.
z() + offset.
z() + zValueOffset );
231 extentMin3D = extentTransform.
transform( extentMin3D );
232 extentMax3D = extentTransform.
transform( extentMax3D );
236 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
240 QgsAABB rootBbox( worldExtentMin3D.
x(), worldExtentMin3D.
y(), worldExtentMin3D.
z(),
241 worldExtentMax3D.
x(), worldExtentMax3D.
y(), worldExtentMax3D.
z() );
248 float maximumScreenSpaceError,
bool showBoundingBoxes,
249 double zValueScale,
double zValueOffset,
251 : QgsChunkedEntity( maximumScreenSpaceError,
252 new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
254 setShowBoundingBoxes( showBoundingBoxes );
257QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
263QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
265 QVector<QgsRayCastingUtils::RayHit> result;
266 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
269 const QgsVector3D rayOriginMapCoords = factory->mMap.worldToMapCoordinates( ray.origin() );
270 const QgsVector3D pointMapCoords = factory->mMap.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
271 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
274 const int screenSizePx = std::max( context.screenSize.height(), context.screenSize.width() );
280 const double pointSize = symbol->
pointSize();
285 const double limitAngle = 2. * pointSize / screenSizePx * factory->mMap.fieldOfView();
288 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
289 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
298 double minDist = -1.;
299 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
300 for ( QgsChunkNode *node : activeNodes )
302 const QgsChunkNodeId
id = node->tileId();
308 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, node->bbox() ) )
311 std::unique_ptr<QgsPointCloudBlock> block( index->
nodeData( n, request ) );
318 const char *ptr = block->data();
321 int xOffset = 0, yOffset = 0, zOffset = 0;
325 for (
int i = 0; i < block->pointCount(); ++i )
328 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
333 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
341 const QgsVector3D v1 = projPoint - adjustedRayOrigin ;
344 if ( angle > limitAngle )
347 const double dist = rayOriginMapCoords.distance( point );
349 if ( minDist < 0 || dist < minDist )
353 else if ( context.singleResult )
360 pointAttr[ QStringLiteral(
"X" ) ] = x;
361 pointAttr[ QStringLiteral(
"Y" ) ] = y;
362 pointAttr[ QStringLiteral(
"Z" ) ] = z;
364 const QgsVector3D worldPoint = factory->mMap.mapToWorldCoordinates( point );
366 if ( context.singleResult )
368 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.
qint32 xMax() const
Returns x max.
qint32 xMin() const
Returns x min.
qint32 yMax() const
Returns y max.
qint32 zMax() const
Returns z max.
qint32 yMin() const
Returns y min.
qint32 zMin() const
Returns z 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)