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