QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgspoint3dsymbol_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspoint3dsymbol_p.cpp
3  --------------------------------------
4  Date : July 2017
5  Copyright : (C) 2017 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgspoint3dsymbol_p.h"
17 
18 #include <Qt3DRender/QAttribute>
19 #include <Qt3DRender/QBuffer>
20 #include <Qt3DRender/QEffect>
21 #include <Qt3DRender/QGraphicsApiFilter>
22 #include <Qt3DRender/QParameter>
23 #include <Qt3DRender/QTechnique>
24 
25 #include <Qt3DExtras/QCylinderGeometry>
26 #include <Qt3DExtras/QConeGeometry>
27 #include <Qt3DExtras/QCuboidGeometry>
28 #include <Qt3DExtras/QPlaneGeometry>
29 #include <Qt3DExtras/QSphereGeometry>
30 #include <Qt3DExtras/QTorusGeometry>
31 #include <Qt3DExtras/QPhongMaterial>
32 #include <Qt3DRender/QSceneLoader>
33 #include <Qt3DRender/QPaintedTextureImage>
34 
35 #include <Qt3DRender/QMesh>
36 
37 #include <Qt3DExtras/QExtrudedTextGeometry>
38 
39 #include <QUrl>
40 #include <QVector3D>
41 
42 #include "qgspoint3dsymbol.h"
43 #include "qgs3dmapsettings.h"
44 
45 #include "qgsapplication.h"
46 #include "qgsvectorlayer.h"
47 #include "qgspoint.h"
48 #include "qgs3dutils.h"
49 #include "qgsbillboardgeometry.h"
51 #include "qgslogger.h"
52 #include "qgssourcecache.h"
53 #include "qgssymbol.h"
54 #include "qgssymbollayerutils.h"
55 #include "qgssymbollayer.h"
56 
58 
59 
60 //* INSTANCED RENDERING *//
61 
62 
63 class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler
64 {
65  public:
66  QgsInstancedPoint3DSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
67  : mSymbol( static_cast< QgsPoint3DSymbol *>( symbol->clone() ) )
68  , mSelectedIds( selectedIds ) {}
69 
70  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
71  void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
72  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
73 
74  private:
75 
76  static Qt3DRender::QMaterial *material( const QgsPoint3DSymbol *symbol );
77  static Qt3DRender::QGeometryRenderer *renderer( const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions );
78  static Qt3DRender::QGeometry *symbolGeometry( QgsPoint3DSymbol::Shape shape, const QVariantMap &shapeProperties );
79 
81  struct PointData
82  {
83  QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
84  };
85 
86  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
87 
88  // input specific for this class
89  std::unique_ptr< QgsPoint3DSymbol > mSymbol;
90  // inputs - generic
91  QgsFeatureIds mSelectedIds;
92 
93  // outputs
94  PointData outNormal;
95  PointData outSelected;
96 };
97 
98 
99 bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
100 {
101  Q_UNUSED( context )
102  Q_UNUSED( attributeNames )
103  return true;
104 }
105 
106 void QgsInstancedPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
107 {
108  PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
109 
110  if ( feature.geometry().isNull() )
111  return;
112 
113  Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
114 }
115 
116 void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
117 {
118  makeEntity( parent, context, outNormal, false );
119  makeEntity( parent, context, outSelected, true );
120 
121  updateZRangeFromPositions( outNormal.positions );
122  updateZRangeFromPositions( outSelected.positions );
123 
124  // the elevation offset is applied in the vertex shader so let's account for it as well
125  const float symbolHeight = mSymbol->transform().data()[13];
126  mZMin += symbolHeight;
127  mZMax += symbolHeight;
128 }
129 
130 void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
131 {
132  // build the default material
133  Qt3DRender::QMaterial *mat = material( mSymbol.get() );
134 
135  if ( selected )
136  {
137  // update the material with selection colors
138  for ( Qt3DRender::QParameter *param : mat->effect()->parameters() )
139  {
140  if ( param->name() == QLatin1String( "kd" ) ) // diffuse
141  param->setValue( context.map().selectionColor() );
142  else if ( param->name() == QLatin1String( "ka" ) ) // ambient
143  param->setValue( context.map().selectionColor().darker() );
144  }
145  }
146 
147  // build the entity
148  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
149  entity->addComponent( renderer( mSymbol.get(), out.positions ) );
150  entity->addComponent( mat );
151  entity->setParent( parent );
152 
153 // cppcheck wrongly believes entity will leak
154 // cppcheck-suppress memleak
155 }
156 
157 
158 
159 Qt3DRender::QMaterial *QgsInstancedPoint3DSymbolHandler::material( const QgsPoint3DSymbol *symbol )
160 {
161  Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
162  filterKey->setName( QStringLiteral( "renderingStyle" ) );
163  filterKey->setValue( "forward" );
164 
165  // the fragment shader implements a simplified version of phong shading that uses hardcoded light
166  // (instead of whatever light we have defined in the scene)
167  // TODO: use phong shading that respects lights from the scene
168  Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram;
169  shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/instanced.vert" ) ) ) );
170  shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/instanced.frag" ) ) ) );
171 
172  Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass;
173  renderPass->setShaderProgram( shaderProgram );
174 
175  Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
176  technique->addFilterKey( filterKey );
177  technique->addRenderPass( renderPass );
178  technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
179  technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
180  technique->graphicsApiFilter()->setMajorVersion( 3 );
181  technique->graphicsApiFilter()->setMinorVersion( 2 );
182 
183  const QMatrix4x4 transformMatrix = symbol->transform();
184  QMatrix3x3 normalMatrix = transformMatrix.normalMatrix(); // transponed inverse of 3x3 sub-matrix
185 
186  // QMatrix3x3 is not supported for passing to shaders, so we pass QMatrix4x4
187  float *n = normalMatrix.data();
188  const QMatrix4x4 normalMatrix4(
189  n[0], n[3], n[6], 0,
190  n[1], n[4], n[7], 0,
191  n[2], n[5], n[8], 0,
192  0, 0, 0, 0 );
193 
194  Qt3DRender::QParameter *paramInst = new Qt3DRender::QParameter;
195  paramInst->setName( QStringLiteral( "inst" ) );
196  paramInst->setValue( transformMatrix );
197 
198  Qt3DRender::QParameter *paramInstNormal = new Qt3DRender::QParameter;
199  paramInstNormal->setName( QStringLiteral( "instNormal" ) );
200  paramInstNormal->setValue( normalMatrix4 );
201 
202  Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
203  effect->addTechnique( technique );
204  effect->addParameter( paramInst );
205  effect->addParameter( paramInstNormal );
206 
207  symbol->material()->addParametersToEffect( effect );
208 
209  Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
210  material->setEffect( effect );
211 
212  return material;
213 }
214 
215 Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions )
216 {
217  const int count = positions.count();
218  const int byteCount = positions.count() * sizeof( QVector3D );
219  QByteArray ba;
220  ba.resize( byteCount );
221  memcpy( ba.data(), positions.constData(), byteCount );
222 
223  Qt3DRender::QBuffer *instanceBuffer = new Qt3DRender::QBuffer();
224  instanceBuffer->setData( ba );
225 
226  Qt3DRender::QAttribute *instanceDataAttribute = new Qt3DRender::QAttribute;
227  instanceDataAttribute->setName( QStringLiteral( "pos" ) );
228  instanceDataAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
229  instanceDataAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
230  instanceDataAttribute->setVertexSize( 3 );
231  instanceDataAttribute->setByteOffset( 0 );
232  instanceDataAttribute->setDivisor( 1 );
233  instanceDataAttribute->setBuffer( instanceBuffer );
234  instanceDataAttribute->setCount( count );
235  instanceDataAttribute->setByteStride( 3 * sizeof( float ) );
236 
237  Qt3DRender::QGeometry *geometry = symbolGeometry( symbol->shape(), symbol->shapeProperties() );
238  geometry->addAttribute( instanceDataAttribute );
239  geometry->setBoundingVolumePositionAttribute( instanceDataAttribute );
240 
241  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
242  renderer->setGeometry( geometry );
243  renderer->setInstanceCount( count );
244 
245  return renderer;
246 }
247 
248 Qt3DRender::QGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry( QgsPoint3DSymbol::Shape shape, const QVariantMap &shapeProperties )
249 {
250  switch ( shape )
251  {
253  {
254  const float radius = shapeProperties[QStringLiteral( "radius" )].toFloat();
255  const float length = shapeProperties[QStringLiteral( "length" )].toFloat();
256  Qt3DExtras::QCylinderGeometry *g = new Qt3DExtras::QCylinderGeometry;
257  //g->setRings(2); // how many vertices vertically
258  //g->setSlices(8); // how many vertices on circumference
259  g->setRadius( radius ? radius : 10 );
260  g->setLength( length ? length : 10 );
261  return g;
262  }
263 
265  {
266  const float radius = shapeProperties[QStringLiteral( "radius" )].toFloat();
267  Qt3DExtras::QSphereGeometry *g = new Qt3DExtras::QSphereGeometry;
268  g->setRadius( radius ? radius : 10 );
269  return g;
270  }
271 
273  {
274  const float length = shapeProperties[QStringLiteral( "length" )].toFloat();
275  const float bottomRadius = shapeProperties[QStringLiteral( "bottomRadius" )].toFloat();
276  const float topRadius = shapeProperties[QStringLiteral( "topRadius" )].toFloat();
277  Qt3DExtras::QConeGeometry *g = new Qt3DExtras::QConeGeometry;
278  g->setLength( length ? length : 10 );
279  g->setBottomRadius( bottomRadius );
280  g->setTopRadius( topRadius );
281  //g->setHasBottomEndcap(hasBottomEndcap);
282  //g->setHasTopEndcap(hasTopEndcap);
283  return g;
284  }
285 
287  {
288  const float size = shapeProperties[QStringLiteral( "size" )].toFloat();
289  Qt3DExtras::QCuboidGeometry *g = new Qt3DExtras::QCuboidGeometry;
290  g->setXExtent( size ? size : 10 );
291  g->setYExtent( size ? size : 10 );
292  g->setZExtent( size ? size : 10 );
293  return g;
294  }
295 
297  {
298  const float radius = shapeProperties[QStringLiteral( "radius" )].toFloat();
299  const float minorRadius = shapeProperties[QStringLiteral( "minorRadius" )].toFloat();
300  Qt3DExtras::QTorusGeometry *g = new Qt3DExtras::QTorusGeometry;
301  g->setRadius( radius ? radius : 10 );
302  g->setMinorRadius( minorRadius ? minorRadius : 5 );
303  return g;
304  }
305 
307  {
308  const float size = shapeProperties[QStringLiteral( "size" )].toFloat();
309  Qt3DExtras::QPlaneGeometry *g = new Qt3DExtras::QPlaneGeometry;
310  g->setWidth( size ? size : 10 );
311  g->setHeight( size ? size : 10 );
312  return g;
313  }
314 
316  {
317  const float depth = shapeProperties[QStringLiteral( "depth" )].toFloat();
318  const QString text = shapeProperties[QStringLiteral( "text" )].toString();
319  Qt3DExtras::QExtrudedTextGeometry *g = new Qt3DExtras::QExtrudedTextGeometry;
320  g->setDepth( depth ? depth : 1 );
321  g->setText( text );
322  return g;
323  }
324 
325  default:
326  Q_ASSERT( false );
327  return nullptr;
328  }
329 }
330 
331 //* 3D MODEL RENDERING *//
332 
333 
334 class QgsModelPoint3DSymbolHandler : public QgsFeature3DHandler
335 {
336  public:
337  QgsModelPoint3DSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
338  : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) )
339  , mSelectedIds( selectedIds ) {}
340 
341  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
342  void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
343  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
344 
345  private:
346 
347  static void addSceneEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent );
348  static void addMeshEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected );
349  static Qt3DCore::QTransform *transform( QVector3D position, const QgsPoint3DSymbol *symbol );
350 
352  struct PointData
353  {
354  QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
355  };
356 
357  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
358 
359  // input specific for this class
360  std::unique_ptr< QgsPoint3DSymbol > mSymbol;
361  // inputs - generic
362  QgsFeatureIds mSelectedIds;
363 
364  // outputs
365  PointData outNormal;
366  PointData outSelected;
367 };
368 
369 bool QgsModelPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
370 {
371  Q_UNUSED( context )
372  Q_UNUSED( attributeNames )
373  return true;
374 }
375 
376 void QgsModelPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
377 {
378  PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
379 
380  if ( feature.geometry().isNull() )
381  return;
382 
383  Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
384 }
385 
386 void QgsModelPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
387 {
388  makeEntity( parent, context, outNormal, false );
389  makeEntity( parent, context, outSelected, true );
390 
391  updateZRangeFromPositions( outNormal.positions );
392  updateZRangeFromPositions( outSelected.positions );
393 
394  // the elevation offset is applied separately in QTransform added to sub-entities
395  const float symbolHeight = mSymbol->transform().data()[13];
396  mZMin += symbolHeight;
397  mZMax += symbolHeight;
398 }
399 
400 void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
401 {
402  if ( selected )
403  {
404  addMeshEntities( context.map(), out.positions, mSymbol.get(), parent, true );
405  }
406  else
407  {
408  // "overwriteMaterial" is a legacy setting indicating that non-embedded material should be used
409  if ( mSymbol->shapeProperties()[QStringLiteral( "overwriteMaterial" )].toBool()
410  || ( mSymbol->material() && mSymbol->material()->type() != QLatin1String( "null" ) ) )
411  {
412  addMeshEntities( context.map(), out.positions, mSymbol.get(), parent, false );
413  }
414  else
415  {
416  addSceneEntities( context.map(), out.positions, mSymbol.get(), parent );
417  }
418  }
419 }
420 
421 
422 
423 void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent )
424 {
425  Q_UNUSED( map )
426  for ( const QVector3D &position : positions )
427  {
428  const QString source = QgsApplication::instance()->sourceCache()->localFilePath( symbol->shapeProperties()[QStringLiteral( "model" )].toString() );
429  // if the source is remote, the Qgs3DMapScene will take care of refreshing this 3D symbol when the source is fetched
430  if ( !source.isEmpty() )
431  {
432  // build the entity
433  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
434 
435  const QUrl url = QUrl::fromLocalFile( source );
436  Qt3DRender::QSceneLoader *modelLoader = new Qt3DRender::QSceneLoader;
437  modelLoader->setSource( url );
438 
439  entity->addComponent( modelLoader );
440  entity->addComponent( transform( position, symbol ) );
441  entity->setParent( parent );
442 
443 // cppcheck wrongly believes entity will leak
444 // cppcheck-suppress memleak
445  }
446  }
447 }
448 
449 void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected )
450 {
451  if ( positions.empty() )
452  return;
453 
454  // build the default material
455  QgsMaterialContext materialContext;
456  materialContext.setIsSelected( are_selected );
457  materialContext.setSelectionColor( map.selectionColor() );
458  Qt3DRender::QMaterial *mat = symbol->material()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
459 
460  // get nodes
461  for ( const QVector3D &position : positions )
462  {
463  const QString source = QgsApplication::instance()->sourceCache()->localFilePath( symbol->shapeProperties()[QStringLiteral( "model" )].toString() );
464  if ( !source.isEmpty() )
465  {
466  // build the entity
467  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
468 
469  const QUrl url = QUrl::fromLocalFile( source );
470  Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
471  mesh->setSource( url );
472 
473  entity->addComponent( mesh );
474  entity->addComponent( mat );
475  entity->addComponent( transform( position, symbol ) );
476  entity->setParent( parent );
477 
478 // cppcheck wrongly believes entity will leak
479 // cppcheck-suppress memleak
480  }
481  }
482 }
483 
484 Qt3DCore::QTransform *QgsModelPoint3DSymbolHandler::transform( QVector3D position, const QgsPoint3DSymbol *symbol )
485 {
486  Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
487  tr->setMatrix( symbol->transform() );
488  tr->setTranslation( position + tr->translation() );
489  return tr;
490 }
491 
492 // --------------
493 
494 //* BILLBOARD RENDERING *//
495 
496 class QgsPoint3DBillboardSymbolHandler : public QgsFeature3DHandler
497 {
498  public:
499  QgsPoint3DBillboardSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
500  : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) )
501  , mSelectedIds( selectedIds ) {}
502 
503  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
504  void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
505  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
506 
507  private:
508 
510  struct PointData
511  {
512  QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
513  };
514 
515  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
516 
517  // input specific for this class
518  std::unique_ptr< QgsPoint3DSymbol > mSymbol;
519  // inputs - generic
520  QgsFeatureIds mSelectedIds;
521 
522  // outputs
523  PointData outNormal;
524  PointData outSelected;
525 };
526 
527 bool QgsPoint3DBillboardSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
528 {
529  Q_UNUSED( context )
530  Q_UNUSED( attributeNames )
531  return true;
532 }
533 
534 void QgsPoint3DBillboardSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
535 {
536  PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
537 
538  if ( feature.geometry().isNull() )
539  return;
540 
541  Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
542 }
543 
544 void QgsPoint3DBillboardSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
545 {
546  makeEntity( parent, context, outNormal, false );
547  makeEntity( parent, context, outSelected, true );
548 
549  updateZRangeFromPositions( outNormal.positions );
550  updateZRangeFromPositions( outSelected.positions );
551 
552  // the elevation offset is applied externally through a QTransform of QEntity so let's account for it
553  const float billboardHeight = mSymbol->transform().data()[13];
554  mZMin += billboardHeight;
555  mZMax += billboardHeight;
556 }
557 
558 void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
559 {
560  // Billboard Geometry
561  QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
562  billboardGeometry->setPoints( out.positions );
563 
564  // Billboard Geometry Renderer
565  Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
566  billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
567  billboardGeometryRenderer->setGeometry( billboardGeometry );
568  billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
569 
570  // Billboard Material
571  QgsPoint3DBillboardMaterial *billboardMaterial = new QgsPoint3DBillboardMaterial();
572  QgsMarkerSymbol *symbol = mSymbol->billboardSymbol();
573 
574  if ( symbol )
575  {
576  billboardMaterial->setTexture2DFromSymbol( symbol, context.map(), selected );
577  }
578  else
579  {
580  billboardMaterial->useDefaultSymbol( context.map(), selected );
581  }
582 
583  // Billboard Transform
584  Qt3DCore::QTransform *billboardTransform = new Qt3DCore::QTransform();
585  billboardTransform->setMatrix( mSymbol->billboardTransform() );
586 
587  // Build the entity
588  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
589 
590  entity->addComponent( billboardMaterial );
591  entity->addComponent( billboardTransform );
592  entity->addComponent( billboardGeometryRenderer );
593  entity->setParent( parent );
594 
595 // cppcheck wrongly believes entity will leak
596 // cppcheck-suppress memleak
597 }
598 
599 
600 namespace Qgs3DSymbolImpl
601 {
602 
603  QgsFeature3DHandler *handlerForPoint3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
604  {
605  const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( symbol );
606  if ( !pointSymbol )
607  return nullptr;
608 
609  if ( pointSymbol->shape() == QgsPoint3DSymbol::Model )
610  return new QgsModelPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
611  // Add proper handler for billboard
612  else if ( pointSymbol->shape() == QgsPoint3DSymbol::Billboard )
613  return new QgsPoint3DBillboardSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
614  else
615  return new QgsInstancedPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
616  }
617 
618  Qt3DCore::QEntity *entityForPoint3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol )
619  {
620  QgsFeature3DHandler *handler = handlerForPoint3DSymbol( layer, &symbol );
621  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
622  delete handler;
623  return e;
624  }
625 }
626 
QColor selectionColor() const
Returns color used for selected features.
static void extractPointPositions(const QgsFeature &f, const Qgs3DMapSettings &map, Qgs3DTypes::AltitudeClamping altClamp, QVector< QVector3D > &positions)
Calculates (x,y,z) positions of (multi)point from the given feature.
Definition: qgs3dutils.cpp:350
virtual Qt3DRender::QMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const =0
Creates a new QMaterial object representing the material settings.
virtual void addParametersToEffect(Qt3DRender::QEffect *effect) const =0
Adds parameters from the material to a destination effect.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsSourceCache * sourceCache()
Returns the application's source cache, used for caching embedded and remote source strings as local ...
void setPoints(const QVector< QVector3D > &vertices)
Set the points for the billboard with vertices.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
A marker symbol type, for rendering Point and MultiPoint geometries.
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected state.
void setSelectionColor(const QColor &color)
Sets the color for representing materials in a selected state.
void useDefaultSymbol(const Qgs3DMapSettings &map, bool selected=false)
Set default symbol for the texture with map and selected parameter for rendering.
void setTexture2DFromSymbol(QgsMarkerSymbol *markerSymbol, const Qgs3DMapSettings &map, bool selected=false)
Set markerSymbol for the texture with map and selected parameter for rendering.
Shape
3D shape types supported by the symbol
@ ExtrudedText
Supported in Qt 5.9+.
QgsAbstractMaterialSettings * material() const
Returns material used for shading of the symbol.
QMatrix4x4 transform() const
Returns transform for individual objects represented by the symbol.
QgsMarkerSymbol * billboardSymbol() const
Returns a symbol for billboard.
Shape shape() const
Returns 3D shape for points.
QVariantMap shapeProperties() const
Returns a key-value dictionary of point shape properties.
QString localFilePath(const QString &path, bool blocking=false)
Returns a local file path reflecting the content of a specified source path.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
@ Triangles
Triangle based rendering (default)
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37