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