QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
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"
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 
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 
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 
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 
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 
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 
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 
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 
165 QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
166 {
167 }
168 
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 
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 
258 QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
259  : QgsPointCloud3DSymbolHandler()
260 {
261 
262 }
263 
264 bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
265 {
266  Q_UNUSED( context )
267  return true;
268 }
269 
270 void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
271 {
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 
323 void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
324 {
325  makeEntity( parent, context, outNormal, false );
326 }
327 
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 
333 QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
334  : QgsPointCloud3DSymbolHandler()
335 {
336 
337 }
338 
339 bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
340 {
341  Q_UNUSED( context )
342  return true;
343 }
344 
345 void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
346 {
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;
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 
456 void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
457 {
458  makeEntity( parent, context, outNormal, false );
459 }
460 
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 
466 QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
467  : QgsPointCloud3DSymbolHandler()
468 {
469 
470 }
471 
472 bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
473 {
474  Q_UNUSED( context )
475  return true;
476 }
477 
478 void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
479 {
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 
596 void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
597 {
598  makeEntity( parent, context, outNormal, false );
599 }
600 
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 
606 QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
607  : QgsPointCloud3DSymbolHandler()
608 {
609 
610 }
611 
612 bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
613 {
614  Q_UNUSED( context )
615  return true;
616 }
617 
618 void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
619 {
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;
633  int attributeOffset = 0;
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 
729 void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
730 {
731  makeEntity( parent, context, outNormal, false );
732 }
733 
734 
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 
Represents a indexed point cloud node in octree.
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 SIP_THROW(QgsCsException)
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.
Definition: qgsexception.h:66
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.
QgsFeedback * feedback() const
Returns the feedback object used to cancel rendering and check if rendering was canceled.
double zValueScale() const
Returns any constant scaling factor which must be applied to z values taken from the point cloud inde...
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.
QgsPointCloud3DSymbol * symbol() const
Returns the symbol used for rendering the point cloud.
double zValueFixedOffset() const
Returns any constant offset which must be applied to z values taken from the point cloud index.
virtual unsigned int byteStride()=0
Returns the byte stride for the geometries used to for the vertex buffer.
virtual void fillMaterial(Qt3DRender::QMaterial *material)=0SIP_SKIP
Used to fill material object with necessary QParameters (and consequently opengl uniforms)
float pointSize() const
Returns the point size of the point cloud.
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.
QgsPointCloudBlock * block()
Returns the requested block.
void finished()
Emitted when the request processing has finished.
Base class for storing raw data from point cloud nodes.
QgsVector3D scale() const
Returns the custom scale of the block.
const char * data() const
Returns raw pointer to data.
int pointCount() const
Returns number of points that are stored in the block.
QgsVector3D offset() const
Returns the custom offset of the block.
Represents a indexed point clouds data in octree.
virtual QgsPointCloudBlock * nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
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...
virtual QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a node data block.
Point cloud data request.
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.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
#define QgsDebugMsg(str)
Definition: qgslogger.h:38