QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgspointcloudlayerchunkloader_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspointcloudlayerchunkloader_p.cpp
3  --------------------------------------
4  Date : October 2020
5  Copyright : (C) 2020 by Peter Petrik
6  Email : zilolv dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgs3dutils.h"
20 #include "qgschunknode_p.h"
21 #include "qgslogger.h"
22 #include "qgspointcloudlayer.h"
23 #include "qgspointcloudindex.h"
24 #include "qgseventtracing.h"
25 
26 #include "qgspoint3dsymbol.h"
28 
29 #include "qgspointcloud3dsymbol.h"
30 #include "qgsapplication.h"
31 #include "qgs3dsymbolregistry.h"
32 #include "qgspointcloudattribute.h"
33 #include "qgspointcloudrequest.h"
35 
36 #include <QtConcurrent>
37 #include <Qt3DRender/QAttribute>
38 #include <Qt3DRender/QTechnique>
39 #include <Qt3DRender/QShaderProgram>
40 #include <Qt3DRender/QGraphicsApiFilter>
41 #include <QPointSize>
42 
44 
45 
47 
48 QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
49  const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset )
50  : QgsChunkLoader( node )
51  , mFactory( factory )
52  , mContext( factory->mMap, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
53 {
54 
55  QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
56  mContext.setAttributes( pc->attributes() );
57 
58  const QgsChunkNodeId nodeId = node->tileId();
59  const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
60 
61  Q_ASSERT( pc->hasNode( pcNode ) );
62 
63  QgsDebugMsgLevel( QStringLiteral( "loading entity %1" ).arg( node->tileId().text() ), 2 );
64 
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" ) )
72  {
73  mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );
74  const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
75  mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
76  }
77 
78  //
79  // this will be run in a background thread
80  //
81  mFutureWatcher = new QFutureWatcher<void>( this );
82  connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
83 
84  const QgsAABB bbox = node->bbox();
85  const QFuture<void> future = QtConcurrent::run( [pc, pcNode, bbox, this]
86  {
87  const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
88 
89  if ( mContext.isCanceled() )
90  {
91  QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
92  return;
93  }
94 
95  mHandler->processNode( pc, pcNode, mContext );
96  if ( mContext.symbol()->renderAsTriangles() )
97  mHandler->triangulate( pc, pcNode, mContext, bbox );
98  } );
99 
100  // emit finished() as soon as the handler is populated with features
101  mFutureWatcher->setFuture( future );
102 }
103 
104 QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
105 {
106  if ( mFutureWatcher && !mFutureWatcher->isFinished() )
107  {
108  disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
109  mContext.cancelRendering();
110  mFutureWatcher->waitForFinished();
111  }
112 }
113 
114 void QgsPointCloudLayerChunkLoader::cancel()
115 {
116  mContext.cancelRendering();
117 }
118 
119 Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
120 {
121  QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
122  const QgsChunkNodeId nodeId = mNode->tileId();
123  const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
124  Q_ASSERT( pc->hasNode( pcNode ) );
125 
126  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
127  mHandler->finalize( entity, mContext );
128  return entity;
129 }
130 
132 
133 
134 QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
135  double zValueScale, double zValueOffset, int pointBudget )
136  : mMap( map )
137  , mCoordinateTransform( coordinateTransform )
138  , mPointCloudIndex( pc )
139  , mZValueScale( zValueScale )
140  , mZValueOffset( zValueOffset )
141  , mPointBudget( pointBudget )
142 {
143  mSymbol.reset( symbol );
144 }
145 
146 QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
147 {
148  const QgsChunkNodeId id = node->tileId();
149 
150  Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
151  QgsPointCloud3DSymbol *symbol = static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() );
152  return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
153 }
154 
155 int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
156 {
157  const QgsChunkNodeId id = node->tileId();
158  const IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
159  Q_ASSERT( mPointCloudIndex->hasNode( n ) );
160  return mPointCloudIndex->nodePointCount( n );
161 }
162 
163 QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset );
164 
165 QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
166 {
167  const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
168  const float error = mPointCloudIndex->nodeError( IndexedPointCloudNode( 0, 0, 0, 0 ) );
169  return new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
170 }
171 
172 QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
173 {
174  QVector<QgsChunkNode *> children;
175  const QgsChunkNodeId nodeId = node->tileId();
176  const QgsAABB bbox = node->bbox();
177  const float childError = node->error() / 2;
178  float xc = bbox.xCenter(), yc = bbox.yCenter(), zc = bbox.zCenter();
179 
180  for ( int i = 0; i < 8; ++i )
181  {
182  int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
183  const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
184 
185  if ( !mPointCloudIndex->hasNode( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
186  continue;
187 
188  // the Y and Z coordinates below are intentionally flipped, because
189  // in chunk node IDs the X,Y axes define horizontal plane,
190  // while in our 3D scene the X,Z axes define the horizontal plane
191  const float chXMin = dx ? xc : bbox.xMin;
192  const float chXMax = dx ? bbox.xMax : xc;
193  // Z axis: values are increasing to the south
194  const float chZMin = !dy ? zc : bbox.zMin;
195  const float chZMax = !dy ? bbox.zMax : zc;
196  const float chYMin = dz ? yc : bbox.yMin;
197  const float chYMax = dz ? bbox.yMax : yc;
198  children << new QgsChunkNode( childId, QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
199  }
200  return children;
201 }
202 
204 
205 
206 QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset )
207 {
208  QgsVector3D extentMin3D( nodeBounds.xMin() * scale.x() + offset.x(), nodeBounds.yMin() * scale.y() + offset.y(), nodeBounds.zMin() * scale.z() + offset.z() + zValueOffset );
209  QgsVector3D extentMax3D( nodeBounds.xMax() * scale.x() + offset.x(), nodeBounds.yMax() * scale.y() + offset.y(), nodeBounds.zMax() * scale.z() + offset.z() + zValueOffset );
210  QgsCoordinateTransform extentTransform = coordinateTransform;
211  extentTransform.setBallparkTransformsAreAppropriate( true );
212  try
213  {
214  extentMin3D = extentTransform.transform( extentMin3D );
215  extentMax3D = extentTransform.transform( extentMax3D );
216  }
217  catch ( QgsCsException & )
218  {
219  QgsDebugMsg( QStringLiteral( "Error transforming node bounds coordinate" ) );
220  }
221  const QgsVector3D worldExtentMin3D = Qgs3DUtils::mapToWorldCoordinates( extentMin3D, map.origin() );
222  const QgsVector3D worldExtentMax3D = Qgs3DUtils::mapToWorldCoordinates( extentMax3D, map.origin() );
223  QgsAABB rootBbox( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMin3D.z(),
224  worldExtentMax3D.x(), worldExtentMax3D.y(), worldExtentMax3D.z() );
225  return rootBbox;
226 }
227 
228 
229 QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map,
230  const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol,
231  float maximumScreenSpaceError, bool showBoundingBoxes,
232  double zValueScale, double zValueOffset,
233  int pointBudget )
234  : QgsChunkedEntity( maximumScreenSpaceError,
235  new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
236 {
237  setUsingAdditiveStrategy( !symbol->renderAsTriangles() );
239 }
240 
241 QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
242 {
243  // cancel / wait for jobs
244  cancelActiveJobs();
245 }
246 
QgsPointCloudLayer3DRenderer::setShowBoundingBoxes
void setShowBoundingBoxes(bool showBoundingBoxes)
Sets whether bounding boxes will be visible when rendering the point cloud.
Definition: qgspointcloudlayer3drenderer.cpp:215
QgsPointCloudDataBounds::zMax
qint32 zMax() const
Returns z max.
Qgs3DUtils::mapToWorldCoordinates
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,...
Definition: qgs3dutils.cpp:540
QgsPointCloudDataBounds::yMin
qint32 yMin() const
Returns y min.
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsVector3D::y
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:64
QgsVector3D
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
qgschunknode_p.h
qgspointcloudattribute.h
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsPointCloudDataBounds::xMax
qint32 xMax() const
Returns x max.
QgsPointCloudIndex::attributes
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Definition: qgspointcloudindex.cpp:192
QgsAABB::zMax
float zMax
Definition: qgsaabb.h:86
QgsPointCloudLayer3DRenderer::symbol
const QgsPointCloud3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Definition: qgspointcloudlayer3drenderer.h:250
QgsPointCloud3DSymbol
3D symbol that draws point cloud geometries as 3D objects.
Definition: qgspointcloud3dsymbol.h:38
QgsAABB::yMin
float yMin
Definition: qgsaabb.h:82
qgs3dsymbolregistry.h
QgsPointCloudDataBounds::yMax
qint32 yMax() const
Returns y max.
QgsPointCloud3DSymbol::renderAsTriangles
bool renderAsTriangles() const
Returns whether points are triangulated to render solid surface.
Definition: qgspointcloud3dsymbol.cpp:40
qgsapplication.h
qgseventtracing.h
IndexedPointCloudNode
Represents a indexed point cloud node in octree.
Definition: qgspointcloudindex.h:57
qgspointcloud3dsymbol.h
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsPointCloudDataBounds::xMin
qint32 xMin() const
Returns x min.
qgspointcloudlayer.h
QgsClassificationPointCloud3DSymbol::getFilteredOutCategories
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
Definition: qgspointcloud3dsymbol.cpp:544
QgsAABB::yCenter
float yCenter() const
Returns center in Y axis.
Definition: qgsaabb.h:52
QgsCoordinateTransform::setBallparkTransformsAreAppropriate
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Definition: qgscoordinatetransform.cpp:939
QgsVector3D::z
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:66
qgs3dutils.h
QgsPointCloudIndex::hasNode
virtual bool hasNode(const IndexedPointCloudNode &n) const
Returns whether the octree contain given node.
Definition: qgspointcloudindex.cpp:160
qgspointcloudlayerchunkloader_p.h
QgsAABB::xMin
float xMin
Definition: qgsaabb.h:81
QgsAABB::zCenter
float zCenter() const
Returns center in Z axis.
Definition: qgsaabb.h:54
QgsAABB
Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:33
QgsPointCloudDataBounds::zMin
qint32 zMin() const
Returns z min.
Qgs3DMapSettings
Definition of the world.
Definition: qgs3dmapsettings.h:57
Qgs3DMapSettings::origin
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
Definition: qgs3dmapsettings.h:89
qgspointcloudindex.h
qgspoint3dsymbol.h
QgsPointCloudLayer3DRenderer::showBoundingBoxes
bool showBoundingBoxes() const
Returns whether bounding boxes will be visible when rendering the point cloud.
Definition: qgspointcloudlayer3drenderer.cpp:210
QgsPointCloudDataBounds
Represents packaged data bounds.
Definition: qgspointcloudindex.h:118
QgsAABB::yMax
float yMax
Definition: qgsaabb.h:85
QgsAABB::xCenter
float xCenter() const
Returns center in X axis.
Definition: qgsaabb.h:50
QgsPointCloudIndex::setAttributes
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
Definition: qgspointcloudindex.cpp:242
QgsPointCloudIndex
Represents a indexed point clouds data in octree.
Definition: qgspointcloudindex.h:163
qgsphongmaterialsettings.h
qgslogger.h
QgsCoordinateTransform::transform
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:272
QgsAABB::zMin
float zMin
Definition: qgsaabb.h:83
qgspointcloudrequest.h
QgsAABB::xMax
float xMax
Definition: qgsaabb.h:84
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsVector3D::x
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:62
QgsClassificationPointCloud3DSymbol
3D symbol that draws point cloud geometries as 3D objects using classification of the dataset
Definition: qgspointcloud3dsymbol.h:474
qgspointcloud3dsymbol_p.h
qgspointcloudlayer3drenderer.h