30#include <QtConcurrent>
31#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
32#include <Qt3DRender/QAttribute>
34#include <Qt3DCore/QAttribute>
36#include <Qt3DRender/QTechnique>
37#include <Qt3DRender/QShaderProgram>
38#include <Qt3DRender/QGraphicsApiFilter>
46QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
48 : QgsChunkLoader( node )
50 , mContext( factory->mMap, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
56 const QgsChunkNodeId nodeId = node->tileId();
59 Q_ASSERT( pc->
hasNode( pcNode ) );
61 QgsDebugMsgLevel( QStringLiteral(
"loading entity %1" ).arg( node->tileId().text() ), 2 );
63 if ( mContext.symbol()->symbolType() == QLatin1String(
"single-color" ) )
64 mHandler.reset(
new QgsSingleColorPointCloud3DSymbolHandler() );
65 else if ( mContext.symbol()->symbolType() == QLatin1String(
"color-ramp" ) )
66 mHandler.reset(
new QgsColorRampPointCloud3DSymbolHandler() );
67 else if ( mContext.symbol()->symbolType() == QLatin1String(
"rgb" ) )
68 mHandler.reset(
new QgsRGBPointCloud3DSymbolHandler() );
69 else if ( mContext.symbol()->symbolType() == QLatin1String(
"classification" ) )
71 mHandler.reset(
new QgsClassificationPointCloud3DSymbolHandler() );
79 mFutureWatcher =
new QFutureWatcher<void>(
this );
80 connect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
82 const QgsAABB bbox = node->bbox();
83 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, bbox,
this]
85 const QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"PC chunk load" ) );
87 if ( mContext.isCanceled() )
93 mHandler->processNode( pc, pcNode, mContext );
94 if ( mContext.symbol()->renderAsTriangles() )
95 mHandler->triangulate( pc, pcNode, mContext, bbox );
99 mFutureWatcher->setFuture( future );
102QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
104 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
106 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsChunkQueueJob::finished );
107 mContext.cancelRendering();
108 mFutureWatcher->waitForFinished();
112void QgsPointCloudLayerChunkLoader::cancel()
114 mContext.cancelRendering();
117Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
120 const QgsChunkNodeId nodeId = mNode->tileId();
122 Q_ASSERT( pc->
hasNode( pcNode ) );
124 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity( parent );
125 mHandler->finalize( entity, mContext );
133 double zValueScale,
double zValueOffset,
int pointBudget )
135 , mCoordinateTransform( coordinateTransform )
136 , mPointCloudIndex( pc )
137 , mZValueScale( zValueScale )
138 , mZValueOffset( zValueOffset )
139 , mPointBudget( pointBudget )
141 mSymbol.reset( symbol );
145 mExtent = mCoordinateTransform.transformBoundingBox( mMap.extent(), Qgis::TransformDirection::Reverse );
150 QgsDebugMsg( QStringLiteral(
"Transformation of extent failed." ) );
154QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node )
const
156 const QgsChunkNodeId
id = node->tileId();
160 return new QgsPointCloudLayerChunkLoader(
this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
163int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
const
165 const QgsChunkNodeId
id = node->tileId();
167 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
168 return mPointCloudIndex->nodePointCount( n );
173QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode()
const
175 const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds(
IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
177 return new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
180QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node )
const
182 QVector<QgsChunkNode *> children;
183 const QgsChunkNodeId nodeId = node->tileId();
184 const QgsAABB bbox = node->bbox();
185 const float childError = node->error() / 2;
188 for (
int i = 0; i < 8; ++i )
190 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
191 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
193 if ( !mPointCloudIndex->hasNode(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
195 if ( !mExtent.isEmpty() &&
196 !mPointCloudIndex->nodeMapExtent(
IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
202 const float chXMin = dx ? xc : bbox.
xMin;
203 const float chXMax = dx ? bbox.
xMax : xc;
205 const float chZMin = !dy ? zc : bbox.
zMin;
206 const float chZMax = !dy ? bbox.
zMax : zc;
207 const float chYMin = dz ? yc : bbox.
yMin;
208 const float chYMax = dz ? bbox.
yMax : yc;
209 children <<
new QgsChunkNode( childId,
QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
219 QgsVector3D extentMin3D( nodeBounds.
xMin() * scale.
x() + offset.
x(), nodeBounds.
yMin() * scale.
y() + offset.
y(), nodeBounds.
zMin() * scale.
z() + offset.
z() + zValueOffset );
220 QgsVector3D extentMax3D( nodeBounds.
xMax() * scale.
x() + offset.
x(), nodeBounds.
yMax() * scale.
y() + offset.
y(), nodeBounds.
zMax() * scale.
z() + offset.
z() + zValueOffset );
225 extentMin3D = extentTransform.
transform( extentMin3D );
226 extentMax3D = extentTransform.
transform( extentMax3D );
230 QgsDebugMsg( QStringLiteral(
"Error transforming node bounds coordinate" ) );
234 QgsAABB rootBbox( worldExtentMin3D.
x(), worldExtentMin3D.
y(), worldExtentMin3D.
z(),
235 worldExtentMax3D.
x(), worldExtentMax3D.
y(), worldExtentMax3D.
z() );
242 float maximumScreenSpaceError,
bool showBoundingBoxes,
243 double zValueScale,
double zValueOffset,
245 : QgsChunkedEntity( maximumScreenSpaceError,
246 new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
249 setShowBoundingBoxes( showBoundingBoxes );
252QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
Represents a indexed point cloud node in octree.
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.
bool renderAsTriangles() const
Returns whether points are triangulated to render solid surface.
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.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
#define QgsDebugMsgLevel(str, level)