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() )
94 mHandler->processNode( pc, pcNode, mContext );
96 if ( mContext.isCanceled() )
102 if ( mContext.symbol()->renderAsTriangles() )
103 mHandler->triangulate( pc, pcNode, mContext, box3D );
107 mFutureWatcher->setFuture( future );
110QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
112 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
114 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
115 mContext.cancelRendering();
116 mFutureWatcher->waitForFinished();
120void QgsPointCloudLayerChunkLoader::cancel()
122 mContext.cancelRendering();
125Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
128 const QgsChunkNodeId nodeId = mNode->tileId();
130 Q_ASSERT( pc->
hasNode( pcNode ) );
132 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
133 mHandler->finalize( entity, mContext );
141 : mRenderContext( context )
142 , mCoordinateTransform( coordinateTransform )
143 , mPointCloudIndex( pc )
144 , mZValueScale( zValueScale )
145 , mZValueOffset( zValueOffset )
146 , mPointBudget( pointBudget )
148 mSymbol.reset( symbol );
157 QgsDebugError( QStringLiteral(
"Transformation of extent failed." ) );
161QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
163 const QgsChunkNodeId
id = node->tileId();
167 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
170int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
172 const QgsChunkNodeId
id = node->tileId();
174 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
175 return mPointCloudIndex->getNode( n ).pointCount();
187 extentMin3D = extentTransform.
transform( extentMin3D );
188 extentMax3D = extentTransform.
transform( extentMax3D );
192 QgsDebugError( QStringLiteral(
"Error transforming node bounds coordinate" ) );
194 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
198QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
200 const QgsPointCloudNode pcNode = mPointCloudIndex->getNode( mPointCloudIndex->root() );
202 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
204 const float error = pcNode.
error();
205 QgsChunkNode *node =
new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
210QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
212 QVector<QgsChunkNode *> children;
213 const QgsChunkNodeId nodeId = node->tileId();
214 const float childError = node->error() / 2;
216 for (
int i = 0; i < 8; ++i )
218 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
219 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
221 if ( !mPointCloudIndex->hasNode( childPcId ) )
225 if ( !mExtent.isEmpty() && !childBounds.
intersects( mExtent ) )
228 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
230 QgsChunkNode *child =
new QgsChunkNode( childId, childBox3D, childError, node );
241 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory(
Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
243 setShowBoundingBoxes( showBoundingBoxes );
246QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
252QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection(
const QgsRayCastingUtils::Ray3D &ray,
const QgsRayCastingUtils::RayCastContext &context )
const
254 QVector<QgsRayCastingUtils::RayHit> result;
255 QgsPointCloudLayerChunkLoaderFactory *factory =
static_cast<QgsPointCloudLayerChunkLoaderFactory *
>( mChunkLoaderFactory );
258 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
259 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
260 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
269 const double pointSize = symbol->
pointSize();
274 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
277 const QgsVector3D adjustedRayOrigin =
QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
278 QgsVector3D adjustedRayDirection =
QgsVector3D( rayDirectionMapCoords.
x(), rayDirectionMapCoords.
y(), rayDirectionMapCoords.
z() / factory->mZValueScale );
287 double minDist = -1.;
288 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
289 for ( QgsChunkNode *node : activeNodes )
291 const QgsChunkNodeId
id = node->tileId();
298 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
301 std::unique_ptr<QgsPointCloudBlock> block( index->
nodeData( n, request ) );
308 const char *ptr = block->data();
311 int xOffset = 0, yOffset = 0, zOffset = 0;
315 for (
int i = 0; i < block->pointCount(); ++i )
318 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
323 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
331 const QgsVector3D v1 = projPoint - adjustedRayOrigin;
334 if ( angle > limitAngle )
337 const double dist = rayOriginMapCoords.distance( point );
339 if ( minDist < 0 || dist < minDist )
350 pointAttr[QStringLiteral(
"X" )] = x;
351 pointAttr[QStringLiteral(
"Y" )] = y;
352 pointAttr[QStringLiteral(
"Z" )] = z;
354 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
358 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.
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 a indexed point clouds data in octree.
virtual bool hasNode(const QgsPointCloudNodeId &n) const
Returns whether the octree contain given node.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
virtual std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)=0
Returns node data block.
Represents a indexed point cloud node's position in octree.
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.