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>
65using namespace Qt::StringLiterals;
73class QgsInstancedPoint3DSymbolHandler :
public QgsFeature3DHandler
76 QgsInstancedPoint3DSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
77 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
78 , mSelectedIds( selectedIds )
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;
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 );
93 QVector<QVector3D> positions;
94 QVector<QVector3D> scales;
95 QVector<QVector4D> rotations;
98 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
101 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
102 QVector3D mSymbolScale;
103 QQuaternion mSymbolRotation;
104 QVector3D mPointTranslation;
109 PointData outSelected;
111 double mGlobeSemiMajorAxis = -1;
112 double mGlobeSemiMinorAxis = -1;
113 bool mOrientToGlobeSurface =
false;
117bool QgsInstancedPoint3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
119 mChunkOrigin = chunkExtent.
center();
120 mChunkExtent = chunkExtent;
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 );
137 mOrientToGlobeSurface =
true;
146 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
157 if ( hasDDTranslation )
162 translation =
QgsVector3D( translationX, translationY, translationZ );
165 const std::size_t oldSize = out.positions.size();
169 const std::size_t added = out.positions.size() - oldSize;
175 out.scales.resize( out.positions.size() );
176 QVector3D *outScale = out.scales.data() + oldSize;
182 for ( std::size_t i = 0; i < added; ++i )
184 ( *outScale++ ) = QVector3D(
static_cast< float >( scaleX ),
static_cast< float >( scaleY ),
static_cast< float >( scaleZ ) );
191 const bool needsRotation = hasDDRotation || mOrientToGlobeSurface;
193 QQuaternion baseRotation = mSymbolRotation;
197 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
204 baseRotation = QQuaternion::fromEulerAngles(
static_cast< float >( rotationX ),
static_cast< float >( rotationY ),
static_cast< float >( rotationZ ) );
210 out.rotations.resize( out.positions.size() );
211 QVector4D *outRotation = needsRotation ? out.rotations.data() + oldSize :
nullptr;
213 if ( mOrientToGlobeSurface )
215 for ( std::size_t i = 0; i < added; ++i )
219 ( *outRotation++ ) = ( enuRotation * baseRotation ).toVector4D();
222 out.positions[oldSize + i] += enuRotation.rotatedVector( translation.
toVector3D() );
227 const QVector4D r = baseRotation.toVector4D();
228 for ( std::size_t i = 0; i < added; ++i )
231 ( *outRotation++ ) = r;
232 out.positions[oldSize + i] += translation.
toVector3D();
239void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
241 makeEntity( parent, context, outNormal,
false );
242 makeEntity( parent, context, outSelected,
true );
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 ) )
253 switch ( mSymbol->shape() )
257 const float length = mSymbol->shapeProperty( u
"length"_s ).toFloat();
258 minZ -= length * 0.5f;
259 maxZ += length * 0.5f;
265 const float radius = mSymbol->shapeProperty( u
"radius"_s ).toFloat();
273 const float length = mSymbol->shapeProperty( u
"length"_s ).toFloat();
274 minZ -= length * 0.5f;
275 maxZ += length * 0.5f;
281 const float size = mSymbol->shapeProperty( u
"size"_s ).toFloat();
289 const float radius = mSymbol->shapeProperty( u
"radius"_s ).toFloat();
299 const float size = mSymbol->shapeProperty( u
"size"_s ).toFloat();
313 const double zScale = ( *scales++ )[2];
319 minZ += pos.z() + mChunkOrigin.z();
320 maxZ += pos.z() + mChunkOrigin.z();
323 mZMin =
static_cast< float >( minZ );
325 mZMax =
static_cast< float >( maxZ );
329 updateZRangeFromPointData( outNormal );
330 updateZRangeFromPointData( outSelected );
333void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
335 if ( out.positions.isEmpty() )
344 QgsMaterial *mat = material( mSymbol.get(), materialContext, !out.scales.empty(), !out.rotations.empty() );
348 mat->addParameter(
new Qt3DRender::QParameter(
"symbolScale", mSymbolScale, mat ) );
349 mat->addParameter(
new Qt3DRender::QParameter(
"symbolRotation", mSymbolRotation.toVector4D(), mat ) );
352 QgsGeoTransform *tr =
new QgsGeoTransform;
353 tr->setGeoTranslation( mChunkOrigin );
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 );
370 if ( hasDataDefinedScale )
372 if ( hasDataDefinedRotation )
375 const QString upAxis = symbol->
shapeProperty( u
"upAxis"_s ).toString();
376 const QString forwardAxis = symbol->
shapeProperty( u
"forwardAxis"_s ).toString();
378 const QMatrix4x4 meshTransform =
Qgs3DUtils::axisTransformMatrix( !upAxis.isEmpty() ? upAxis : u
"y"_s, !forwardAxis.isEmpty() ? forwardAxis : u
"-z"_s );
383 mat->setInstancingEnabled(
true, flags );
384 mat->setInstancingMeshTransform( meshTransform );
391 return handler->toInstancedMaterial( settings, materialContext, flags, meshTransform );
397Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer(
398 const QgsPoint3DSymbol *symbol,
const QVector<QVector3D> &positions,
const QVector<QVector3D> &scales,
const QVector<QVector4D> rotations
401 const std::size_t count = positions.count();
402 const std::size_t byteCount = positions.count() *
sizeof( QVector3D );
404 ba.resize( byteCount );
405 memcpy( ba.data(), positions.constData(), byteCount );
407 Qt3DCore::QBuffer *instanceBuffer =
new Qt3DCore::QBuffer();
408 instanceBuffer->setData( ba );
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 ) );
421 Qt3DCore::QGeometry *geometry = symbolGeometry( symbol );
422 geometry->addAttribute( instanceTranslationAttribute );
423 geometry->setBoundingVolumePositionAttribute( instanceTranslationAttribute );
425 if ( !scales.empty() )
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 ) );
437 scaleBa.resize( byteCount );
438 memcpy( scaleBa.data(), scales.constData(), byteCount );
440 scaleBuffer->setData( scaleBa );
441 instanceScaleAttribute->setCount( count );
443 instanceScaleAttribute->setBuffer( scaleBuffer );
444 geometry->addAttribute( instanceScaleAttribute );
447 if ( !rotations.empty() )
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 ) );
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 );
466 instanceRotationAttribute->setBuffer( rotationBuffer );
467 geometry->addAttribute( instanceRotationAttribute );
470 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
471 renderer->setGeometry( geometry );
472 renderer->setInstanceCount( count );
477Qt3DCore::QGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry(
const QgsPoint3DSymbol *symbol )
479 switch ( symbol->
shape() )
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;
488 g->setRadius( radius );
489 g->setLength( length );
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();
500 Qt3DExtras::QSphereGeometry *g =
new Qt3DExtras::QSphereGeometry;
501 g->setRadius( radius );
502 g->setRings( rings );
503 g->setSlices( slices );
504 g->setGenerateTangents( tangents );
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();
514 Qt3DExtras::QConeGeometry *g =
new Qt3DExtras::QConeGeometry;
515 g->setLength( length );
516 g->setBottomRadius( bottomRadius );
517 g->setTopRadius( topRadius );
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 );
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 );
545 const float size = symbol->
shapeProperty( u
"size"_s ).toFloat();
546 Qt3DExtras::QPlaneGeometry *g =
new Qt3DExtras::QPlaneGeometry;
548 g->setHeight( size );
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 );
573class QgsModelPoint3DSymbolHandler :
public QgsFeature3DHandler
576 QgsModelPoint3DSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
577 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
578 , mSelectedIds( selectedIds )
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;
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
600 QVector<QVector3D> positions;
601 QVector<QVector3D> scales;
602 QVector<QQuaternion> rotations;
605 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
608 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
610 QVector3D mSymbolScale;
611 QQuaternion mSymbolRotation;
612 QVector3D mPointTranslation;
618 PointData outSelected;
620 double mGlobeSemiMajorAxis = -1;
621 double mGlobeSemiMinorAxis = -1;
622 bool mOrientToGlobeSurface =
false;
625bool QgsModelPoint3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
628 Q_UNUSED( attributeNames )
630 mChunkOrigin = chunkExtent.
center();
631 mChunkExtent = chunkExtent;
633 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
634 attributeNames.unite( attrs );
646 mOrientToGlobeSurface =
true;
655 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
666 if ( hasDDTranslation )
671 translation =
QgsVector3D( translationX, translationY, translationZ );
674 const std::size_t oldSize = out.positions.size();
678 const std::size_t added = out.positions.size() - oldSize;
683 out.scales.resize( out.positions.size() );
684 QVector3D *outScale = out.scales.data() + oldSize;
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;
696 const bool needsRotation = hasDDRotation || mOrientToGlobeSurface;
698 QQuaternion baseRotation = mSymbolRotation;
701 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
705 baseRotation = QQuaternion::fromEulerAngles(
static_cast< float >( rotationX ),
static_cast< float >( rotationY ),
static_cast< float >( rotationZ ) );
709 out.rotations.resize( out.positions.size() );
710 QQuaternion *outRotation = needsRotation ? out.rotations.data() + oldSize :
nullptr;
712 if ( mOrientToGlobeSurface )
714 for ( std::size_t i = 0; i < added; ++i )
718 ( *outRotation++ ) = enuRotation * baseRotation;
721 out.positions[oldSize + i] += enuRotation.rotatedVector( translation.
toVector3D() );
726 for ( std::size_t i = 0; i < added; ++i )
729 ( *outRotation++ ) = baseRotation;
730 out.positions[oldSize + i] += translation.
toVector3D();
737void QgsModelPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
739 makeEntity( parent, context, outNormal,
false );
740 makeEntity( parent, context, outSelected,
true );
742 updateZRangeFromPositions( outNormal.positions );
743 updateZRangeFromPositions( outSelected.positions );
746 const float symbolHeight = mSymbol->transform().data()[14];
748 mZMin +=
static_cast<float>( symbolHeight + mChunkOrigin.z() );
749 mZMax +=
static_cast<float>( symbolHeight + mChunkOrigin.z() );
752void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
754 if ( out.positions.isEmpty() )
760 const bool useEmbeddedTexture = !mSymbol->shapeProperty( u
"overwriteMaterial"_s ).toBool() && ( !settings || settings->
type() ==
"null"_L1 );
766 addInstancedEntities( out.positions, out.scales, out.rotations, mChunkOrigin, mSymbol.get(), parent, materialContext, useEmbeddedTexture );
769void QgsModelPoint3DSymbolHandler::addInstancedEntities(
770 const QVector<QVector3D> &positions,
771 const QVector<QVector3D> &scales,
772 const QVector<QQuaternion> &rotations,
775 Qt3DCore::QEntity *parent,
777 bool useEmbeddedTexture
782 std::vector<QgsMeshNodeData> meshes;
784 const QString suffix = QFileInfo( source ).suffix().toLower();
785 if ( suffix ==
"gltf"_L1 || suffix ==
"glb"_L1 )
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 ) ) );
793 else if ( suffix ==
"obj"_L1 )
795 for (
QgsMeshNodeData &m : QgsObj3DUtils::buildObjGeometries( source, materialContext ) )
796 meshes.push_back( std::move( m ) );
800 QgsDebugError( u
"Unsupported model file suffix '%1' for source: %2"_s.arg( suffix, source ) );
804 if ( meshes.empty() )
806 QgsDebugMsgLevel( u
"No meshes loaded for model symbol source: %1"_s.arg( source ), 2 );
810 const int count = positions.size();
812 QByteArray translationData(
reinterpret_cast<const char *
>( positions.constData() ),
static_cast<qsizetype
>( count *
sizeof( QVector3D ) ) );
814 const QString upAxis = symbol->
shapeProperty( u
"upAxis"_s ).toString();
815 const QString forwardAxis = symbol->
shapeProperty( u
"forwardAxis"_s ).toString();
818 if ( !scales.empty() )
820 if ( !rotations.empty() )
825 Qt3DCore::QBuffer *translationBufferData =
new Qt3DCore::QBuffer( parent );
826 translationBufferData->setData( translationData );
828 Qt3DCore::QBuffer *scaleBufferData =
nullptr;
829 if ( !scales.empty() )
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 );
836 Qt3DCore::QBuffer *rotationBufferData =
nullptr;
837 if ( !rotations.empty() )
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 );
850 Qt3DCore::QGeometry *geom = mesh.geometry.release();
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 );
865 if ( scaleBufferData )
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 );
880 if ( rotationBufferData )
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 );
895 const QMatrix4x4 meshTransformCombined = meshTransform * mesh.meshTransform;
901 highlightMaterial->setInstancingEnabled(
true, instancedFlags );
902 highlightMaterial->setInstancingMeshTransform( meshTransformCombined );
903 mat = highlightMaterial;
905 else if ( useEmbeddedTexture )
907 if ( QgsPhongTexturedMaterial *phongTexMat = qobject_cast<QgsPhongTexturedMaterial *>( mesh.material.get() ) )
909 phongTexMat->setInstancingEnabled(
true, instancedFlags );
910 phongTexMat->setInstancingMeshTransform( meshTransformCombined );
912 else if ( QgsPhongMaterial *phongMat = qobject_cast<QgsPhongMaterial *>( mesh.material.get() ) )
914 phongMat->setInstancingEnabled(
true, instancedFlags );
915 phongMat->setInstancingMeshTransform( meshTransformCombined );
917 else if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( mesh.material.get() ) )
919 pbrMat->setInstancingEnabled(
true, instancedFlags );
920 pbrMat->setInstancingMeshTransform( meshTransformCombined );
922 else if ( QgsTextureMaterial *gltfTexMat = qobject_cast<QgsTextureMaterial *>( mesh.material.get() ) )
924 gltfTexMat->setInstancingEnabled(
true, instancedFlags );
925 gltfTexMat->setInstancingMeshTransform( meshTransformCombined );
927 mat = mesh.material.release();
933 mat = handler->toInstancedMaterial( settings, materialContext, instancedFlags, meshTransformCombined );
938 QgsMetalRoughMaterial *metal =
new QgsMetalRoughMaterial();
939 metal->setInstancingEnabled(
true, instancedFlags );
940 metal->setInstancingMeshTransform( meshTransformCombined );
944 mat->addParameter(
new Qt3DRender::QParameter(
"symbolScale", mSymbolScale, mat ) );
945 mat->addParameter(
new Qt3DRender::QParameter(
"symbolRotation", mSymbolRotation.toVector4D(), mat ) );
947 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
948 renderer->setGeometry( geom );
949 renderer->setInstanceCount(
static_cast<int>( count ) );
951 QgsGeoTransform *geoTransform =
new QgsGeoTransform;
952 geoTransform->setGeoTranslation( chunkOrigin );
954 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
955 entity->addComponent( renderer );
956 entity->addComponent( mat );
957 entity->addComponent( geoTransform );
958 entity->setParent( parent );
969class QgsPoint3DBillboardSymbolHandler :
public QgsFeature3DHandler
972 QgsPoint3DBillboardSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
973 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
974 , mSelectedIds( selectedIds )
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;
985 QVector<QVector3D> positions;
988 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
991 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
996 PointData outSelected;
999bool QgsPoint3DBillboardSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
1002 Q_UNUSED( attributeNames )
1004 mChunkOrigin = chunkExtent.
center();
1005 mChunkExtent = chunkExtent;
1012 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
1021void QgsPoint3DBillboardSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
1023 makeEntity( parent, context, outNormal,
false );
1024 makeEntity( parent, context, outSelected,
true );
1026 updateZRangeFromPositions( outNormal.positions );
1027 updateZRangeFromPositions( outSelected.positions );
1030 const float billboardHeight = mSymbol->billboardHeight();
1032 mZMin +=
static_cast<float>( billboardHeight + mChunkOrigin.z() );
1033 mZMax +=
static_cast<float>( billboardHeight + mChunkOrigin.z() );
1036void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
1038 if ( out.positions.isEmpty() )
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() );
1068 QgsGeoTransform *billboardTransform =
new QgsGeoTransform;
1069 billboardTransform->setGeoTranslation( mChunkOrigin +
QgsVector3D( 0, 0, mSymbol->billboardHeight() ) );
1072 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
1074 entity->addComponent( billboardMaterial );
1075 entity->addComponent( billboardTransform );
1076 entity->addComponent( billboardGeometryRenderer );
1077 entity->setParent( parent );
1084namespace Qgs3DSymbolImpl
1087 QgsFeature3DHandler *handlerForPoint3DSymbol(
const QgsVectorLayer *layer,
const QgsAbstract3DSymbol *symbol )
1089 const QgsPoint3DSymbol *pointSymbol =
dynamic_cast<const QgsPoint3DSymbol *
>( symbol );
1097 return new QgsPoint3DBillboardSymbolHandler( pointSymbol, layer->
selectedFeatureIds() );
1099 return new QgsInstancedPoint3DSymbolHandler( pointSymbol, layer->
selectedFeatureIds() );
@ ExtrudedText
Extruded text.
@ Geocentric
Geocentric CRS.
QFlags< InstancedMaterialFlag > InstancedMaterialFlags
@ DataDefinedRotation
Per-instance data-defined rotation.
@ DataDefinedScale
Per-instance data-defined scale.
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...
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
@ RotationX
X-axis rotation.
@ TranslationY
Y-axis translation.
@ RotationY
Y-axis rotation.
@ TranslationZ
Z-axis translation.
@ TranslationX
X-axis translation.
@ RotationZ
Z-axis rotation.
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.
QgsVector3D center() const
Returns the center of the box as a vector.
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...
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.
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...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QSet< QgsFeatureId > QgsFeatureIds
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
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.