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