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