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
16 #include "qgspointcloud3dsymbol_p.h"
17
18 ///@cond PRIVATE
19
20 #include "qgspointcloud3dsymbol.h"
21 #include "qgsapplication.h"
22 #include "qgs3dsymbolregistry.h"
23 #include "qgspointcloudattribute.h"
24 #include "qgspointcloudrequest.h"
25 #include "qgscolorramptexture.h"
26 #include "qgs3dmapsettings.h"
27 #include "qgspointcloudindex.h"
28 #include "qgspointcloudblockrequest.h"
29 #include "qgsfeedback.h"
30
31 #include <Qt3DRender/QGeometryRenderer>
32 #include <Qt3DRender/QAttribute>
33 #include <Qt3DRender/QTechnique>
34 #include <Qt3DRender/QShaderProgram>
35 #include <Qt3DRender/QGraphicsApiFilter>
36 #include <Qt3DRender/QEffect>
37 #include <QPointSize>
38 #include <QUrl>
39
QgsPointCloud3DGeometry(Qt3DCore::QNode * parent,unsigned int byteStride)40 QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, unsigned int byteStride )
41 : Qt3DRender::QGeometry( parent )
42 , mPositionAttribute( new Qt3DRender::QAttribute( this ) )
43 , mParameterAttribute( new Qt3DRender::QAttribute( this ) )
44 , mColorAttribute( new Qt3DRender::QAttribute( this ) )
45 , mVertexBuffer( new Qt3DRender::QBuffer( this ) )
46 , mByteStride( byteStride )
47 {
48
49 }
50
QgsSingleColorPointCloud3DGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)51 QgsSingleColorPointCloud3DGeometry::QgsSingleColorPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
52 : QgsPointCloud3DGeometry( parent, byteStride )
53 {
54 mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
55 mPositionAttribute->setBuffer( mVertexBuffer );
56 mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
57 mPositionAttribute->setVertexSize( 3 );
58 mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
59 mPositionAttribute->setByteOffset( 0 );
60 mPositionAttribute->setByteStride( mByteStride );
61 addAttribute( mPositionAttribute );
62 makeVertexBuffer( data );
63 }
64
makeVertexBuffer(const QgsPointCloud3DSymbolHandler::PointData & data)65 void QgsSingleColorPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
66 {
67 QByteArray vertexBufferData;
68 vertexBufferData.resize( data.positions.size() * mByteStride );
69 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
70 int idx = 0;
71 for ( int i = 0; i < data.positions.size(); ++i )
72 {
73 rawVertexArray[idx++] = data.positions.at( i ).x();
74 rawVertexArray[idx++] = data.positions.at( i ).y();
75 rawVertexArray[idx++] = data.positions.at( i ).z();
76 }
77
78 mVertexCount = data.positions.size();
79 mVertexBuffer->setData( vertexBufferData );
80 }
81
QgsColorRampPointCloud3DGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)82 QgsColorRampPointCloud3DGeometry::QgsColorRampPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
83 : QgsPointCloud3DGeometry( parent, byteStride )
84 {
85 mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
86 mPositionAttribute->setBuffer( mVertexBuffer );
87 mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
88 mPositionAttribute->setVertexSize( 3 );
89 mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
90 mPositionAttribute->setByteOffset( 0 );
91 mPositionAttribute->setByteStride( mByteStride );
92 addAttribute( mPositionAttribute );
93 mParameterAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
94 mParameterAttribute->setBuffer( mVertexBuffer );
95 mParameterAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
96 mParameterAttribute->setVertexSize( 1 );
97 mParameterAttribute->setName( "vertexParameter" );
98 mParameterAttribute->setByteOffset( 12 );
99 mParameterAttribute->setByteStride( mByteStride );
100 addAttribute( mParameterAttribute );
101 makeVertexBuffer( data );
102 }
103
makeVertexBuffer(const QgsPointCloud3DSymbolHandler::PointData & data)104 void QgsColorRampPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
105 {
106 QByteArray vertexBufferData;
107 vertexBufferData.resize( data.positions.size() * mByteStride );
108 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
109 int idx = 0;
110 Q_ASSERT( data.positions.size() == data.parameter.size() );
111 for ( int i = 0; i < data.positions.size(); ++i )
112 {
113 rawVertexArray[idx++] = data.positions.at( i ).x();
114 rawVertexArray[idx++] = data.positions.at( i ).y();
115 rawVertexArray[idx++] = data.positions.at( i ).z();
116 rawVertexArray[idx++] = data.parameter.at( i );
117 }
118
119 mVertexCount = data.positions.size();
120 mVertexBuffer->setData( vertexBufferData );
121 }
122
QgsRGBPointCloud3DGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)123 QgsRGBPointCloud3DGeometry::QgsRGBPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
124 : QgsPointCloud3DGeometry( parent, byteStride )
125 {
126 mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
127 mPositionAttribute->setBuffer( mVertexBuffer );
128 mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
129 mPositionAttribute->setVertexSize( 3 );
130 mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
131 mPositionAttribute->setByteOffset( 0 );
132 mPositionAttribute->setByteStride( mByteStride );
133 addAttribute( mPositionAttribute );
134 mColorAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
135 mColorAttribute->setBuffer( mVertexBuffer );
136 mColorAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
137 mColorAttribute->setVertexSize( 3 );
138 mColorAttribute->setName( QStringLiteral( "vertexColor" ) );
139 mColorAttribute->setByteOffset( 12 );
140 mColorAttribute->setByteStride( mByteStride );
141 addAttribute( mColorAttribute );
142 makeVertexBuffer( data );
143 }
144
makeVertexBuffer(const QgsPointCloud3DSymbolHandler::PointData & data)145 void QgsRGBPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
146 {
147 QByteArray vertexBufferData;
148 vertexBufferData.resize( data.positions.size() * mByteStride );
149 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
150 int idx = 0;
151 Q_ASSERT( data.positions.size() == data.colors.size() );
152 for ( int i = 0; i < data.positions.size(); ++i )
153 {
154 rawVertexArray[idx++] = data.positions.at( i ).x();
155 rawVertexArray[idx++] = data.positions.at( i ).y();
156 rawVertexArray[idx++] = data.positions.at( i ).z();
157 rawVertexArray[idx++] = data.colors.at( i ).x();
158 rawVertexArray[idx++] = data.colors.at( i ).y();
159 rawVertexArray[idx++] = data.colors.at( i ).z();
160 }
161 mVertexCount = data.positions.size();
162 mVertexBuffer->setData( vertexBufferData );
163 }
164
QgsPointCloud3DSymbolHandler()165 QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
166 {
167 }
168
makeEntity(Qt3DCore::QEntity * parent,const QgsPointCloud3DRenderContext & context,QgsPointCloud3DSymbolHandler::PointData & out,bool selected)169 void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context, QgsPointCloud3DSymbolHandler::PointData &out, bool selected )
170 {
171 Q_UNUSED( selected )
172
173 if ( out.positions.empty() )
174 return;
175
176 // Geometry
177 Qt3DRender::QGeometry *geom = makeGeometry( parent, out, context.symbol()->byteStride() );
178 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
179 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
180 gr->setVertexCount( out.positions.count() );
181 gr->setGeometry( geom );
182
183 // Transform
184 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
185
186 // Material
187 Qt3DRender::QMaterial *mat = new Qt3DRender::QMaterial;
188 if ( context.symbol() )
189 context.symbol()->fillMaterial( mat );
190
191 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( mat );
192 shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.vert" ) ) ) );
193 shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.frag" ) ) ) );
194
195 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( mat );
196 renderPass->setShaderProgram( shaderProgram );
197
198 Qt3DRender::QPointSize *pointSize = new Qt3DRender::QPointSize( renderPass );
199 pointSize->setSizeMode( Qt3DRender::QPointSize::Programmable ); // supported since OpenGL 3.2
200 pointSize->setValue( context.symbol() ? context.symbol()->pointSize() : 1.0f );
201 renderPass->addRenderState( pointSize );
202
203 // without this filter the default forward renderer would not render this
204 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
205 filterKey->setName( QStringLiteral( "renderingStyle" ) );
206 filterKey->setValue( "forward" );
207
208 Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
209 technique->addRenderPass( renderPass );
210 technique->addFilterKey( filterKey );
211 technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
212 technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
213 technique->graphicsApiFilter()->setMajorVersion( 3 );
214 technique->graphicsApiFilter()->setMinorVersion( 1 );
215
216 Qt3DRender::QEffect *eff = new Qt3DRender::QEffect;
217 eff->addTechnique( technique );
218 mat->setEffect( eff );
219
220 // All together
221 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
222 entity->addComponent( gr );
223 entity->addComponent( tr );
224 entity->addComponent( mat );
225 entity->setParent( parent );
226 // cppcheck wrongly believes entity will leak
227 // cppcheck-suppress memleak
228 }
229
pointCloudBlock(QgsPointCloudIndex * pc,const IndexedPointCloudNode & n,const QgsPointCloudRequest & request,const QgsPointCloud3DRenderContext & context)230 QgsPointCloudBlock *QgsPointCloud3DSymbolHandler::pointCloudBlock( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloud3DRenderContext &context )
231 {
232 QgsPointCloudBlock *block = nullptr;
233 if ( pc->accessType() == QgsPointCloudIndex::AccessType::Local )
234 {
235 block = pc->nodeData( n, request );
236 }
237 else if ( pc->accessType() == QgsPointCloudIndex::AccessType::Remote )
238 {
239 bool loopAborted = false;
240 QEventLoop loop;
241 QgsPointCloudBlockRequest *req = pc->asyncNodeData( n, request );
242 QObject::connect( req, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
243 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, [ & ]()
244 {
245 loopAborted = true;
246 loop.quit();
247 } );
248 loop.exec();
249
250 if ( !loopAborted )
251 block = req->block();
252 }
253 return block;
254 }
255
256 //
257
QgsSingleColorPointCloud3DSymbolHandler()258 QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
259 : QgsPointCloud3DSymbolHandler()
260 {
261
262 }
263
prepare(const QgsPointCloud3DRenderContext & context)264 bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
265 {
266 Q_UNUSED( context )
267 return true;
268 }
269
processNode(QgsPointCloudIndex * pc,const IndexedPointCloudNode & n,const QgsPointCloud3DRenderContext & context)270 void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
271 {
272 QgsPointCloudAttributeCollection attributes;
273 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
274 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
275 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
276
277 QgsPointCloudRequest request;
278 request.setAttributes( attributes );
279 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
280 if ( !block )
281 return;
282
283 const char *ptr = block->data();
284 const int count = block->pointCount();
285 const std::size_t recordSize = attributes.pointRecordSize();
286 const QgsVector3D blockScale = block->scale();
287 const QgsVector3D blockOffset = block->offset();
288 const double zValueScale = context.zValueScale();
289 const double zValueOffset = context.zValueFixedOffset();
290 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
291 bool alreadyPrintedDebug = false;
292
293 for ( int i = 0; i < count; ++i )
294 {
295 if ( context.isCanceled() )
296 break;
297
298 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
299 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
300 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
301
302 double x = blockOffset.x() + blockScale.x() * ix;
303 double y = blockOffset.y() + blockScale.y() * iy;
304 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
305 try
306 {
307 coordinateTransform.transformInPlace( x, y, z );
308 }
309 catch ( QgsCsException &e )
310 {
311 if ( !alreadyPrintedDebug )
312 {
313 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
314 alreadyPrintedDebug = true;
315 }
316 }
317 const QgsVector3D point( x, y, z );
318 const QgsVector3D p = context.map().mapToWorldCoordinates( QgsVector3D( x, y, z ) );
319 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
320 }
321 }
322
finalize(Qt3DCore::QEntity * parent,const QgsPointCloud3DRenderContext & context)323 void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
324 {
325 makeEntity( parent, context, outNormal, false );
326 }
327
makeGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)328 Qt3DRender::QGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
329 {
330 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
331 }
332
QgsColorRampPointCloud3DSymbolHandler()333 QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
334 : QgsPointCloud3DSymbolHandler()
335 {
336
337 }
338
prepare(const QgsPointCloud3DRenderContext & context)339 bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
340 {
341 Q_UNUSED( context )
342 return true;
343 }
344
processNode(QgsPointCloudIndex * pc,const IndexedPointCloudNode & n,const QgsPointCloud3DRenderContext & context)345 void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
346 {
347 QgsPointCloudAttributeCollection attributes;
348 const int xOffset = 0;
349 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
350 const int yOffset = attributes.pointRecordSize();
351 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
352 const int zOffset = attributes.pointRecordSize();
353 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
354
355 QString attributeName;
356 bool attrIsX = false;
357 bool attrIsY = false;
358 bool attrIsZ = false;
359 QgsPointCloudAttribute::DataType attributeType = QgsPointCloudAttribute::Float;
360 int attributeOffset = 0;
361 const double zValueScale = context.zValueScale();
362 const double zValueOffset = context.zValueFixedOffset();
363 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
364 bool alreadyPrintedDebug = false;
365
366 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
367 if ( symbol )
368 {
369 int offset = 0;
370 const QgsPointCloudAttributeCollection collection = context.attributes();
371
372 if ( symbol->attribute() == QLatin1String( "X" ) )
373 {
374 attrIsX = true;
375 }
376 else if ( symbol->attribute() == QLatin1String( "Y" ) )
377 {
378 attrIsY = true;
379 }
380 else if ( symbol->attribute() == QLatin1String( "Z" ) )
381 {
382 attrIsZ = true;
383 }
384 else
385 {
386 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
387 if ( attr )
388 {
389 attributeType = attr->type();
390 attributeName = attr->name();
391 attributeOffset = attributes.pointRecordSize();
392 attributes.push_back( *attr );
393 }
394 }
395 }
396
397 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
398 return;
399
400 QgsPointCloudRequest request;
401 request.setAttributes( attributes );
402 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
403 if ( !block )
404 return;
405
406 const char *ptr = block->data();
407 const int count = block->pointCount();
408 const std::size_t recordSize = attributes.pointRecordSize();
409
410 const QgsVector3D blockScale = block->scale();
411 const QgsVector3D blockOffset = block->offset();
412
413 for ( int i = 0; i < count; ++i )
414 {
415 if ( context.isCanceled() )
416 break;
417
418 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
419 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
420 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
421
422 double x = blockOffset.x() + blockScale.x() * ix;
423 double y = blockOffset.y() + blockScale.y() * iy;
424 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
425 try
426 {
427 coordinateTransform.transformInPlace( x, y, z );
428 }
429 catch ( QgsCsException & )
430 {
431 if ( !alreadyPrintedDebug )
432 {
433 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
434 alreadyPrintedDebug = true;
435 }
436 }
437 QgsVector3D point( x, y, z );
438 point = context.map().mapToWorldCoordinates( point );
439 outNormal.positions.push_back( QVector3D( point.x(), point.y(), point.z() ) );
440
441 if ( attrIsX )
442 outNormal.parameter.push_back( x );
443 else if ( attrIsY )
444 outNormal.parameter.push_back( y );
445 else if ( attrIsZ )
446 outNormal.parameter.push_back( z );
447 else
448 {
449 float iParam = 0.0f;
450 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
451 outNormal.parameter.push_back( iParam );
452 }
453 }
454 }
455
finalize(Qt3DCore::QEntity * parent,const QgsPointCloud3DRenderContext & context)456 void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
457 {
458 makeEntity( parent, context, outNormal, false );
459 }
460
makeGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)461 Qt3DRender::QGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
462 {
463 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
464 }
465
QgsRGBPointCloud3DSymbolHandler()466 QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
467 : QgsPointCloud3DSymbolHandler()
468 {
469
470 }
471
prepare(const QgsPointCloud3DRenderContext & context)472 bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
473 {
474 Q_UNUSED( context )
475 return true;
476 }
477
processNode(QgsPointCloudIndex * pc,const IndexedPointCloudNode & n,const QgsPointCloud3DRenderContext & context)478 void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
479 {
480 QgsPointCloudAttributeCollection attributes;
481 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
482 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
483 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
484
485 QgsRgbPointCloud3DSymbol *symbol = dynamic_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
486
487 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
488 int attrOffset = 0 ;
489
490 const int redOffset = attributes.pointRecordSize();
491 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
492 attributes.push_back( *colorAttribute );
493 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
494
495 const int greenOffset = attributes.pointRecordSize();
496 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
497 attributes.push_back( *colorAttribute );
498 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
499
500 const int blueOffset = attributes.pointRecordSize();
501 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
502 attributes.push_back( *colorAttribute );
503 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
504
505 QgsPointCloudRequest request;
506 request.setAttributes( attributes );
507 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
508 if ( !block )
509 return;
510
511 const char *ptr = block->data();
512 const int count = block->pointCount();
513 const std::size_t recordSize = attributes.pointRecordSize();
514
515 const QgsVector3D blockScale = block->scale();
516 const QgsVector3D blockOffset = block->offset();
517 const double zValueScale = context.zValueScale();
518 const double zValueOffset = context.zValueFixedOffset();
519 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
520 bool alreadyPrintedDebug = false;
521
522 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
523 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
524 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
525
526 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
527 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
528 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
529
530 int ir = 0;
531 int ig = 0;
532 int ib = 0;
533 for ( int i = 0; i < count; ++i )
534 {
535 if ( context.isCanceled() )
536 break;
537
538 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
539 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
540 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
541 double x = blockOffset.x() + blockScale.x() * ix;
542 double y = blockOffset.y() + blockScale.y() * iy;
543 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
544 try
545 {
546 coordinateTransform.transformInPlace( x, y, z );
547 }
548 catch ( QgsCsException & )
549 {
550 if ( !alreadyPrintedDebug )
551 {
552 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
553 alreadyPrintedDebug = true;
554 }
555 }
556 const QgsVector3D point( x, y, z );
557 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
558
559 QVector3D color( 0.0f, 0.0f, 0.0f );
560
561 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
562 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
563 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
564
565 //skip if red, green or blue not in displayable range
566 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
567 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
568 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
569 {
570 continue;
571 }
572
573 //stretch color values
574 if ( useRedContrastEnhancement )
575 {
576 ir = redContrastEnhancement->enhanceContrast( ir );
577 }
578 if ( useGreenContrastEnhancement )
579 {
580 ig = greenContrastEnhancement->enhanceContrast( ig );
581 }
582 if ( useBlueContrastEnhancement )
583 {
584 ib = blueContrastEnhancement->enhanceContrast( ib );
585 }
586
587 color.setX( ir / 255.0f );
588 color.setY( ig / 255.0f );
589 color.setZ( ib / 255.0f );
590
591 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
592 outNormal.colors.push_back( color );
593 }
594 }
595
finalize(Qt3DCore::QEntity * parent,const QgsPointCloud3DRenderContext & context)596 void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
597 {
598 makeEntity( parent, context, outNormal, false );
599 }
600
makeGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)601 Qt3DRender::QGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
602 {
603 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
604 }
605
QgsClassificationPointCloud3DSymbolHandler()606 QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
607 : QgsPointCloud3DSymbolHandler()
608 {
609
610 }
611
prepare(const QgsPointCloud3DRenderContext & context)612 bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
613 {
614 Q_UNUSED( context )
615 return true;
616 }
617
processNode(QgsPointCloudIndex * pc,const IndexedPointCloudNode & n,const QgsPointCloud3DRenderContext & context)618 void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
619 {
620 QgsPointCloudAttributeCollection attributes;
621 const int xOffset = 0;
622 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
623 const int yOffset = attributes.pointRecordSize();
624 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
625 const int zOffset = attributes.pointRecordSize();
626 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
627
628 QString attributeName;
629 bool attrIsX = false;
630 bool attrIsY = false;
631 bool attrIsZ = false;
632 QgsPointCloudAttribute::DataType attributeType = QgsPointCloudAttribute::Float;
633 int attributeOffset = 0;
634 QgsClassificationPointCloud3DSymbol *symbol = dynamic_cast<QgsClassificationPointCloud3DSymbol *>( context.symbol() );
635 if ( symbol )
636 {
637 int offset = 0;
638 const QgsPointCloudAttributeCollection collection = context.attributes();
639
640 if ( symbol->attribute() == QLatin1String( "X" ) )
641 {
642 attrIsX = true;
643 }
644 else if ( symbol->attribute() == QLatin1String( "Y" ) )
645 {
646 attrIsY = true;
647 }
648 else if ( symbol->attribute() == QLatin1String( "Z" ) )
649 {
650 attrIsZ = true;
651 }
652 else
653 {
654 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
655 if ( attr )
656 {
657 attributeType = attr->type();
658 attributeName = attr->name();
659 attributeOffset = attributes.pointRecordSize();
660 attributes.push_back( *attr );
661 }
662 }
663 }
664
665 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
666 return;
667
668 QgsPointCloudRequest request;
669 request.setAttributes( attributes );
670 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
671 if ( !block )
672 return;
673
674 const char *ptr = block->data();
675 const int count = block->pointCount();
676 const std::size_t recordSize = attributes.pointRecordSize();
677
678 const QgsVector3D blockScale = block->scale();
679 const QgsVector3D blockOffset = block->offset();
680 const double zValueScale = context.zValueScale();
681 const double zValueOffset = context.zValueFixedOffset();
682 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
683 bool alreadyPrintedDebug = false;
684
685 const QSet<int> filteredOutValues = context.getFilteredOutValues();
686 for ( int i = 0; i < count; ++i )
687 {
688 if ( context.isCanceled() )
689 break;
690
691 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
692 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
693 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
694
695 double x = blockOffset.x() + blockScale.x() * ix;
696 double y = blockOffset.y() + blockScale.y() * iy;
697 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
698 try
699 {
700 coordinateTransform.transformInPlace( x, y, z );
701 }
702 catch ( QgsCsException & )
703 {
704 if ( !alreadyPrintedDebug )
705 {
706 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
707 alreadyPrintedDebug = true;
708 }
709 }
710 const QgsVector3D point( x, y, z );
711 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
712 float iParam = 0.0f;
713 if ( attrIsX )
714 iParam = x;
715 else if ( attrIsY )
716 iParam = y;
717 else if ( attrIsZ )
718 iParam = z;
719 else
720 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
721
722 if ( filteredOutValues.contains( ( int ) iParam ) )
723 continue;
724 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
725 outNormal.parameter.push_back( iParam );
726 }
727 }
728
finalize(Qt3DCore::QEntity * parent,const QgsPointCloud3DRenderContext & context)729 void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
730 {
731 makeEntity( parent, context, outNormal, false );
732 }
733
734
makeGeometry(Qt3DCore::QNode * parent,const QgsPointCloud3DSymbolHandler::PointData & data,unsigned int byteStride)735 Qt3DRender::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
736 {
737 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
738 }
739
740
741 /// @endcond
742