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