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