QGIS API Documentation 4.3.0-Master (0cfde48c85b)
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 <memory>
19
20#include "qgs3d.h"
21#include "qgs3drendercontext.h"
22#include "qgs3dutils.h"
23#include "qgsapplication.h"
25#include "qgsellipsoidutils.h"
27#include "qgsgeotransform.h"
28#include "qgsgltf3dutils.h"
29#include "qgslogger.h"
32#include "qgsobj3dutils.h"
33#include "qgsphongmaterial.h"
37#include "qgspoint3dsymbol.h"
38#include "qgssourcecache.h"
39#include "qgstexturematerial.h"
40#include "qgsunlitmaterial.h"
41#include "qgsvectorlayer.h"
42
43#include <QFileInfo>
44#include <QString>
45#include <QUrl>
46#include <QVector3D>
47#include <Qt3DCore/QAttribute>
48#include <Qt3DCore/QBuffer>
49#include <Qt3DCore/QGeometry>
50#include <Qt3DExtras/QConeGeometry>
51#include <Qt3DExtras/QCuboidGeometry>
52#include <Qt3DExtras/QCylinderGeometry>
53#include <Qt3DExtras/QExtrudedTextGeometry>
54#include <Qt3DExtras/QPhongMaterial>
55#include <Qt3DExtras/QPlaneGeometry>
56#include <Qt3DExtras/QSphereGeometry>
57#include <Qt3DExtras/QTorusGeometry>
58#include <Qt3DRender/QEffect>
59#include <Qt3DRender/QGeometryRenderer>
60#include <Qt3DRender/QGraphicsApiFilter>
61#include <Qt3DRender/QPaintedTextureImage>
62#include <Qt3DRender/QParameter>
63#include <Qt3DRender/QTechnique>
64
65using namespace Qt::StringLiterals;
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
81 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent ) override;
82 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
83 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
84
85 private:
86 static QgsMaterial *material( const QgsPoint3DSymbol *symbol, const QgsMaterialContext &materialContext, bool hasDataDefinedScale, bool hasDataDefinedRotation );
87 static Qt3DRender::QGeometryRenderer *renderer( const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions, const QVector<QVector3D> &scales, const QVector<QVector4D> rotations );
88 static Qt3DCore::QGeometry *symbolGeometry( const QgsPoint3DSymbol *symbol );
89
91 struct PointData
92 {
93 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
94 QVector<QVector3D> scales;
95 QVector<QVector4D> rotations;
96 };
97
98 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
99
100 // input specific for this class
101 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
102 QVector3D mSymbolScale;
103 QQuaternion mSymbolRotation;
104 QVector3D mPointTranslation;
105 // inputs - generic
106 QgsFeatureIds mSelectedIds;
107 // outputs
108 PointData outNormal;
109 PointData outSelected;
110
111 double mGlobeSemiMajorAxis = -1;
112 double mGlobeSemiMinorAxis = -1;
113 bool mOrientToGlobeSurface = false;
114};
115
116
117bool QgsInstancedPoint3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent )
118{
119 mChunkOrigin = chunkExtent.center();
120 mChunkExtent = chunkExtent;
121
122 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
123 attributeNames.unite( attrs );
124 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.expressionContext() );
125 attributeNames.unite( attrs );
126
127 Qgs3DUtils::decomposeTransformMatrix( mSymbol->transform(), mPointTranslation, mSymbolRotation, mSymbolScale );
128
129 // on a globe, symbols need to be oriented relative to the globe
130 if ( context.crs().type() == Qgis::CrsType::Geocentric )
131 {
133 if ( params.valid )
134 {
135 mGlobeSemiMajorAxis = params.semiMajor;
136 mGlobeSemiMinorAxis = params.semiMinor;
137 mOrientToGlobeSurface = true;
138 }
139 }
140
141 return true;
142}
143
144void QgsInstancedPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
145{
146 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
147
148 if ( feature.geometry().isNull() )
149 return;
150
151 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
152
153 QgsVector3D translation = mPointTranslation;
154 const bool hasDDTranslation = ddp.isActive( QgsAbstract3DSymbol::Property::TranslationX )
157 if ( hasDDTranslation )
158 {
159 const double translationX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationX, context.expressionContext(), translation.x() );
160 const double translationY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationY, context.expressionContext(), translation.y() );
161 const double translationZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationZ, context.expressionContext(), translation.z() );
162 translation = QgsVector3D( translationX, translationY, translationZ );
163 }
164
165 const std::size_t oldSize = out.positions.size();
166
167 Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions );
168
169 const std::size_t added = out.positions.size() - oldSize;
170
172
173 if ( hasDDScale )
174 {
175 out.scales.resize( out.positions.size() );
176 QVector3D *outScale = out.scales.data() + oldSize;
177
178 const double scaleX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleX, context.expressionContext(), mSymbolScale.x() );
179 const double scaleY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleY, context.expressionContext(), mSymbolScale.y() );
180 const double scaleZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleZ, context.expressionContext(), mSymbolScale.z() );
181
182 for ( std::size_t i = 0; i < added; ++i )
183 {
184 ( *outScale++ ) = QVector3D( static_cast< float >( scaleX ), static_cast< float >( scaleY ), static_cast< float >( scaleZ ) );
185 }
186 }
187
188 const bool hasDDRotation = ddp.isActive( QgsAbstract3DSymbol::Property::RotationX )
191 const bool needsRotation = hasDDRotation || mOrientToGlobeSurface;
192
193 QQuaternion baseRotation = mSymbolRotation;
194 if ( hasDDRotation )
195 {
196 // extract default rotation components from symbol rotation
197 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
198
199 const double rotationX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationX, context.expressionContext(), baseEuler.x() );
200 const double rotationY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationY, context.expressionContext(), baseEuler.y() );
201 const double rotationZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationZ, context.expressionContext(), baseEuler.z() );
202
203 //... and then re-calculate the rotation vector for this feature
204 baseRotation = QQuaternion::fromEulerAngles( static_cast< float >( rotationX ), static_cast< float >( rotationY ), static_cast< float >( rotationZ ) );
205 }
206
207 // rotations and translations
208 // if we are using globe, we first rotate the points and then translate them, so that they are offset relative to the globe
209 if ( needsRotation )
210 out.rotations.resize( out.positions.size() );
211 QVector4D *outRotation = needsRotation ? out.rotations.data() + oldSize : nullptr;
212
213 if ( mOrientToGlobeSurface )
214 {
215 for ( std::size_t i = 0; i < added; ++i )
216 {
217 const QgsVector3D truePosition = mChunkOrigin + QgsVector3D( out.positions[oldSize + i] );
218 const QQuaternion enuRotation = QgsEllipsoidUtils::ellipsoidEastNorthUpRotation( truePosition, mGlobeSemiMajorAxis, mGlobeSemiMinorAxis );
219 ( *outRotation++ ) = ( enuRotation * baseRotation ).toVector4D();
220
221 // translation is relative to the surface ("up" is always away from the globe)
222 out.positions[oldSize + i] += enuRotation.rotatedVector( translation.toVector3D() );
223 }
224 }
225 else // we are not on globe, but we still have to apply (uncoditionally) translations and if set, rotations as well
226 {
227 const QVector4D r = baseRotation.toVector4D();
228 for ( std::size_t i = 0; i < added; ++i )
229 {
230 if ( needsRotation )
231 ( *outRotation++ ) = r;
232 out.positions[oldSize + i] += translation.toVector3D();
233 }
234 }
235
236 mFeatureCount++;
237}
238
239void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
240{
241 makeEntity( parent, context, outNormal, false );
242 makeEntity( parent, context, outSelected, true );
243
244 auto updateZRangeFromPointData = [this]( const PointData &pointData ) {
245 const QVector3D *scales = pointData.scales.empty() ? nullptr : pointData.scales.constData();
246 for ( const QVector3D &pos : std::as_const( pointData.positions ) )
247 {
248 double minZ = 0;
249 double maxZ = 0;
250
251 // also account for the actual height of the objects themselves
252 // NOTE -- these calculations are naive, and assume no rotation or scaling of the symbol!
253 switch ( mSymbol->shape() )
254 {
256 {
257 const float length = mSymbol->shapeProperty( u"length"_s ).toFloat();
258 minZ -= length * 0.5f;
259 maxZ += length * 0.5f;
260 break;
261 }
262
264 {
265 const float radius = mSymbol->shapeProperty( u"radius"_s ).toFloat();
266 minZ -= radius;
267 maxZ += radius;
268 break;
269 }
270
272 {
273 const float length = mSymbol->shapeProperty( u"length"_s ).toFloat();
274 minZ -= length * 0.5f;
275 maxZ += length * 0.5f;
276 break;
277 }
278
280 {
281 const float size = mSymbol->shapeProperty( u"size"_s ).toFloat();
282 minZ -= size * 0.5f;
283 maxZ += size * 0.5f;
284 break;
285 }
286
288 {
289 const float radius = mSymbol->shapeProperty( u"radius"_s ).toFloat();
290 minZ -= radius;
291 maxZ += radius;
292 break;
293 }
294
296 {
297 // worst case scenario -- even though planes are usually rotated so that they are flat,
298 // let's account for possible overridden rotation
299 const float size = mSymbol->shapeProperty( u"size"_s ).toFloat();
300 minZ -= size * 0.5f;
301 maxZ += size * 0.5f;
302 break;
303 }
304
308 break;
309 }
310
311 if ( scales )
312 {
313 const double zScale = ( *scales++ )[2];
314 minZ *= zScale;
315 maxZ *= zScale;
316 }
317
318 // as we are relative to chunk center elevation we have to add mChunkOrigin.z()
319 minZ += pos.z() + mChunkOrigin.z();
320 maxZ += pos.z() + mChunkOrigin.z();
321
322 if ( minZ < mZMin )
323 mZMin = static_cast< float >( minZ );
324 if ( maxZ > mZMax )
325 mZMax = static_cast< float >( maxZ );
326 }
327 };
328
329 updateZRangeFromPointData( outNormal );
330 updateZRangeFromPointData( outSelected );
331}
332
333void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
334{
335 if ( out.positions.isEmpty() )
336 {
337 return; // nothing to show - no need to create the entity
338 }
339
340 // build the default material
342 materialContext.setIsSelected( selected );
343 materialContext.setIsHighlighted( mHighlightingEnabled );
344 QgsMaterial *mat = material( mSymbol.get(), materialContext, !out.scales.empty(), !out.rotations.empty() );
345 if ( !mat )
346 return;
347
348 mat->addParameter( new Qt3DRender::QParameter( "symbolScale", mSymbolScale, mat ) );
349 mat->addParameter( new Qt3DRender::QParameter( "symbolRotation", mSymbolRotation.toVector4D(), mat ) );
350
351 // add transform (our geometry has coordinates relative to mChunkOrigin)
352 QgsGeoTransform *tr = new QgsGeoTransform;
353 tr->setGeoTranslation( mChunkOrigin );
354
355 // build the entity
356 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
357 entity->addComponent( renderer( mSymbol.get(), out.positions, out.scales, out.rotations ) );
358 entity->addComponent( mat );
359 entity->addComponent( tr );
360 entity->setParent( parent );
361
362 // cppcheck wrongly believes entity will leak
363 // cppcheck-suppress memleak
364}
365
366
367QgsMaterial *QgsInstancedPoint3DSymbolHandler::material( const QgsPoint3DSymbol *symbol, const QgsMaterialContext &materialContext, bool hasDataDefinedScale, bool hasDataDefinedRotation )
368{
370 if ( hasDataDefinedScale )
372 if ( hasDataDefinedRotation )
374
375 const QString upAxis = symbol->shapeProperty( u"upAxis"_s ).toString();
376 const QString forwardAxis = symbol->shapeProperty( u"forwardAxis"_s ).toString();
377
378 const QMatrix4x4 meshTransform = Qgs3DUtils::axisTransformMatrix( !upAxis.isEmpty() ? upAxis : u"y"_s, !forwardAxis.isEmpty() ? forwardAxis : u"-z"_s );
379
380 if ( materialContext.isHighlighted() )
381 {
382 QgsUnlitMaterial *mat = Qgs3D::createHighlightMaterial();
383 mat->setInstancingEnabled( true, flags );
384 mat->setInstancingMeshTransform( meshTransform );
385 return mat;
386 }
387
388 const QgsAbstractMaterialSettings *settings = symbol->materialSettings();
389 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( settings ) )
390 {
391 return handler->toInstancedMaterial( settings, materialContext, flags, meshTransform );
392 }
393
394 return nullptr;
395}
396
397Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer(
398 const QgsPoint3DSymbol *symbol, const QVector<QVector3D> &positions, const QVector<QVector3D> &scales, const QVector<QVector4D> rotations
399)
400{
401 const std::size_t count = positions.count();
402 const std::size_t byteCount = positions.count() * sizeof( QVector3D );
403 QByteArray ba;
404 ba.resize( byteCount );
405 memcpy( ba.data(), positions.constData(), byteCount );
406
407 Qt3DCore::QBuffer *instanceBuffer = new Qt3DCore::QBuffer();
408 instanceBuffer->setData( ba );
409
410 Qt3DCore::QAttribute *instanceTranslationAttribute = new Qt3DCore::QAttribute;
411 instanceTranslationAttribute->setName( u"instanceTranslation"_s );
412 instanceTranslationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
413 instanceTranslationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
414 instanceTranslationAttribute->setVertexSize( 3 );
415 instanceTranslationAttribute->setByteOffset( 0 );
416 instanceTranslationAttribute->setDivisor( 1 );
417 instanceTranslationAttribute->setBuffer( instanceBuffer );
418 instanceTranslationAttribute->setCount( count );
419 instanceTranslationAttribute->setByteStride( 3 * sizeof( float ) );
420
421 Qt3DCore::QGeometry *geometry = symbolGeometry( symbol );
422 geometry->addAttribute( instanceTranslationAttribute );
423 geometry->setBoundingVolumePositionAttribute( instanceTranslationAttribute );
424
425 if ( !scales.empty() )
426 {
427 auto scaleBuffer = new Qt3DCore::QBuffer();
428 auto instanceScaleAttribute = new Qt3DCore::QAttribute;
429 instanceScaleAttribute->setName( u"instanceScale"_s );
430 instanceScaleAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
431 instanceScaleAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
432 instanceScaleAttribute->setVertexSize( 3 );
433 instanceScaleAttribute->setByteOffset( 0 );
434 instanceScaleAttribute->setDivisor( 1 );
435 instanceScaleAttribute->setByteStride( 3 * sizeof( float ) );
436 QByteArray scaleBa;
437 scaleBa.resize( byteCount );
438 memcpy( scaleBa.data(), scales.constData(), byteCount );
439
440 scaleBuffer->setData( scaleBa );
441 instanceScaleAttribute->setCount( count );
442
443 instanceScaleAttribute->setBuffer( scaleBuffer );
444 geometry->addAttribute( instanceScaleAttribute );
445 }
446
447 if ( !rotations.empty() )
448 {
449 auto rotationBuffer = new Qt3DCore::QBuffer();
450 auto instanceRotationAttribute = new Qt3DCore::QAttribute;
451 instanceRotationAttribute->setName( u"instanceRotation"_s );
452 instanceRotationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
453 instanceRotationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
454 instanceRotationAttribute->setVertexSize( 4 );
455 instanceRotationAttribute->setByteOffset( 0 );
456 instanceRotationAttribute->setDivisor( 1 );
457 instanceRotationAttribute->setByteStride( 4 * sizeof( float ) );
458
459 QByteArray rotationBa;
460 const std::size_t rotationByteCount = positions.count() * sizeof( QVector4D );
461 rotationBa.resize( rotationByteCount );
462 memcpy( rotationBa.data(), rotations.constData(), rotationByteCount );
463 rotationBuffer->setData( rotationBa );
464 instanceRotationAttribute->setCount( count );
465
466 instanceRotationAttribute->setBuffer( rotationBuffer );
467 geometry->addAttribute( instanceRotationAttribute );
468 }
469
470 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
471 renderer->setGeometry( geometry );
472 renderer->setInstanceCount( count );
473
474 return renderer;
475}
476
477Qt3DCore::QGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry( const QgsPoint3DSymbol *symbol )
478{
479 switch ( symbol->shape() )
480 {
482 {
483 const float radius = symbol->shapeProperty( u"radius"_s ).toFloat();
484 const float length = symbol->shapeProperty( u"length"_s ).toFloat();
485 Qt3DExtras::QCylinderGeometry *g = new Qt3DExtras::QCylinderGeometry;
486 //g->setRings(2); // how many vertices vertically
487 //g->setSlices(8); // how many vertices on circumference
488 g->setRadius( radius );
489 g->setLength( length );
490 return g;
491 }
492
494 {
495 const float radius = symbol->shapeProperty( u"radius"_s ).toFloat();
496 const int rings = symbol->shapeProperty( u"rings"_s ).toInt();
497 const int slices = symbol->shapeProperty( u"slices"_s ).toInt();
498
499 const bool tangents = symbol->materialSettings() && symbol->materialSettings()->requiresTangents();
500 Qt3DExtras::QSphereGeometry *g = new Qt3DExtras::QSphereGeometry;
501 g->setRadius( radius );
502 g->setRings( rings );
503 g->setSlices( slices );
504 g->setGenerateTangents( tangents );
505 return g;
506 }
507
509 {
510 const float length = symbol->shapeProperty( u"length"_s ).toFloat();
511 const float bottomRadius = symbol->shapeProperty( u"bottomRadius"_s ).toFloat();
512 const float topRadius = symbol->shapeProperty( u"topRadius"_s ).toFloat();
513
514 Qt3DExtras::QConeGeometry *g = new Qt3DExtras::QConeGeometry;
515 g->setLength( length );
516 g->setBottomRadius( bottomRadius );
517 g->setTopRadius( topRadius );
518 //g->setHasBottomEndcap(hasBottomEndcap);
519 //g->setHasTopEndcap(hasTopEndcap);
520 return g;
521 }
522
524 {
525 const float size = symbol->shapeProperty( u"size"_s ).toFloat();
526 Qt3DExtras::QCuboidGeometry *g = new Qt3DExtras::QCuboidGeometry;
527 g->setXExtent( size );
528 g->setYExtent( size );
529 g->setZExtent( size );
530 return g;
531 }
532
534 {
535 const float radius = symbol->shapeProperty( u"radius"_s ).toFloat();
536 const float minorRadius = symbol->shapeProperty( u"minorRadius"_s ).toFloat();
537 Qt3DExtras::QTorusGeometry *g = new Qt3DExtras::QTorusGeometry;
538 g->setRadius( radius );
539 g->setMinorRadius( minorRadius );
540 return g;
541 }
542
544 {
545 const float size = symbol->shapeProperty( u"size"_s ).toFloat();
546 Qt3DExtras::QPlaneGeometry *g = new Qt3DExtras::QPlaneGeometry;
547 g->setWidth( size );
548 g->setHeight( size );
549 return g;
550 }
551
553 {
554 const float depth = symbol->shapeProperty( u"depth"_s ).toFloat();
555 const QString text = symbol->shapeProperty( u"text"_s ).toString();
556 Qt3DExtras::QExtrudedTextGeometry *g = new Qt3DExtras::QExtrudedTextGeometry;
557 g->setDepth( depth );
558 g->setText( text );
559 return g;
560 }
561
564 break;
565 }
566 Q_ASSERT( false );
567 return nullptr;
568}
569
570//* 3D MODEL RENDERING *//
571
572
573class QgsModelPoint3DSymbolHandler : public QgsFeature3DHandler
574{
575 public:
576 QgsModelPoint3DSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
577 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
578 , mSelectedIds( selectedIds )
579 {}
580
581 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent ) override;
582 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
583 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
584
585 private:
586 void addInstancedEntities(
587 const QVector<QVector3D> &positions,
588 const QVector<QVector3D> &scales,
589 const QVector<QQuaternion> &rotations,
590 const QgsVector3D &chunkOrigin,
591 const QgsPoint3DSymbol *symbol,
592 Qt3DCore::QEntity *parent,
593 const QgsMaterialContext &materialContext,
594 bool useEmbeddedTexture
595 );
596
598 struct PointData
599 {
600 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
601 QVector<QVector3D> scales;
602 QVector<QQuaternion> rotations;
603 };
604
605 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
606
607 // input specific for this class
608 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
609
610 QVector3D mSymbolScale;
611 QQuaternion mSymbolRotation;
612 QVector3D mPointTranslation;
613
614 // inputs - generic
615 QgsFeatureIds mSelectedIds;
616 // outputs
617 PointData outNormal;
618 PointData outSelected;
619
620 double mGlobeSemiMajorAxis = -1;
621 double mGlobeSemiMinorAxis = -1;
622 bool mOrientToGlobeSurface = false;
623};
624
625bool QgsModelPoint3DSymbolHandler::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 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
634 attributeNames.unite( attrs );
635
636 Qgs3DUtils::decomposeTransformMatrix( mSymbol->transform(), mPointTranslation, mSymbolRotation, mSymbolScale );
637
638 // on a globe, symbols need to be oriented to the ellipsoid surface normal at their position
639 if ( context.crs().type() == Qgis::CrsType::Geocentric )
640 {
642 if ( params.valid )
643 {
644 mGlobeSemiMajorAxis = params.semiMajor;
645 mGlobeSemiMinorAxis = params.semiMinor;
646 mOrientToGlobeSurface = true;
647 }
648 }
649
650 return true;
651}
652
653void QgsModelPoint3DSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
654{
655 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
656
657 if ( feature.geometry().isNull() )
658 return;
659
660 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
661
662 QgsVector3D translation = mPointTranslation;
663 const bool hasDDTranslation = ddp.isActive( QgsAbstract3DSymbol::Property::TranslationX )
666 if ( hasDDTranslation )
667 {
668 const double translationX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationX, context.expressionContext(), translation.x() );
669 const double translationY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationY, context.expressionContext(), translation.y() );
670 const double translationZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::TranslationZ, context.expressionContext(), translation.z() );
671 translation = QgsVector3D( translationX, translationY, translationZ );
672 }
673
674 const std::size_t oldSize = out.positions.size();
675
676 // on a globe, translation is deferred and rotated into the feature's local ENU frame
677 Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions );
678 const std::size_t added = out.positions.size() - oldSize;
679
681 if ( hasDDScale )
682 {
683 out.scales.resize( out.positions.size() );
684 QVector3D *outScale = out.scales.data() + oldSize;
685 const double scaleX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleX, context.expressionContext(), mSymbolScale.x() );
686 const double scaleY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleY, context.expressionContext(), mSymbolScale.y() );
687 const double scaleZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ScaleZ, context.expressionContext(), mSymbolScale.z() );
688 const QVector3D scale( static_cast< float >( scaleX ), static_cast< float >( scaleY ), static_cast< float >( scaleZ ) );
689 for ( std::size_t i = 0; i < added; ++i )
690 ( *outScale++ ) = scale;
691 }
692
693 const bool hasDDRotation = ddp.isActive( QgsAbstract3DSymbol::Property::RotationX )
696 const bool needsRotation = hasDDRotation || mOrientToGlobeSurface;
697
698 QQuaternion baseRotation = mSymbolRotation;
699 if ( hasDDRotation )
700 {
701 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
702 const double rotationX = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationX, context.expressionContext(), baseEuler.x() );
703 const double rotationY = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationY, context.expressionContext(), baseEuler.y() );
704 const double rotationZ = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::RotationZ, context.expressionContext(), baseEuler.z() );
705 baseRotation = QQuaternion::fromEulerAngles( static_cast< float >( rotationX ), static_cast< float >( rotationY ), static_cast< float >( rotationZ ) );
706 }
707
708 if ( needsRotation )
709 out.rotations.resize( out.positions.size() );
710 QQuaternion *outRotation = needsRotation ? out.rotations.data() + oldSize : nullptr;
711
712 if ( mOrientToGlobeSurface )
713 {
714 for ( std::size_t i = 0; i < added; ++i )
715 {
716 const QgsVector3D truePosition = mChunkOrigin + QgsVector3D( out.positions[oldSize + i] );
717 const QQuaternion enuRotation = QgsEllipsoidUtils::ellipsoidEastNorthUpRotation( truePosition, mGlobeSemiMajorAxis, mGlobeSemiMinorAxis );
718 ( *outRotation++ ) = enuRotation * baseRotation;
719
720 // translation is relative to the surface ("up" is always away from the globe)
721 out.positions[oldSize + i] += enuRotation.rotatedVector( translation.toVector3D() );
722 }
723 }
724 else
725 {
726 for ( std::size_t i = 0; i < added; ++i )
727 {
728 if ( needsRotation )
729 ( *outRotation++ ) = baseRotation;
730 out.positions[oldSize + i] += translation.toVector3D();
731 }
732 }
733
734 mFeatureCount++;
735}
736
737void QgsModelPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
738{
739 makeEntity( parent, context, outNormal, false );
740 makeEntity( parent, context, outSelected, true );
741
742 updateZRangeFromPositions( outNormal.positions );
743 updateZRangeFromPositions( outSelected.positions );
744
745 // the elevation offset is applied separately in QTransform added to sub-entities
746 const float symbolHeight = mSymbol->transform().data()[14];
747 // as we are relative to chunk center elevation we have to add mChunkOrigin.z()
748 mZMin += static_cast<float>( symbolHeight + mChunkOrigin.z() );
749 mZMax += static_cast<float>( symbolHeight + mChunkOrigin.z() );
750}
751
752void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
753{
754 if ( out.positions.isEmpty() )
755 {
756 return; // nothing to show - no need to create the entity
757 }
758
759 const QgsAbstractMaterialSettings *settings = mSymbol->materialSettings();
760 const bool useEmbeddedTexture = !mSymbol->shapeProperty( u"overwriteMaterial"_s ).toBool() && ( !settings || settings->type() == "null"_L1 );
761
763 materialContext.setIsSelected( selected );
764 materialContext.setIsHighlighted( mHighlightingEnabled );
765
766 addInstancedEntities( out.positions, out.scales, out.rotations, mChunkOrigin, mSymbol.get(), parent, materialContext, useEmbeddedTexture );
767}
768
769void QgsModelPoint3DSymbolHandler::addInstancedEntities(
770 const QVector<QVector3D> &positions,
771 const QVector<QVector3D> &scales,
772 const QVector<QQuaternion> &rotations,
773 const QgsVector3D &chunkOrigin,
774 const QgsPoint3DSymbol *symbol,
775 Qt3DCore::QEntity *parent,
776 const QgsMaterialContext &materialContext,
777 bool useEmbeddedTexture
778)
779{
780 const QString source = QgsApplication::sourceCache()->localFilePath( symbol->shapeProperty( u"model"_s ).toString() );
781
782 std::vector<QgsMeshNodeData> meshes;
783
784 const QString suffix = QFileInfo( source ).suffix().toLower();
785 if ( suffix == "gltf"_L1 || suffix == "glb"_L1 )
786 {
787 QStringList gltfErrors;
788 for ( QgsMeshNodeData &m : QgsGltf3DUtils::buildGltfGeometries( source, materialContext, &gltfErrors, parent ) )
789 meshes.push_back( std::move( m ) );
790 if ( !gltfErrors.isEmpty() )
791 QgsDebugError( u"GLTF instancing errors for '%1': %2"_s.arg( source, gltfErrors.join( ", "_L1 ) ) );
792 }
793 else if ( suffix == "obj"_L1 )
794 {
795 for ( QgsMeshNodeData &m : QgsObj3DUtils::buildObjGeometries( source, materialContext ) )
796 meshes.push_back( std::move( m ) );
797 }
798 else
799 {
800 QgsDebugError( u"Unsupported model file suffix '%1' for source: %2"_s.arg( suffix, source ) );
801 return;
802 }
803
804 if ( meshes.empty() )
805 {
806 QgsDebugMsgLevel( u"No meshes loaded for model symbol source: %1"_s.arg( source ), 2 );
807 return;
808 }
809
810 const int count = positions.size();
811
812 QByteArray translationData( reinterpret_cast<const char *>( positions.constData() ), static_cast<qsizetype>( count * sizeof( QVector3D ) ) );
813
814 const QString upAxis = symbol->shapeProperty( u"upAxis"_s ).toString();
815 const QString forwardAxis = symbol->shapeProperty( u"forwardAxis"_s ).toString();
816
817 Qgis::InstancedMaterialFlags instancedFlags;
818 if ( !scales.empty() )
820 if ( !rotations.empty() )
822
823 const QMatrix4x4 meshTransform = Qgs3DUtils::axisTransformMatrix( upAxis, forwardAxis );
824
825 Qt3DCore::QBuffer *translationBufferData = new Qt3DCore::QBuffer( parent );
826 translationBufferData->setData( translationData );
827
828 Qt3DCore::QBuffer *scaleBufferData = nullptr;
829 if ( !scales.empty() )
830 {
831 QByteArray scaleData( reinterpret_cast<const char *>( scales.constData() ), static_cast<qsizetype>( count * sizeof( QVector3D ) ) );
832 scaleBufferData = new Qt3DCore::QBuffer( parent );
833 scaleBufferData->setData( scaleData );
834 }
835
836 Qt3DCore::QBuffer *rotationBufferData = nullptr;
837 if ( !rotations.empty() )
838 {
839 QVector<QVector4D> rotationVectors;
840 rotationVectors.reserve( count );
841 for ( const QQuaternion &q : rotations )
842 rotationVectors.append( q.toVector4D() );
843 QByteArray rotationData( reinterpret_cast<const char *>( rotationVectors.constData() ), static_cast<qsizetype>( count * sizeof( QVector4D ) ) );
844 rotationBufferData = new Qt3DCore::QBuffer( parent );
845 rotationBufferData->setData( rotationData );
846 }
847
848 for ( QgsMeshNodeData &mesh : meshes )
849 {
850 Qt3DCore::QGeometry *geom = mesh.geometry.release();
851
852 Qt3DCore::QAttribute *translationAttribute = new Qt3DCore::QAttribute;
853 translationAttribute->setName( u"instanceTranslation"_s );
854 translationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
855 translationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
856 translationAttribute->setVertexSize( 3 );
857 translationAttribute->setByteOffset( 0 );
858 translationAttribute->setByteStride( 3 * sizeof( float ) );
859 translationAttribute->setDivisor( 1 );
860 translationAttribute->setCount( static_cast<uint>( count ) );
861 translationAttribute->setBuffer( translationBufferData );
862 geom->addAttribute( translationAttribute );
863 geom->setBoundingVolumePositionAttribute( translationAttribute );
864
865 if ( scaleBufferData )
866 {
867 Qt3DCore::QAttribute *scaleAttribute = new Qt3DCore::QAttribute;
868 scaleAttribute->setName( u"instanceScale"_s );
869 scaleAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
870 scaleAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
871 scaleAttribute->setVertexSize( 3 );
872 scaleAttribute->setByteOffset( 0 );
873 scaleAttribute->setByteStride( 3 * sizeof( float ) );
874 scaleAttribute->setDivisor( 1 );
875 scaleAttribute->setCount( static_cast<uint>( count ) );
876 scaleAttribute->setBuffer( scaleBufferData );
877 geom->addAttribute( scaleAttribute );
878 }
879
880 if ( rotationBufferData )
881 {
882 Qt3DCore::QAttribute *rotationAttribute = new Qt3DCore::QAttribute;
883 rotationAttribute->setName( u"instanceRotation"_s );
884 rotationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
885 rotationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
886 rotationAttribute->setVertexSize( 4 );
887 rotationAttribute->setByteOffset( 0 );
888 rotationAttribute->setByteStride( 4 * sizeof( float ) );
889 rotationAttribute->setDivisor( 1 );
890 rotationAttribute->setCount( static_cast<uint>( count ) );
891 rotationAttribute->setBuffer( rotationBufferData );
892 geom->addAttribute( rotationAttribute );
893 }
894
895 const QMatrix4x4 meshTransformCombined = meshTransform * mesh.meshTransform;
896
897 QgsMaterial *mat = nullptr;
898 if ( materialContext.isHighlighted() )
899 {
900 QgsUnlitMaterial *highlightMaterial = Qgs3D::createHighlightMaterial();
901 highlightMaterial->setInstancingEnabled( true, instancedFlags );
902 highlightMaterial->setInstancingMeshTransform( meshTransformCombined );
903 mat = highlightMaterial;
904 }
905 else if ( useEmbeddedTexture )
906 {
907 if ( QgsPhongTexturedMaterial *phongTexMat = qobject_cast<QgsPhongTexturedMaterial *>( mesh.material.get() ) )
908 {
909 phongTexMat->setInstancingEnabled( true, instancedFlags );
910 phongTexMat->setInstancingMeshTransform( meshTransformCombined );
911 }
912 else if ( QgsPhongMaterial *phongMat = qobject_cast<QgsPhongMaterial *>( mesh.material.get() ) )
913 {
914 phongMat->setInstancingEnabled( true, instancedFlags );
915 phongMat->setInstancingMeshTransform( meshTransformCombined );
916 }
917 else if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( mesh.material.get() ) )
918 {
919 pbrMat->setInstancingEnabled( true, instancedFlags );
920 pbrMat->setInstancingMeshTransform( meshTransformCombined );
921 }
922 else if ( QgsTextureMaterial *gltfTexMat = qobject_cast<QgsTextureMaterial *>( mesh.material.get() ) )
923 {
924 gltfTexMat->setInstancingEnabled( true, instancedFlags );
925 gltfTexMat->setInstancingMeshTransform( meshTransformCombined );
926 }
927 mat = mesh.material.release();
928 }
929 else
930 {
931 const QgsAbstractMaterialSettings *settings = symbol->materialSettings();
932 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( settings ) )
933 mat = handler->toInstancedMaterial( settings, materialContext, instancedFlags, meshTransformCombined );
934 }
935
936 if ( !mat )
937 {
938 QgsMetalRoughMaterial *metal = new QgsMetalRoughMaterial();
939 metal->setInstancingEnabled( true, instancedFlags );
940 metal->setInstancingMeshTransform( meshTransformCombined );
941 mat = metal;
942 }
943
944 mat->addParameter( new Qt3DRender::QParameter( "symbolScale", mSymbolScale, mat ) );
945 mat->addParameter( new Qt3DRender::QParameter( "symbolRotation", mSymbolRotation.toVector4D(), mat ) );
946
947 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
948 renderer->setGeometry( geom );
949 renderer->setInstanceCount( static_cast<int>( count ) );
950
951 QgsGeoTransform *geoTransform = new QgsGeoTransform;
952 geoTransform->setGeoTranslation( chunkOrigin );
953
954 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
955 entity->addComponent( renderer );
956 entity->addComponent( mat );
957 entity->addComponent( geoTransform );
958 entity->setParent( parent );
959
960 // cppcheck wrongly believes entity will leak
961 // cppcheck-suppress memleak
962 }
963}
964
965// --------------
966
967//* BILLBOARD RENDERING *//
968
969class QgsPoint3DBillboardSymbolHandler : public QgsFeature3DHandler
970{
971 public:
972 QgsPoint3DBillboardSymbolHandler( const QgsPoint3DSymbol *symbol, const QgsFeatureIds &selectedIds )
973 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
974 , mSelectedIds( selectedIds )
975 {}
976
977 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent ) override;
978 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
979 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
980
981 private:
983 struct PointData
984 {
985 QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
986 };
987
988 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );
989
990 // input specific for this class
991 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
992 // inputs - generic
993 QgsFeatureIds mSelectedIds;
994 // outputs
995 PointData outNormal;
996 PointData outSelected;
997};
998
999bool QgsPoint3DBillboardSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent )
1000{
1001 Q_UNUSED( context )
1002 Q_UNUSED( attributeNames )
1003
1004 mChunkOrigin = chunkExtent.center();
1005 mChunkExtent = chunkExtent;
1006
1007 return true;
1008}
1009
1010void QgsPoint3DBillboardSymbolHandler::processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context )
1011{
1012 PointData &out = mSelectedIds.contains( feature.id() ) ? outSelected : outNormal;
1013
1014 if ( feature.geometry().isNull() )
1015 return;
1016
1017 Qgs3DUtils::extractPointPositions( feature, context, mChunkOrigin, mSymbol->altitudeClamping(), out.positions );
1018 mFeatureCount++;
1019}
1020
1021void QgsPoint3DBillboardSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
1022{
1023 makeEntity( parent, context, outNormal, false );
1024 makeEntity( parent, context, outSelected, true );
1025
1026 updateZRangeFromPositions( outNormal.positions );
1027 updateZRangeFromPositions( outSelected.positions );
1028
1029 // the elevation offset is applied externally through a QTransform of QEntity so let's account for it
1030 const float billboardHeight = mSymbol->billboardHeight();
1031 // as we are relative to chunk center elevation we have to add mChunkOrigin.z()
1032 mZMin += static_cast<float>( billboardHeight + mChunkOrigin.z() );
1033 mZMax += static_cast<float>( billboardHeight + mChunkOrigin.z() );
1034}
1035
1036void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected )
1037{
1038 if ( out.positions.isEmpty() )
1039 {
1040 return; // nothing to show - no need to create the entity
1041 }
1042
1043 // Billboard Geometry
1044 QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
1045 billboardGeometry->setPositions( out.positions );
1046
1047 // Billboard Geometry Renderer
1048 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
1049 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::TriangleStrip );
1050 billboardGeometryRenderer->setGeometry( billboardGeometry );
1051 billboardGeometryRenderer->setVertexCount( 4 );
1052 billboardGeometryRenderer->setInstanceCount( out.positions.count() );
1053
1054 // Billboard Material
1056 QgsMarkerSymbol *symbol = mSymbol->billboardSymbol();
1057
1058 if ( symbol )
1059 {
1060 billboardMaterial->setTexture2DFromSymbol( symbol, context, selected );
1061 }
1062 else
1063 {
1064 billboardMaterial->useDefaultSymbol( context, selected );
1065 }
1066
1067 // Billboard Transform
1068 QgsGeoTransform *billboardTransform = new QgsGeoTransform;
1069 billboardTransform->setGeoTranslation( mChunkOrigin + QgsVector3D( 0, 0, mSymbol->billboardHeight() ) );
1070
1071 // Build the entity
1072 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
1073
1074 entity->addComponent( billboardMaterial );
1075 entity->addComponent( billboardTransform );
1076 entity->addComponent( billboardGeometryRenderer );
1077 entity->setParent( parent );
1078
1079 // cppcheck wrongly believes entity will leak
1080 // cppcheck-suppress memleak
1081}
1082
1083
1084namespace Qgs3DSymbolImpl
1085{
1086
1087 QgsFeature3DHandler *handlerForPoint3DSymbol( const QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
1088 {
1089 const QgsPoint3DSymbol *pointSymbol = dynamic_cast<const QgsPoint3DSymbol *>( symbol );
1090 if ( !pointSymbol )
1091 return nullptr;
1092
1093 if ( pointSymbol->shape() == Qgis::Point3DShape::Model )
1094 return new QgsModelPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
1095 // Add proper handler for billboard
1096 else if ( pointSymbol->shape() == Qgis::Point3DShape::Billboard )
1097 return new QgsPoint3DBillboardSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
1098 else
1099 return new QgsInstancedPoint3DSymbolHandler( pointSymbol, layer->selectedFeatureIds() );
1100 }
1101} // namespace Qgs3DSymbolImpl
1102
@ Plane
Flat plane.
Definition qgis.h:4437
@ Cylinder
Cylinder.
Definition qgis.h:4432
@ Torus
Torus.
Definition qgis.h:4436
@ ExtrudedText
Extruded text.
Definition qgis.h:4438
@ Model
Model.
Definition qgis.h:4439
@ Sphere
Sphere.
Definition qgis.h:4433
@ Billboard
Billboard.
Definition qgis.h:4440
@ Geocentric
Geocentric CRS.
Definition qgis.h:2516
QFlags< InstancedMaterialFlag > InstancedMaterialFlags
Definition qgis.h:4488
@ DataDefinedRotation
Per-instance data-defined rotation.
Definition qgis.h:4485
@ DataDefinedScale
Per-instance data-defined scale.
Definition qgis.h:4484
Rendering context for preparation of 3D entities.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
QgsExpressionContext & expressionContext()
Gets the expression context.
static void decomposeTransformMatrix(const QMatrix4x4 &matrix, QVector3D &translation, QQuaternion &rotation, QVector3D &scale)
Tries to decompose a 4x4 transform matrix into translation, rotation and scale components.
static QMatrix4x4 axisTransformMatrix(const QString &upAxis, const QString &forwardAxis)
Computes a 3x3 orientation matrix from the given up and forward axis strings.
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.
static QgsUnlitMaterial * createHighlightMaterial()
Creates a new highlight material, consisting of an unlit material respecting the user's map highlight...
Definition qgs3d.cpp:168
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
Definition qgs3d.cpp:142
Abstract base class for material 3D handlers.
Abstract base class for material settings.
virtual bool requiresTangents() const
Returns true if the material requires tangents generated during triangulation.
virtual QString type() const =0
Returns the unique type name for the material.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
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
QgsVector3D center() const
Returns the center of the box as a vector.
Definition qgsbox3d.cpp:124
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Qgis::CrsType type() const
Returns the type of the CRS.
static QQuaternion ellipsoidEastNorthUpRotation(const QgsVector3D &position, double semiMajorAxis, double semiMinorAxis)
Returns a rotation quaternion for the east-north-up (ENU) reference frame at position on the ellipsoi...
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
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:63
QgsGeometry geometry
Definition qgsfeature.h:66
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.
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.
static QgsMaterialContext fromRenderContext(const Qgs3DRenderContext &context)
Constructs a material context from the settings in a 3D render context.
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.
QgsMarkerSymbol * billboardSymbol() const
Returns a symbol for billboard.
Qgis::Point3DShape shape() const
Returns 3D shape for points.
QgsAbstractMaterialSettings * materialSettings() const override
Returns material settings used for shading of the symbol.
QVariant shapeProperty(const QString &property) const
Returns the value for a specific shape property.
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
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
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QSet< QgsFeatureId > QgsFeatureIds
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
Contains parameters for an ellipsoid.
double semiMajor
Semi-major axis, in meters.
bool valid
Whether ellipsoid parameters are valid.
double semiMinor
Semi-minor axis, in meters.
Pairs a Qt3D geometry with its material and transform.
Definition qgs3dutils.h:71