QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
Loading...
Searching...
No Matches
qgspointcloud3dsymbol_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloud3dsymbol_p.cpp
3 ------------------------------
4 Date : December 2020
5 Copyright : (C) 2020 by Nedjima Belgacem
6 Email : belgacem dot nedjima 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
19
23#include "qgspointcloudindex.h"
25#include "qgsfeedback.h"
26#include "qgsaabb.h"
27
28#include <Qt3DCore/QEntity>
29#include <Qt3DRender/QGeometryRenderer>
30#include <Qt3DRender/QParameter>
31#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
32#include <Qt3DRender/QAttribute>
33#include <Qt3DRender/QBuffer>
34#include <Qt3DRender/QGeometry>
35
36typedef Qt3DRender::QAttribute Qt3DQAttribute;
37typedef Qt3DRender::QBuffer Qt3DQBuffer;
38typedef Qt3DRender::QGeometry Qt3DQGeometry;
39#else
40#include <Qt3DCore/QAttribute>
41#include <Qt3DCore/QBuffer>
42#include <Qt3DCore/QGeometry>
43
44typedef Qt3DCore::QAttribute Qt3DQAttribute;
45typedef Qt3DCore::QBuffer Qt3DQBuffer;
46typedef Qt3DCore::QGeometry Qt3DQGeometry;
47#endif
48#include <Qt3DRender/QTechnique>
49#include <Qt3DRender/QShaderProgram>
50#include <Qt3DRender/QGraphicsApiFilter>
51#include <Qt3DRender/QEffect>
52#include <QPointSize>
53#include <QUrl>
54
55#include <delaunator.hpp>
56
57// pick a point that we'll use as origin for coordinates for this node's points
58static QgsVector3D originFromNodeBounds( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, const QgsPointCloudBlock *block )
59{
60 const QgsVector3D blockScale = block->scale();
61 const QgsVector3D blockOffset = block->offset();
62
63 QgsPointCloudDataBounds bounds = pc->nodeBounds( n );
64 double nodeOriginX = bounds.xMin() * blockScale.x() + blockOffset.x();
65 double nodeOriginY = bounds.yMin() * blockScale.y() + blockOffset.y();
66 double nodeOriginZ = ( bounds.zMin() * blockScale.z() + blockOffset.z() ) * context.zValueScale() + context.zValueFixedOffset();
67 try
68 {
69 context.coordinateTransform().transformInPlace( nodeOriginX, nodeOriginY, nodeOriginZ );
70 }
71 catch ( QgsCsException & )
72 {
73 QgsDebugError( QStringLiteral( "Error transforming node origin point" ) );
74 }
75 return QgsVector3D( nodeOriginX, nodeOriginY, nodeOriginZ );
76}
77
78
79QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
80 : Qt3DQGeometry( parent )
81 , mPositionAttribute( new Qt3DQAttribute( this ) )
82 , mParameterAttribute( new Qt3DQAttribute( this ) )
83 , mPointSizeAttribute( new Qt3DQAttribute( this ) )
84 , mColorAttribute( new Qt3DQAttribute( this ) )
85 , mTriangleIndexAttribute( new Qt3DQAttribute( this ) )
86 , mNormalsAttribute( new Qt3DQAttribute( this ) )
87 , mVertexBuffer( new Qt3DQBuffer( this ) )
88 , mByteStride( byteStride )
89{
90 if ( !data.triangles.isEmpty() )
91 {
92 mTriangleBuffer = new Qt3DQBuffer( this );
93 mTriangleIndexAttribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
94 mTriangleIndexAttribute->setBuffer( mTriangleBuffer );
95 mTriangleIndexAttribute->setVertexBaseType( Qt3DQAttribute::UnsignedInt );
96 mTriangleBuffer->setData( data.triangles );
97 mTriangleIndexAttribute->setCount( data.triangles.size() / sizeof( quint32 ) );
98 addAttribute( mTriangleIndexAttribute );
99 }
100
101 if ( !data.normals.isEmpty() )
102 {
103 mNormalsBuffer = new Qt3DQBuffer( this );
104 mNormalsAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
105 mNormalsAttribute->setVertexBaseType( Qt3DQAttribute::Float );
106 mNormalsAttribute->setVertexSize( 3 );
107 mNormalsAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
108 mNormalsAttribute->setBuffer( mNormalsBuffer );
109 mNormalsBuffer->setData( data.normals );
110 mNormalsAttribute->setCount( data.normals.size() / ( 3 * sizeof( float ) ) );
111 addAttribute( mNormalsAttribute );
112 }
113}
114
115QgsSingleColorPointCloud3DGeometry::QgsSingleColorPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
116 : QgsPointCloud3DGeometry( parent, data, byteStride )
117{
118 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
119 mPositionAttribute->setBuffer( mVertexBuffer );
120 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
121 mPositionAttribute->setVertexSize( 3 );
122 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
123 mPositionAttribute->setByteOffset( 0 );
124 mPositionAttribute->setByteStride( mByteStride );
125 addAttribute( mPositionAttribute );
126 makeVertexBuffer( data );
127}
128
129void QgsSingleColorPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
130{
131 QByteArray vertexBufferData;
132 vertexBufferData.resize( data.positions.size() * mByteStride );
133 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
134 int idx = 0;
135 for ( int i = 0; i < data.positions.size(); ++i )
136 {
137 rawVertexArray[idx++] = data.positions.at( i ).x();
138 rawVertexArray[idx++] = data.positions.at( i ).y();
139 rawVertexArray[idx++] = data.positions.at( i ).z();
140 }
141
142 mVertexCount = data.positions.size();
143 mVertexBuffer->setData( vertexBufferData );
144}
145
146QgsColorRampPointCloud3DGeometry::QgsColorRampPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
147 : QgsPointCloud3DGeometry( parent, data, byteStride )
148{
149 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
150 mPositionAttribute->setBuffer( mVertexBuffer );
151 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
152 mPositionAttribute->setVertexSize( 3 );
153 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
154 mPositionAttribute->setByteOffset( 0 );
155 mPositionAttribute->setByteStride( mByteStride );
156 addAttribute( mPositionAttribute );
157 mParameterAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
158 mParameterAttribute->setBuffer( mVertexBuffer );
159 mParameterAttribute->setVertexBaseType( Qt3DQAttribute::Float );
160 mParameterAttribute->setVertexSize( 1 );
161 mParameterAttribute->setName( "vertexParameter" );
162 mParameterAttribute->setByteOffset( 12 );
163 mParameterAttribute->setByteStride( mByteStride );
164 addAttribute( mParameterAttribute );
165 makeVertexBuffer( data );
166}
167
168void QgsColorRampPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
169{
170 QByteArray vertexBufferData;
171 vertexBufferData.resize( data.positions.size() * mByteStride );
172 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
173 int idx = 0;
174 Q_ASSERT( data.positions.size() == data.parameter.size() );
175 for ( int i = 0; i < data.positions.size(); ++i )
176 {
177 rawVertexArray[idx++] = data.positions.at( i ).x();
178 rawVertexArray[idx++] = data.positions.at( i ).y();
179 rawVertexArray[idx++] = data.positions.at( i ).z();
180 rawVertexArray[idx++] = data.parameter.at( i );
181 }
182
183 mVertexCount = data.positions.size();
184 mVertexBuffer->setData( vertexBufferData );
185}
186
187QgsRGBPointCloud3DGeometry::QgsRGBPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
188 : QgsPointCloud3DGeometry( parent, data, byteStride )
189{
190 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
191 mPositionAttribute->setBuffer( mVertexBuffer );
192 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
193 mPositionAttribute->setVertexSize( 3 );
194 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
195 mPositionAttribute->setByteOffset( 0 );
196 mPositionAttribute->setByteStride( mByteStride );
197 addAttribute( mPositionAttribute );
198 mColorAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
199 mColorAttribute->setBuffer( mVertexBuffer );
200 mColorAttribute->setVertexBaseType( Qt3DQAttribute::Float );
201 mColorAttribute->setVertexSize( 3 );
202 mColorAttribute->setName( QStringLiteral( "vertexColor" ) );
203 mColorAttribute->setByteOffset( 12 );
204 mColorAttribute->setByteStride( mByteStride );
205 addAttribute( mColorAttribute );
206 makeVertexBuffer( data );
207}
208
209void QgsRGBPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
210{
211 QByteArray vertexBufferData;
212 vertexBufferData.resize( data.positions.size() * mByteStride );
213 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
214 int idx = 0;
215 Q_ASSERT( data.positions.size() == data.colors.size() );
216 for ( int i = 0; i < data.positions.size(); ++i )
217 {
218 rawVertexArray[idx++] = data.positions.at( i ).x();
219 rawVertexArray[idx++] = data.positions.at( i ).y();
220 rawVertexArray[idx++] = data.positions.at( i ).z();
221 rawVertexArray[idx++] = data.colors.at( i ).x();
222 rawVertexArray[idx++] = data.colors.at( i ).y();
223 rawVertexArray[idx++] = data.colors.at( i ).z();
224 }
225 mVertexCount = data.positions.size();
226 mVertexBuffer->setData( vertexBufferData );
227}
228
229QgsClassificationPointCloud3DGeometry::QgsClassificationPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
230 : QgsPointCloud3DGeometry( parent, data, byteStride )
231{
232 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
233 mPositionAttribute->setBuffer( mVertexBuffer );
234 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
235 mPositionAttribute->setVertexSize( 3 );
236 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
237 mPositionAttribute->setByteOffset( 0 );
238 mPositionAttribute->setByteStride( mByteStride );
239 addAttribute( mPositionAttribute );
240 mParameterAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
241 mParameterAttribute->setBuffer( mVertexBuffer );
242 mParameterAttribute->setVertexBaseType( Qt3DQAttribute::Float );
243 mParameterAttribute->setVertexSize( 1 );
244 mParameterAttribute->setName( "vertexParameter" );
245 mParameterAttribute->setByteOffset( 12 );
246 mParameterAttribute->setByteStride( mByteStride );
247 addAttribute( mParameterAttribute );
248 mPointSizeAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
249 mPointSizeAttribute->setBuffer( mVertexBuffer );
250 mPointSizeAttribute->setVertexBaseType( Qt3DQAttribute::Float );
251 mPointSizeAttribute->setVertexSize( 1 );
252 mPointSizeAttribute->setName( "vertexSize" );
253 mPointSizeAttribute->setByteOffset( 16 );
254 mPointSizeAttribute->setByteStride( mByteStride );
255 addAttribute( mPointSizeAttribute );
256 makeVertexBuffer( data );
257}
258
259void QgsClassificationPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
260{
261 QByteArray vertexBufferData;
262 vertexBufferData.resize( data.positions.size() * mByteStride );
263 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
264 int idx = 0;
265 Q_ASSERT( data.positions.size() == data.parameter.size() );
266 Q_ASSERT( data.positions.size() == data.pointSizes.size() );
267 for ( int i = 0; i < data.positions.size(); ++i )
268 {
269 rawVertexArray[idx++] = data.positions.at( i ).x();
270 rawVertexArray[idx++] = data.positions.at( i ).y();
271 rawVertexArray[idx++] = data.positions.at( i ).z();
272 rawVertexArray[idx++] = data.parameter.at( i );
273 rawVertexArray[idx++] = data.pointSizes.at( i );
274 }
275
276 mVertexCount = data.positions.size();
277 mVertexBuffer->setData( vertexBufferData );
278}
279
280QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
281{
282}
283
284
285void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context, const QgsPointCloud3DSymbolHandler::PointData &out, bool selected )
286{
287 Q_UNUSED( selected )
288
289 if ( out.positions.empty() )
290 return;
291
292 // Geometry
293 Qt3DQGeometry *geom = makeGeometry( parent, out, context.symbol()->byteStride() );
294 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
295 if ( context.symbol()->renderAsTriangles() && ! out.triangles.isEmpty() )
296 {
297 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
298 gr->setVertexCount( out.triangles.size() / sizeof( quint32 ) );
299 }
300 else
301 {
302 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
303 gr->setVertexCount( out.positions.count() );
304 }
305 gr->setGeometry( geom );
306
307 // Transform: chunks are using coordinates relative to chunk origin, with X,Y,Z axes being the same
308 // as map coordinates, so we need to rotate and translate entities to get them into world coordinates
309 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
310 QVector3D nodeTranslation = ( out.positionsOrigin - context.origin() ).toVector3D();
311 tr->setRotation( QQuaternion::fromAxisAndAngle( QVector3D( 1, 0, 0 ), -90 ) ); // flip map (x,y,z) to world (x,z,-y)
312 tr->setTranslation( QVector3D( nodeTranslation.x(), nodeTranslation.z(), -nodeTranslation.y() ) );
313
314 // Material
315 QgsMaterial *mat = new QgsMaterial;
316 if ( context.symbol() )
317 context.symbol()->fillMaterial( mat );
318
319 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( mat );
320 shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.vert" ) ) ) );
321 shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.frag" ) ) ) );
322
323 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( mat );
324 renderPass->setShaderProgram( shaderProgram );
325
326 if ( out.triangles.isEmpty() )
327 {
328 Qt3DRender::QPointSize *pointSize = new Qt3DRender::QPointSize( renderPass );
329 pointSize->setSizeMode( Qt3DRender::QPointSize::Programmable ); // supported since OpenGL 3.2
330 renderPass->addRenderState( pointSize );
331 }
332
333 // without this filter the default forward renderer would not render this
334 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
335 filterKey->setName( QStringLiteral( "renderingStyle" ) );
336 filterKey->setValue( "forward" );
337
338 Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
339 technique->addRenderPass( renderPass );
340 technique->addFilterKey( filterKey );
341 technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
342 technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
343 technique->graphicsApiFilter()->setMajorVersion( 3 );
344 technique->graphicsApiFilter()->setMinorVersion( 1 );
345 technique->addParameter( new Qt3DRender::QParameter( "triangulate", !out.triangles.isEmpty() ) );
346
347 Qt3DRender::QEffect *eff = new Qt3DRender::QEffect;
348 eff->addTechnique( technique );
349 mat->setEffect( eff );
350
351 // All together
352 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
353 entity->addComponent( gr );
354 entity->addComponent( tr );
355 entity->addComponent( mat );
356 entity->setParent( parent );
357 // cppcheck wrongly believes entity will leak
358 // cppcheck-suppress memleak
359}
360
361
362std::vector<double> QgsPointCloud3DSymbolHandler::getVertices( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
363{
364
365 bool hasColorData = !outNormal.colors.empty();
366 bool hasParameterData = !outNormal.parameter.empty();
367 bool hasPointSizeData = !outNormal.pointSizes.empty();
368
369 // first, get the points of the concerned node
370 std::vector<double> vertices( outNormal.positions.size() * 2 );
371 size_t idx = 0;
372 for ( int i = 0; i < outNormal.positions.size(); ++i )
373 {
374 vertices[idx++] = outNormal.positions.at( i ).x();
375 vertices[idx++] = -outNormal.positions.at( i ).y(); // flipping y to have correctly oriented triangles from delaunator
376 }
377
378 // next, we also need all points of all parents nodes to make the triangulation (also external points)
379 IndexedPointCloudNode parentNode = n.parentNode();
380
381 double span = pc->span();
382 //factor to take account of the density of the point to calculate extension of the bounding box
383 // with a usual value span = 128, bounding box is extended by 12.5 % on each side.
384 double extraBoxFactor = 16 / span;
385 double extraX = extraBoxFactor * bbox.xExtent();
386 double extraY = extraBoxFactor * bbox.yExtent();
387
388 // We keep all points in vertical direction to avoid odd triangulation if points are isolated on top
389 const QgsAABB extendedBBox( bbox.xMin - extraX, bbox.yMin - extraY, -std::numeric_limits<float>::max(), bbox.xMax + extraX, bbox.yMax + extraY, std::numeric_limits<float>::max() );
390
391 PointData filteredExtraPointData;
392 while ( parentNode.d() >= 0 )
393 {
394 PointData outputParent;
395 processNode( pc, parentNode, context, &outputParent );
396
397 // the "main" chunk and each parent chunks have their origins
398 QVector3D originDifference = ( outputParent.positionsOrigin - outNormal.positionsOrigin ).toVector3D();
399
400 for ( int i = 0; i < outputParent.positions.count(); ++i )
401 {
402 const QVector3D pos = outputParent.positions.at( i ) + originDifference;
403 if ( extendedBBox.intersects( pos.x(), pos.y(), pos.z() ) )
404 {
405 filteredExtraPointData.positions.append( pos );
406 vertices.push_back( pos.x() );
407 vertices.push_back( -pos.y() ); // flipping y to have correctly oriented triangles from delaunator
408
409 if ( hasColorData )
410 filteredExtraPointData.colors.append( outputParent.colors.at( i ) );
411 if ( hasParameterData )
412 filteredExtraPointData.parameter.append( outputParent.parameter.at( i ) );
413 if ( hasPointSizeData )
414 filteredExtraPointData.pointSizes.append( outputParent.pointSizes.at( i ) );
415 }
416 }
417
418 parentNode = parentNode.parentNode();
419 }
420
421 outNormal.positions.append( filteredExtraPointData.positions );
422 outNormal.colors.append( filteredExtraPointData.colors );
423 outNormal.parameter.append( filteredExtraPointData.parameter );
424 outNormal.pointSizes.append( filteredExtraPointData.pointSizes );
425
426 return vertices;
427}
428
429void QgsPointCloud3DSymbolHandler::calculateNormals( const std::vector<size_t> &triangles )
430{
431 QVector<QVector3D> normals( outNormal.positions.count(), QVector3D( 0.0, 0.0, 0.0 ) );
432 for ( size_t i = 0; i < triangles.size(); i += 3 )
433 {
434 QVector<QVector3D> triangleVertices( 3 );
435 for ( size_t j = 0; j < 3; ++j )
436 {
437 size_t vertIndex = triangles.at( i + j );
438 triangleVertices[j] = outNormal.positions.at( vertIndex );
439 }
440 //calculate normals
441 for ( size_t j = 0; j < 3; ++j )
442 normals[triangles.at( i + j )] += QVector3D::crossProduct(
443 triangleVertices.at( 1 ) - triangleVertices.at( 0 ),
444 triangleVertices.at( 2 ) - triangleVertices.at( 0 ) );
445 }
446
447 // Build now normals array
448 outNormal.normals.resize( ( outNormal.positions.count() ) * sizeof( float ) * 3 );
449 float *normPtr = reinterpret_cast<float *>( outNormal.normals.data() );
450 for ( int i = 0; i < normals.size(); ++i )
451 {
452 QVector3D normal = normals.at( i );
453 normal = normal.normalized();
454
455 *normPtr++ = normal.x();
456 *normPtr++ = normal.y();
457 *normPtr++ = normal.z();
458 }
459}
460
461void QgsPointCloud3DSymbolHandler::filterTriangles( const std::vector<size_t> &triangleIndexes, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
462{
463 outNormal.triangles.resize( triangleIndexes.size() * sizeof( quint32 ) );
464 quint32 *indexPtr = reinterpret_cast<quint32 *>( outNormal.triangles.data() );
465 size_t effective = 0;
466
467 bool horizontalFilter = context.symbol()->horizontalTriangleFilter();
468 bool verticalFilter = context.symbol()->verticalTriangleFilter();
469 float horizontalThreshold = context.symbol()->horizontalFilterThreshold();
470 float verticalThreshold = context.symbol()->verticalFilterThreshold();
471
472 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
473 {
474 bool atLeastOneInBox = false;
475 bool horizontalSkip = false;
476 bool verticalSkip = false;
477 for ( size_t j = 0; j < 3; j++ )
478 {
479 QVector3D pos = outNormal.positions.at( triangleIndexes.at( i + j ) );
480 atLeastOneInBox |= bbox.intersects( pos.x(), pos.y(), pos.z() );
481
482 if ( verticalFilter || horizontalFilter )
483 {
484 const QVector3D pos2 = outNormal.positions.at( triangleIndexes.at( i + ( j + 1 ) % 3 ) );
485
486 if ( verticalFilter )
487 verticalSkip |= std::fabs( pos.z() - pos2.z() ) > verticalThreshold;
488
489 if ( horizontalFilter && ! verticalSkip )
490 {
491 // filter only in the horizontal plan, it is a 2.5D triangulation.
492 horizontalSkip |= sqrt( std::pow( pos.x() - pos2.x(), 2 ) +
493 std::pow( pos.y() - pos2.y(), 2 ) ) > horizontalThreshold;
494 }
495
496 if ( horizontalSkip || verticalSkip )
497 break;
498 }
499 }
500 if ( atLeastOneInBox && !horizontalSkip && !verticalSkip )
501 {
502 for ( size_t j = 0; j < 3; j++ )
503 {
504 size_t vertIndex = triangleIndexes.at( i + j );
505 *indexPtr++ = quint32( vertIndex );
506 }
507 effective++;
508 }
509 }
510
511 if ( effective != 0 )
512 {
513 outNormal.triangles.resize( effective * 3 * sizeof( quint32 ) );
514 }
515 else
516 {
517 outNormal.triangles.clear();
518 outNormal.normals.clear();
519 }
520}
521
522void QgsPointCloud3DSymbolHandler::triangulate( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
523{
524 if ( outNormal.positions.isEmpty() )
525 return;
526
527 // The bbox we get is in world coordinates, we need to transform it to map coordinates
528 // (flip axes and add scene origin vector), but relative to the chunk's origin (subtract it)
529 // because that's the coordinate system used within the chunk
530 QgsBox3D boxRelativeToChunkOrigin(
531 bbox.xMin + context.origin().x() - outNormal.positionsOrigin.x(),
532 -bbox.zMax + context.origin().y() - outNormal.positionsOrigin.y(),
533 bbox.yMin + context.origin().z() - outNormal.positionsOrigin.z(),
534 bbox.xMax + context.origin().x() - outNormal.positionsOrigin.x(),
535 -bbox.zMin + context.origin().y() - outNormal.positionsOrigin.y(),
536 bbox.yMax + context.origin().z() - outNormal.positionsOrigin.z() );
537 QgsAABB aabbRelativeToChunkOrigin(
538 boxRelativeToChunkOrigin.xMinimum(), boxRelativeToChunkOrigin.yMinimum(), boxRelativeToChunkOrigin.zMinimum(),
539 boxRelativeToChunkOrigin.xMaximum(), boxRelativeToChunkOrigin.yMaximum(), boxRelativeToChunkOrigin.zMaximum() );
540
541 // Triangulation happens here
542 std::unique_ptr<delaunator::Delaunator> triangulation;
543 try
544 {
545 std::vector<double> vertices = getVertices( pc, n, context, aabbRelativeToChunkOrigin );
546 triangulation.reset( new delaunator::Delaunator( vertices ) );
547 }
548 catch ( std::exception &e )
549 {
550 // something went wrong, better to retrieve initial state
551 QgsDebugMsgLevel( QStringLiteral( "Error with triangulation" ), 4 );
552 outNormal = PointData();
553 processNode( pc, n, context );
554 return;
555 }
556
557 const std::vector<size_t> &triangleIndexes = triangulation->triangles;
558
559 calculateNormals( triangleIndexes );
560 filterTriangles( triangleIndexes, context, aabbRelativeToChunkOrigin );
561}
562
563std::unique_ptr<QgsPointCloudBlock> QgsPointCloud3DSymbolHandler::pointCloudBlock( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloud3DRenderContext &context )
564{
565 std::unique_ptr<QgsPointCloudBlock> block;
567 {
568 block = pc->nodeData( n, request );
569 }
571 {
572 if ( pc->nodePointCount( n ) < 1 )
573 return block;
574
575 bool loopAborted = false;
576 QEventLoop loop;
577 QgsPointCloudBlockRequest *req = pc->asyncNodeData( n, request );
578 QObject::connect( req, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
579 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, [ & ]()
580 {
581 loopAborted = true;
582 loop.quit();
583 } );
584 loop.exec();
585
586 if ( !loopAborted )
587 block = req->takeBlock();
588 }
589 return block;
590}
591
592//
593
594QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
595 : QgsPointCloud3DSymbolHandler()
596{
597
598}
599
600bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
601{
602 Q_UNUSED( context )
603 return true;
604}
605
606void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, PointData *output )
607{
609 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
610 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
611 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
612
613 QgsPointCloudRequest request;
614 request.setAttributes( attributes );
615 request.setFilterRect( context.layerExtent() );
616 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
617 if ( !block )
618 return;
619
620 const char *ptr = block->data();
621 const int count = block->pointCount();
622 const std::size_t recordSize = block->attributes().pointRecordSize();
623 const QgsVector3D blockScale = block->scale();
624 const QgsVector3D blockOffset = block->offset();
625 const double zValueScale = context.zValueScale();
626 const double zValueOffset = context.zValueFixedOffset();
627 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
628 bool alreadyPrintedDebug = false;
629
630 if ( !output )
631 output = &outNormal;
632
633 output->positionsOrigin = originFromNodeBounds( pc, n, context, block.get() );
634
635 for ( int i = 0; i < count; ++i )
636 {
637 if ( context.isCanceled() )
638 break;
639
640 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
641 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
642 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
643
644 double x = blockOffset.x() + blockScale.x() * ix;
645 double y = blockOffset.y() + blockScale.y() * iy;
646 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
647 try
648 {
649 coordinateTransform.transformInPlace( x, y, z );
650 }
651 catch ( QgsCsException &e )
652 {
653 if ( !alreadyPrintedDebug )
654 {
655 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
656 alreadyPrintedDebug = true;
657 }
658 }
659 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
660 output->positions.push_back( point.toVector3D() );
661 }
662}
663
664void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
665{
666 makeEntity( parent, context, outNormal, false );
667}
668
669Qt3DQGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
670{
671 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
672}
673
674QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
675 : QgsPointCloud3DSymbolHandler()
676{
677
678}
679
680bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
681{
682 Q_UNUSED( context )
683 return true;
684}
685
686void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, PointData *output )
687{
689 const int xOffset = 0;
690 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
691 const int yOffset = attributes.pointRecordSize();
692 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
693 const int zOffset = attributes.pointRecordSize();
694 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
695
696 QString attributeName;
697 bool attrIsX = false;
698 bool attrIsY = false;
699 bool attrIsZ = false;
701 int attributeOffset = 0;
702 const double zValueScale = context.zValueScale();
703 const double zValueOffset = context.zValueFixedOffset();
704 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
705 bool alreadyPrintedDebug = false;
706
707 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
708 if ( symbol )
709 {
710 int offset = 0;
711 const QgsPointCloudAttributeCollection collection = context.attributes();
712
713 if ( symbol->attribute() == QLatin1String( "X" ) )
714 {
715 attrIsX = true;
716 }
717 else if ( symbol->attribute() == QLatin1String( "Y" ) )
718 {
719 attrIsY = true;
720 }
721 else if ( symbol->attribute() == QLatin1String( "Z" ) )
722 {
723 attrIsZ = true;
724 }
725 else
726 {
727 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
728 if ( attr )
729 {
730 attributeType = attr->type();
731 attributeName = attr->name();
732 attributeOffset = attributes.pointRecordSize();
733 attributes.push_back( *attr );
734 }
735 }
736 }
737
738 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
739 return;
740
741 QgsPointCloudRequest request;
742 request.setAttributes( attributes );
743 request.setFilterRect( context.layerExtent() );
744 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
745 if ( !block )
746 return;
747
748 const char *ptr = block->data();
749 const int count = block->pointCount();
750 const std::size_t recordSize = block->attributes().pointRecordSize();
751
752 const QgsVector3D blockScale = block->scale();
753 const QgsVector3D blockOffset = block->offset();
754
755 if ( !output )
756 output = &outNormal;
757
758 output->positionsOrigin = originFromNodeBounds( pc, n, context, block.get() );
759
760 for ( int i = 0; i < count; ++i )
761 {
762 if ( context.isCanceled() )
763 break;
764
765 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
766 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
767 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
768
769 double x = blockOffset.x() + blockScale.x() * ix;
770 double y = blockOffset.y() + blockScale.y() * iy;
771 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
772 try
773 {
774 coordinateTransform.transformInPlace( x, y, z );
775 }
776 catch ( QgsCsException & )
777 {
778 if ( !alreadyPrintedDebug )
779 {
780 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
781 alreadyPrintedDebug = true;
782 }
783 }
784 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
785 output->positions.push_back( point.toVector3D() );
786
787 if ( attrIsX )
788 output->parameter.push_back( x );
789 else if ( attrIsY )
790 output->parameter.push_back( y );
791 else if ( attrIsZ )
792 output->parameter.push_back( z );
793 else
794 {
795 float iParam = 0.0f;
796 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
797 output->parameter.push_back( iParam );
798 }
799 }
800}
801
802void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
803{
804 makeEntity( parent, context, outNormal, false );
805}
806
807Qt3DQGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
808{
809 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
810}
811
812QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
813 : QgsPointCloud3DSymbolHandler()
814{
815
816}
817
818bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
819{
820 Q_UNUSED( context )
821 return true;
822}
823
824void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, PointData *output )
825{
827 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
828 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
829 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
830
831 QgsRgbPointCloud3DSymbol *symbol = dynamic_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
832
833 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
834 int attrOffset = 0 ;
835
836 const int redOffset = attributes.pointRecordSize();
837 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
838 attributes.push_back( *colorAttribute );
839 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
840
841 const int greenOffset = attributes.pointRecordSize();
842 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
843 attributes.push_back( *colorAttribute );
844 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
845
846 const int blueOffset = attributes.pointRecordSize();
847 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
848 attributes.push_back( *colorAttribute );
849 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
850
851 QgsPointCloudRequest request;
852 request.setAttributes( attributes );
853 request.setFilterRect( context.layerExtent() );
854 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
855 if ( !block )
856 return;
857
858 const char *ptr = block->data();
859 const int count = block->pointCount();
860 const std::size_t recordSize = block->attributes().pointRecordSize();
861
862 const QgsVector3D blockScale = block->scale();
863 const QgsVector3D blockOffset = block->offset();
864 const double zValueScale = context.zValueScale();
865 const double zValueOffset = context.zValueFixedOffset();
866 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
867 bool alreadyPrintedDebug = false;
868
869 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
870 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
871 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
872
873 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
874 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
875 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
876
877 if ( !output )
878 output = &outNormal;
879
880 output->positionsOrigin = originFromNodeBounds( pc, n, context, block.get() );
881
882 int ir = 0;
883 int ig = 0;
884 int ib = 0;
885 for ( int i = 0; i < count; ++i )
886 {
887 if ( context.isCanceled() )
888 break;
889
890 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
891 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
892 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
893 double x = blockOffset.x() + blockScale.x() * ix;
894 double y = blockOffset.y() + blockScale.y() * iy;
895 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
896 try
897 {
898 coordinateTransform.transformInPlace( x, y, z );
899 }
900 catch ( QgsCsException & )
901 {
902 if ( !alreadyPrintedDebug )
903 {
904 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
905 alreadyPrintedDebug = true;
906 }
907 }
908 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
909
910 QVector3D color( 0.0f, 0.0f, 0.0f );
911
912 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
913 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
914 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
915
916 //skip if red, green or blue not in displayable range
917 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
918 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
919 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
920 {
921 continue;
922 }
923
924 //stretch color values
925 if ( useRedContrastEnhancement )
926 {
927 ir = redContrastEnhancement->enhanceContrast( ir );
928 }
929 if ( useGreenContrastEnhancement )
930 {
931 ig = greenContrastEnhancement->enhanceContrast( ig );
932 }
933 if ( useBlueContrastEnhancement )
934 {
935 ib = blueContrastEnhancement->enhanceContrast( ib );
936 }
937
938 color.setX( ir / 255.0f );
939 color.setY( ig / 255.0f );
940 color.setZ( ib / 255.0f );
941
942 output->positions.push_back( point.toVector3D() );
943 output->colors.push_back( color );
944 }
945}
946
947void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
948{
949 makeEntity( parent, context, outNormal, false );
950}
951
952Qt3DQGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
953{
954 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
955}
956
957QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
958 : QgsPointCloud3DSymbolHandler()
959{
960
961}
962
963bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
964{
965 Q_UNUSED( context )
966 return true;
967}
968
969void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, PointData *output )
970{
972 const int xOffset = 0;
973 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
974 const int yOffset = attributes.pointRecordSize();
975 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
976 const int zOffset = attributes.pointRecordSize();
977 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
978
979 QString attributeName;
980 bool attrIsX = false;
981 bool attrIsY = false;
982 bool attrIsZ = false;
984 int attributeOffset = 0;
986 if ( symbol )
987 {
988 int offset = 0;
989 const QgsPointCloudAttributeCollection collection = context.attributes();
990
991 if ( symbol->attribute() == QLatin1String( "X" ) )
992 {
993 attrIsX = true;
994 }
995 else if ( symbol->attribute() == QLatin1String( "Y" ) )
996 {
997 attrIsY = true;
998 }
999 else if ( symbol->attribute() == QLatin1String( "Z" ) )
1000 {
1001 attrIsZ = true;
1002 }
1003 else
1004 {
1005 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
1006 if ( attr )
1007 {
1008 attributeType = attr->type();
1009 attributeName = attr->name();
1010 attributeOffset = attributes.pointRecordSize();
1011 attributes.push_back( *attr );
1012 }
1013 }
1014 }
1015
1016 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
1017 return;
1018
1019 QgsPointCloudRequest request;
1020 request.setAttributes( attributes );
1021 request.setFilterRect( context.layerExtent() );
1022 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
1023 if ( !block )
1024 return;
1025
1026 const char *ptr = block->data();
1027 const int count = block->pointCount();
1028 const std::size_t recordSize = block->attributes().pointRecordSize();
1029
1030 const QgsVector3D blockScale = block->scale();
1031 const QgsVector3D blockOffset = block->offset();
1032 const double zValueScale = context.zValueScale();
1033 const double zValueOffset = context.zValueFixedOffset();
1034 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
1035 bool alreadyPrintedDebug = false;
1036
1037 QList<QgsPointCloudCategory> categoriesList = symbol->categoriesList();
1038 QVector<int> categoriesValues;
1039 QHash<int, double> categoriesPointSizes;
1040 for ( QgsPointCloudCategory &c : categoriesList )
1041 {
1042 categoriesValues.push_back( c.value() );
1043 categoriesPointSizes.insert( c.value(), c.pointSize() > 0 ? c.pointSize() :
1044 context.symbol() ? context.symbol()->pointSize() : 1.0 );
1045 }
1046
1047 if ( !output )
1048 output = &outNormal;
1049
1050 output->positionsOrigin = originFromNodeBounds( pc, n, context, block.get() );
1051
1052 const QSet<int> filteredOutValues = context.getFilteredOutValues();
1053 for ( int i = 0; i < count; ++i )
1054 {
1055 if ( context.isCanceled() )
1056 break;
1057
1058 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
1059 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
1060 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
1061
1062 double x = blockOffset.x() + blockScale.x() * ix;
1063 double y = blockOffset.y() + blockScale.y() * iy;
1064 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
1065 try
1066 {
1067 coordinateTransform.transformInPlace( x, y, z );
1068 }
1069 catch ( QgsCsException & )
1070 {
1071 if ( !alreadyPrintedDebug )
1072 {
1073 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
1074 alreadyPrintedDebug = true;
1075 }
1076 }
1077 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
1078 float iParam = 0.0f;
1079 if ( attrIsX )
1080 iParam = x;
1081 else if ( attrIsY )
1082 iParam = y;
1083 else if ( attrIsZ )
1084 iParam = z;
1085 else
1086 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
1087
1088 if ( filteredOutValues.contains( ( int ) iParam ) ||
1089 ! categoriesValues.contains( ( int ) iParam ) )
1090 continue;
1091 output->positions.push_back( point.toVector3D() );
1092
1093 // find iParam actual index in the categories list
1094 float iParam2 = categoriesValues.indexOf( ( int )iParam ) + 1;
1095 output->parameter.push_back( iParam2 );
1096 output->pointSizes.push_back( categoriesPointSizes.value( ( int ) iParam ) );
1097 }
1098}
1099
1100void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
1101{
1102 makeEntity( parent, context, outNormal, false );
1103}
1104
1105Qt3DQGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
1106{
1107 return new QgsClassificationPointCloud3DGeometry( parent, data, byteStride );
1108}
1109
Represents a indexed point cloud node in octree.
int d() const
Returns d.
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
float yMax
Definition qgsaabb.h:90
float yExtent() const
Returns box width in Y axis.
Definition qgsaabb.h:44
float xMax
Definition qgsaabb.h:89
float xExtent() const
Returns box width in X axis.
Definition qgsaabb.h:42
float xMin
Definition qgsaabb.h:86
float zMax
Definition qgsaabb.h:91
bool intersects(const QgsAABB &other) const
Determines whether the box intersects some other axis aligned box.
Definition qgsaabb.cpp:35
float yMin
Definition qgsaabb.h:87
float zMin
Definition qgsaabb.h:88
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
QgsPointCloudCategoryList categoriesList() const
Returns the list of categories of the classification.
QString attribute() const
Returns the attribute used to select the color of the point cloud.
QString attribute() const
Returns the attribute used to select the color of the point cloud.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i....
int enhanceContrast(double value)
Applies the contrast enhancement to a value.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
Class for doing transforms between two map coordinate systems.
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
Custom exception class for Coordinate Reference System related exceptions.
void canceled()
Internal routines can connect to this signal if they use event loop.
Encapsulates the render context for a 3D point cloud rendering operation.
void getAttribute(const char *data, std::size_t offset, QgsPointCloudAttribute::DataType type, T &value) const
Retrieves the attribute value from data at the specified offset, where type indicates the original da...
QSet< int > getFilteredOutValues() const
Returns a set containing the filtered out values.
QgsPointCloud3DSymbol * symbol() const
Returns the symbol used for rendering the point cloud.
double zValueScale() const
Returns any constant scaling factor which must be applied to z values taken from the point cloud inde...
QgsFeedback * feedback() const
Returns the feedback object used to cancel rendering and check if rendering was canceled.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes associated with the rendered block.
bool isCanceled() const
Returns true if the rendering is canceled.
QgsCoordinateTransform coordinateTransform() const
Returns the coordinate transform used to transform points from layer CRS to the map CRS.
QgsRectangle layerExtent() const
Returns the 3D scene's extent in layer crs.
double zValueFixedOffset() const
Returns any constant offset which must be applied to z values taken from the point cloud index.
bool verticalTriangleFilter() const
Returns whether triangles are filtered by vertical height for rendering.
float verticalFilterThreshold() const
Returns the threshold vertical height value for filtering triangles.
virtual void fillMaterial(QgsMaterial *material)=0SIP_SKIP
Used to fill material object with necessary QParameters (and consequently opengl uniforms)
virtual unsigned int byteStride()=0
Returns the byte stride for the geometries used to for the vertex buffer.
float horizontalFilterThreshold() const
Returns the threshold horizontal size value for filtering triangles.
bool renderAsTriangles() const
Returns whether points are triangulated to render solid surface.
bool horizontalTriangleFilter() const
Returns whether triangles are filtered by horizontal size for rendering.
Collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
Attribute for point cloud data pair of name and size in bytes.
DataType
Systems of unit measurement.
QString name() const
Returns name of the attribute.
DataType type() const
Returns the data type.
Base class for handling loading QgsPointCloudBlock asynchronously.
void finished()
Emitted when the request processing has finished.
std::unique_ptr< QgsPointCloudBlock > takeBlock()
Returns the requested block.
Base class for storing raw data from point cloud nodes.
QgsVector3D scale() const
Returns the custom scale of the block.
QgsVector3D offset() const
Returns the custom offset of the block.
Represents an individual category (class) from a QgsPointCloudClassifiedRenderer.
Represents packaged data bounds.
qint64 xMin() const
Returns x min.
qint64 zMin() const
Returns z min.
qint64 yMin() const
Returns y min.
Represents a indexed point clouds data in octree.
int span() const
Returns the number of points in one direction in a single node.
virtual qint64 nodePointCount(const IndexedPointCloudNode &n) const
Returns the number of points of a given node n.
virtual QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a node data block.
@ Remote
Remote means it's loaded through a protocol like HTTP.
@ Local
Local means the source is a local file on the machine.
virtual AccessType accessType() const =0
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
QgsPointCloudDataBounds nodeBounds(const IndexedPointCloudNode &node) const
Returns bounds of particular node.
virtual std::unique_ptr< QgsPointCloudBlock > nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
Point cloud data request.
void setFilterRect(QgsRectangle extent)
Sets the rectangle from which points will be taken, in point cloud's crs.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
QString blueAttribute() const
Returns the attribute to use for the blue channel.
QString greenAttribute() const
Returns the attribute to use for the green channel.
QgsContrastEnhancement * blueContrastEnhancement()
Returns the contrast enhancement to use for the blue channel.
QString redAttribute() const
Returns the attribute to use for the red channel.
QgsContrastEnhancement * greenContrastEnhancement()
Returns the contrast enhancement to use for the green channel.
QgsContrastEnhancement * redContrastEnhancement()
Returns the contrast enhancement to use for the red channel.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:52
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38