QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
19#include <Qt3DRender/QAttribute>
20#include <Qt3DRender/QBuffer>
21#include <Qt3DRender/QGeometry>
22#include <Qt3DCore/QTransform>
23
24typedef Qt3DRender::QAttribute Qt3DQAttribute;
25typedef Qt3DRender::QBuffer Qt3DQBuffer;
26typedef Qt3DRender::QGeometry Qt3DQGeometry;
27#else
28#include <Qt3DCore/QAttribute>
29#include <Qt3DCore/QBuffer>
30#include <Qt3DCore/QGeometry>
31
32typedef Qt3DCore::QAttribute Qt3DQAttribute;
33typedef Qt3DCore::QBuffer Qt3DQBuffer;
34typedef Qt3DCore::QGeometry Qt3DQGeometry;
35#endif
36
37#include <Qt3DRender/QEffect>
38#include <Qt3DRender/QGraphicsApiFilter>
39#include <Qt3DRender/QParameter>
40#include <Qt3DRender/QTechnique>
41
42#include <Qt3DExtras/QCylinderGeometry>
43#include <Qt3DExtras/QConeGeometry>
44#include <Qt3DExtras/QCuboidGeometry>
45#include <Qt3DExtras/QPlaneGeometry>
46#include <Qt3DExtras/QSphereGeometry>
47#include <Qt3DExtras/QTorusGeometry>
48#include <Qt3DExtras/QPhongMaterial>
49#include <Qt3DRender/QSceneLoader>
50#include <Qt3DRender/QPaintedTextureImage>
51
52#include <Qt3DRender/QMesh>
53
54#include <Qt3DExtras/QExtrudedTextGeometry>
55
56#include <QUrl>
57#include <QVector3D>
58
59#include "qgspoint3dsymbol.h"
60#include "qgs3dmapsettings.h"
61
62#include "qgsapplication.h"
63#include "qgsvectorlayer.h"
64#include "qgs3dutils.h"
67#include "qgssourcecache.h"
68
70
71
72//* INSTANCED RENDERING *//
73
74
75class QgsInstancedPoint3DSymbolHandler : public QgsFeature3DHandler
76{
77 public:
78 QgsInstancedPoint3DSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
79 : mSymbol( static_cast< QgsPoint3DSymbol *>( symbol->clone() ) )
80 , mSelectedIds( selectedIds ) {}
81
82 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
83 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
84 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
85
86 private:
87
88 static Qt3DRender::QMaterial *material( const QgsPoint3DSymbol *symbol );
89 static Qt3DRender::QGeometryRenderer *renderer( const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions );
90 static Qt3DQGeometry *symbolGeometry( const QgsPoint3DSymbol *symbol );
91
93 struct PointData
94 {
95 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
96 };
97
98 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
99
100 // input specific for this class
101 std::unique_ptr< QgsPoint3DSymbol > mSymbol;
102 // inputs - generic
103 QgsFeatureIds mSelectedIds;
104
105 // outputs
106 PointData outNormal;
107 PointData outSelected;
108};
109
110
111bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
112{
113 Q_UNUSED( context )
114 Q_UNUSED( attributeNames )
115 return true;
116}
117
118void QgsInstancedPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
119{
120 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
121
122 if ( feature.geometry().isNull() )
123 return;
124
125 Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
126 mFeatureCount++;
127}
128
129void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
130{
131 makeEntity( parent, context, outNormal, false );
132 makeEntity( parent, context, outSelected, true );
133
134 updateZRangeFromPositions( outNormal.positions );
135 updateZRangeFromPositions( outSelected.positions );
136
137 // the elevation offset is applied in the vertex shader so let's account for it as well
138 const float symbolOffset = mSymbol->transform().data()[13];
139
140 // also account for the actual height of the objects themselves
141 // NOTE -- these calculations are naive, and assume no rotation or scaling of the symbol!
142 switch ( mSymbol->shape() )
143 {
145 {
146 const float length = mSymbol->shapeProperty( QStringLiteral( "length" ) ).toFloat();
147 mZMin -= length * 0.5f;
148 mZMax += length * 0.5f;
149 break;
150 }
151
153 {
154 const float radius = mSymbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat();
155 mZMin -= radius;
156 mZMax += radius;
157 break;
158 }
159
161 {
162 const float length = mSymbol->shapeProperty( QStringLiteral( "length" ) ).toFloat();
163 mZMin -= length * 0.5f;
164 mZMax += length * 0.5f;
165 break;
166 }
167
169 {
170 const float size = mSymbol->shapeProperty( QStringLiteral( "size" ) ).toFloat();
171 mZMin -= size * 0.5f;
172 mZMax += size * 0.5f;
173 break;
174 }
175
177 {
178 const float radius = mSymbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat();
179 mZMin -= radius;
180 mZMax += radius;
181 break;
182 }
183
185 {
186 // worst case scenario -- even though planes are usually rotated so that they are flat,
187 // let's account for possible overridden rotation
188 const float size = mSymbol->shapeProperty( QStringLiteral( "size" ) ).toFloat();
189 mZMin -= size * 0.5f;
190 mZMax += size * 0.5f;
191 break;
192 }
193
197 break;
198 }
199
200 mZMin += symbolOffset;
201 mZMax += symbolOffset;
202}
203
204void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
205{
206 // build the default material
207 Qt3DRender::QMaterial *mat = material( mSymbol.get() );
208
209 if ( selected )
210 {
211 // update the material with selection colors
212 for ( Qt3DRender::QParameter *param : mat->effect()->parameters() )
213 {
214 if ( param->name() == QLatin1String( "diffuseColor" ) )
215 param->setValue( context.map().selectionColor() );
216 else if ( param->name() == QLatin1String( "ambientColor" ) )
217 param->setValue( context.map().selectionColor().darker() );
218 }
219 }
220
221 // build the entity
222 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
223 entity->addComponent( renderer( mSymbol.get(), out.positions ) );
224 entity->addComponent( mat );
225 entity->setParent( parent );
226
227// cppcheck wrongly believes entity will leak
228// cppcheck-suppress memleak
229}
230
231
232
233Qt3DRender::QMaterial *QgsInstancedPoint3DSymbolHandler::material( const QgsPoint3DSymbol *symbol )
234{
235 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
236 filterKey->setName( QStringLiteral( "renderingStyle" ) );
237 filterKey->setValue( "forward" );
238
239 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram;
240 shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/instanced.vert" ) ) ) );
241 shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/phongConstant.frag" ) ) ) );
242
243 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass;
244 renderPass->setShaderProgram( shaderProgram );
245
246 Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
247 technique->addFilterKey( filterKey );
248 technique->addRenderPass( renderPass );
249 technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
250 technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
251 technique->graphicsApiFilter()->setMajorVersion( 3 );
252 technique->graphicsApiFilter()->setMinorVersion( 2 );
253
254 const QMatrix4x4 transformMatrix = symbol->transform();
255 QMatrix3x3 normalMatrix = transformMatrix.normalMatrix(); // transponed inverse of 3x3 sub-matrix
256
257 // QMatrix3x3 is not supported for passing to shaders, so we pass QMatrix4x4
258 float *n = normalMatrix.data();
259 const QMatrix4x4 normalMatrix4(
260 n[0], n[3], n[6], 0,
261 n[1], n[4], n[7], 0,
262 n[2], n[5], n[8], 0,
263 0, 0, 0, 0 );
264
265 Qt3DRender::QParameter *paramInst = new Qt3DRender::QParameter;
266 paramInst->setName( QStringLiteral( "inst" ) );
267 paramInst->setValue( transformMatrix );
268
269 Qt3DRender::QParameter *paramInstNormal = new Qt3DRender::QParameter;
270 paramInstNormal->setName( QStringLiteral( "instNormal" ) );
271 paramInstNormal->setValue( normalMatrix4 );
272
273 Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
274 effect->addTechnique( technique );
275 effect->addParameter( paramInst );
276 effect->addParameter( paramInstNormal );
277
278 symbol->materialSettings()->addParametersToEffect( effect );
279
280 Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
281 material->setEffect( effect );
282
283 return material;
284}
285
286Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer( const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions )
287{
288 const int count = positions.count();
289 const int byteCount = positions.count() * sizeof( QVector3D );
290 QByteArray ba;
291 ba.resize( byteCount );
292 memcpy( ba.data(), positions.constData(), byteCount );
293
294 Qt3DQBuffer *instanceBuffer = new Qt3DQBuffer();
295 instanceBuffer->setData( ba );
296
297 Qt3DQAttribute *instanceDataAttribute = new Qt3DQAttribute;
298 instanceDataAttribute->setName( QStringLiteral( "pos" ) );
299 instanceDataAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
300 instanceDataAttribute->setVertexBaseType( Qt3DQAttribute::Float );
301 instanceDataAttribute->setVertexSize( 3 );
302 instanceDataAttribute->setByteOffset( 0 );
303 instanceDataAttribute->setDivisor( 1 );
304 instanceDataAttribute->setBuffer( instanceBuffer );
305 instanceDataAttribute->setCount( count );
306 instanceDataAttribute->setByteStride( 3 * sizeof( float ) );
307
308 Qt3DQGeometry *geometry = symbolGeometry( symbol );
309 geometry->addAttribute( instanceDataAttribute );
310 geometry->setBoundingVolumePositionAttribute( instanceDataAttribute );
311
312 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
313 renderer->setGeometry( geometry );
314 renderer->setInstanceCount( count );
315
316 return renderer;
317}
318
319Qt3DQGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry( const QgsPoint3DSymbol *symbol )
320{
321 switch ( symbol->shape() )
322 {
324 {
325 const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat();
326 const float length = symbol->shapeProperty( QStringLiteral( "length" ) ).toFloat();
327 Qt3DExtras::QCylinderGeometry *g = new Qt3DExtras::QCylinderGeometry;
328 //g->setRings(2); // how many vertices vertically
329 //g->setSlices(8); // how many vertices on circumference
330 g->setRadius( radius );
331 g->setLength( length );
332 return g;
333 }
334
336 {
337 const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat();
338 Qt3DExtras::QSphereGeometry *g = new Qt3DExtras::QSphereGeometry;
339 g->setRadius( radius );
340 return g;
341 }
342
344 {
345 const float length = symbol->shapeProperty( QStringLiteral( "length" ) ).toFloat();
346 const float bottomRadius = symbol->shapeProperty( QStringLiteral( "bottomRadius" ) ).toFloat();
347 const float topRadius = symbol->shapeProperty( QStringLiteral( "topRadius" ) ).toFloat();
348
349 Qt3DExtras::QConeGeometry *g = new Qt3DExtras::QConeGeometry;
350 g->setLength( length );
351 g->setBottomRadius( bottomRadius );
352 g->setTopRadius( topRadius );
353 //g->setHasBottomEndcap(hasBottomEndcap);
354 //g->setHasTopEndcap(hasTopEndcap);
355 return g;
356 }
357
359 {
360 const float size = symbol->shapeProperty( QStringLiteral( "size" ) ).toFloat();
361 Qt3DExtras::QCuboidGeometry *g = new Qt3DExtras::QCuboidGeometry;
362 g->setXExtent( size );
363 g->setYExtent( size );
364 g->setZExtent( size );
365 return g;
366 }
367
369 {
370 const float radius = symbol->shapeProperty( QStringLiteral( "radius" ) ).toFloat();
371 const float minorRadius = symbol->shapeProperty( QStringLiteral( "minorRadius" ) ).toFloat();
372 Qt3DExtras::QTorusGeometry *g = new Qt3DExtras::QTorusGeometry;
373 g->setRadius( radius );
374 g->setMinorRadius( minorRadius );
375 return g;
376 }
377
379 {
380 const float size = symbol->shapeProperty( QStringLiteral( "size" ) ).toFloat();
381 Qt3DExtras::QPlaneGeometry *g = new Qt3DExtras::QPlaneGeometry;
382 g->setWidth( size );
383 g->setHeight( size );
384 return g;
385 }
386
388 {
389 const float depth = symbol->shapeProperty( QStringLiteral( "depth" ) ).toFloat();
390 const QString text = symbol->shapeProperty( QStringLiteral( "text" ) ).toString();
391 Qt3DExtras::QExtrudedTextGeometry *g = new Qt3DExtras::QExtrudedTextGeometry;
392 g->setDepth( depth );
393 g->setText( text );
394 return g;
395 }
396
399 break;
400 }
401 Q_ASSERT( false );
402 return nullptr;
403}
404
405//* 3D MODEL RENDERING *//
406
407
408class QgsModelPoint3DSymbolHandler : public QgsFeature3DHandler
409{
410 public:
411 QgsModelPoint3DSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
412 : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) )
413 , mSelectedIds( selectedIds ) {}
414
415 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
416 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
417 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
418
419 private:
420
421 static void addSceneEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent );
422 static void addMeshEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected );
423 static Qt3DCore::QTransform *transform( QVector3D position, const QgsPoint3DSymbol *symbol );
424
426 struct PointData
427 {
428 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
429 };
430
431 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
432
433 // input specific for this class
434 std::unique_ptr< QgsPoint3DSymbol > mSymbol;
435 // inputs - generic
436 QgsFeatureIds mSelectedIds;
437
438 // outputs
439 PointData outNormal;
440 PointData outSelected;
441};
442
443bool QgsModelPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
444{
445 Q_UNUSED( context )
446 Q_UNUSED( attributeNames )
447 return true;
448}
449
450void QgsModelPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
451{
452 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
453
454 if ( feature.geometry().isNull() )
455 return;
456
457 Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
458 mFeatureCount++;
459}
460
461void QgsModelPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
462{
463 makeEntity( parent, context, outNormal, false );
464 makeEntity( parent, context, outSelected, true );
465
466 updateZRangeFromPositions( outNormal.positions );
467 updateZRangeFromPositions( outSelected.positions );
468
469 // the elevation offset is applied separately in QTransform added to sub-entities
470 const float symbolHeight = mSymbol->transform().data()[13];
471 mZMin += symbolHeight;
472 mZMax += symbolHeight;
473}
474
475void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
476{
477 if ( selected )
478 {
479 addMeshEntities( context.map(), out.positions, mSymbol.get(), parent, true );
480 }
481 else
482 {
483 // "overwriteMaterial" is a legacy setting indicating that non-embedded material should be used
484 if ( mSymbol->shapeProperty( QStringLiteral( "overwriteMaterial" ) ).toBool()
485 || ( mSymbol->materialSettings() && mSymbol->materialSettings()->type() != QLatin1String( "null" ) ) )
486 {
487 addMeshEntities( context.map(), out.positions, mSymbol.get(), parent, false );
488 }
489 else
490 {
491 addSceneEntities( context.map(), out.positions, mSymbol.get(), parent );
492 }
493 }
494}
495
496
497
498void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent )
499{
500 Q_UNUSED( map )
501 for ( const QVector3D &position : positions )
502 {
503 const QString source = QgsApplication::sourceCache()->localFilePath( symbol->shapeProperty( QStringLiteral( "model" ) ).toString() );
504 // if the source is remote, the Qgs3DMapScene will take care of refreshing this 3D symbol when the source is fetched
505 if ( !source.isEmpty() )
506 {
507 // build the entity
508 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
509
510 const QUrl url = QUrl::fromLocalFile( source );
511 Qt3DRender::QSceneLoader *modelLoader = new Qt3DRender::QSceneLoader;
512 modelLoader->setSource( url );
513
514 entity->addComponent( modelLoader );
515 entity->addComponent( transform( position, symbol ) );
516 entity->setParent( parent );
517
518// cppcheck wrongly believes entity will leak
519// cppcheck-suppress memleak
520 }
521 }
522}
523
524void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected )
525{
526 if ( positions.empty() )
527 return;
528
529 // build the default material
530 QgsMaterialContext materialContext;
531 materialContext.setIsSelected( are_selected );
532 materialContext.setSelectionColor( map.selectionColor() );
533 Qt3DRender::QMaterial *mat = symbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
534
535 // get nodes
536 for ( const QVector3D &position : positions )
537 {
538 const QString source = QgsApplication::sourceCache()->localFilePath( symbol->shapeProperty( QStringLiteral( "model" ) ).toString() );
539 if ( !source.isEmpty() )
540 {
541 // build the entity
542 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
543
544 const QUrl url = QUrl::fromLocalFile( source );
545 Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
546 mesh->setSource( url );
547
548 entity->addComponent( mesh );
549 entity->addComponent( mat );
550 entity->addComponent( transform( position, symbol ) );
551 entity->setParent( parent );
552
553// cppcheck wrongly believes entity will leak
554// cppcheck-suppress memleak
555 }
556 }
557}
558
559Qt3DCore::QTransform *QgsModelPoint3DSymbolHandler::transform( QVector3D position, const QgsPoint3DSymbol *symbol )
560{
561 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
562 tr->setMatrix( symbol->transform() );
563 tr->setTranslation( position + tr->translation() );
564 return tr;
565}
566
567// --------------
568
569//* BILLBOARD RENDERING *//
570
571class QgsPoint3DBillboardSymbolHandler : public QgsFeature3DHandler
572{
573 public:
574 QgsPoint3DBillboardSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
575 : mSymbol( static_cast< QgsPoint3DSymbol * >( symbol->clone() ) )
576 , mSelectedIds( selectedIds ) {}
577
578 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
579 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
580 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
581
582 private:
583
585 struct PointData
586 {
587 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
588 };
589
590 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
591
592 // input specific for this class
593 std::unique_ptr< QgsPoint3DSymbol > mSymbol;
594 // inputs - generic
595 QgsFeatureIds mSelectedIds;
596
597 // outputs
598 PointData outNormal;
599 PointData outSelected;
600};
601
602bool QgsPoint3DBillboardSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
603{
604 Q_UNUSED( context )
605 Q_UNUSED( attributeNames )
606 return true;
607}
608
609void QgsPoint3DBillboardSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
610{
611 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
612
613 if ( feature.geometry().isNull() )
614 return;
615
616 Qgs3DUtils::extractPointPositions( feature, context.map(), mSymbol->altitudeClamping(), out.positions );
617 mFeatureCount++;
618}
619
620void QgsPoint3DBillboardSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
621{
622 makeEntity( parent, context, outNormal, false );
623 makeEntity( parent, context, outSelected, true );
624
625 updateZRangeFromPositions( outNormal.positions );
626 updateZRangeFromPositions( outSelected.positions );
627
628 // the elevation offset is applied externally through a QTransform of QEntity so let's account for it
629 const float billboardHeight = mSymbol->transform().data()[13];
630 mZMin += billboardHeight;
631 mZMax += billboardHeight;
632}
633
634void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
635{
636 // Billboard Geometry
637 QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
638 billboardGeometry->setPoints( out.positions );
639
640 // Billboard Geometry Renderer
641 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
642 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
643 billboardGeometryRenderer->setGeometry( billboardGeometry );
644 billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
645
646 // Billboard Material
648 QgsMarkerSymbol *symbol = mSymbol->billboardSymbol();
649
650 if ( symbol )
651 {
652 billboardMaterial->setTexture2DFromSymbol( symbol, context.map(), selected );
653 }
654 else
655 {
656 billboardMaterial->useDefaultSymbol( context.map(), selected );
657 }
658
659 // Billboard Transform
660 Qt3DCore::QTransform *billboardTransform = new Qt3DCore::QTransform();
661 billboardTransform->setMatrix( mSymbol->billboardTransform() );
662
663 // Build the entity
664 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
665
666 entity->addComponent( billboardMaterial );
667 entity->addComponent( billboardTransform );
668 entity->addComponent( billboardGeometryRenderer );
669 entity->setParent( parent );
670
671// cppcheck wrongly believes entity will leak
672// cppcheck-suppress memleak
673}
674
675
676namespace Qgs3DSymbolImpl
677{
678
679 QgsFeature3DHandler *handlerForPoint3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
680 {
681 const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( symbol );
682 if ( !pointSymbol )
683 return nullptr;
684
685 if ( pointSymbol->shape() == Qgis::Point3DShape::Model )
686 return new QgsModelPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
687 // Add proper handler for billboard
688 else if ( pointSymbol->shape() == Qgis::Point3DShape::Billboard )
689 return new QgsPoint3DBillboardSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
690 else
691 return new QgsInstancedPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
692 }
693
694 Qt3DCore::QEntity *entityForPoint3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPoint3DSymbol &symbol )
695 {
696 QgsFeature3DHandler *handler = handlerForPoint3DSymbol( layer, &symbol );
697 Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
698 delete handler;
699 return e;
700 }
701}
702
@ Plane
Flat plane.
@ Cylinder
Cylinder.
@ ExtrudedText
Extruded text.
@ Billboard
Billboard.
QColor selectionColor() const
Returns color used for selected features.
static void extractPointPositions(const QgsFeature &f, const Qgs3DMapSettings &map, Qgis::AltitudeClamping altClamp, QVector< QVector3D > &positions)
Calculates (x,y,z) positions of (multi)point from the given feature.
Definition: qgs3dutils.cpp:496
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 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:164
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.
QgsAbstractMaterialSettings * materialSettings() const
Returns material settings 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.
Qgis::Point3DShape shape() const
Returns 3D shape for points.
QgsAbstract3DSymbol * clone() const override
Returns a new instance of the symbol with the same settings.
QVariant shapeProperty(const QString &property) const
Returns the value for a specific shape property.
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)
Qt3DCore::QAttribute Qt3DQAttribute
Definition: qgs3daxis.cpp:28
Qt3DCore::QBuffer Qt3DQBuffer
Definition: qgs3daxis.cpp:30
Qt3DCore::QGeometry Qt3DQGeometry
Definition: qgs3daxis.cpp:29
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry