46#include <Qt3DCore/QAttribute>
47#include <Qt3DCore/QBuffer>
48#include <Qt3DCore/QGeometry>
49#include <Qt3DExtras/QConeGeometry>
50#include <Qt3DExtras/QCuboidGeometry>
51#include <Qt3DExtras/QCylinderGeometry>
52#include <Qt3DExtras/QExtrudedTextGeometry>
53#include <Qt3DExtras/QPhongMaterial>
54#include <Qt3DExtras/QPlaneGeometry>
55#include <Qt3DExtras/QSphereGeometry>
56#include <Qt3DExtras/QTorusGeometry>
57#include <Qt3DRender/QEffect>
58#include <Qt3DRender/QGeometryRenderer>
59#include <Qt3DRender/QGraphicsApiFilter>
60#include <Qt3DRender/QPaintedTextureImage>
61#include <Qt3DRender/QParameter>
62#include <Qt3DRender/QTechnique>
64using namespace Qt::StringLiterals;
72class QgsInstancedPoint3DSymbolHandler :
public QgsFeature3DHandler
75 QgsInstancedPoint3DSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
76 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
77 , mSelectedIds( selectedIds )
80 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
81 void processFeature(
const QgsFeature &feature,
const Qgs3DRenderContext &context )
override;
82 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
85 static QgsMaterial *material(
const QgsPoint3DSymbol *symbol,
const QgsMaterialContext &materialContext,
bool hasDataDefinedScale,
bool hasDataDefinedRotation );
86 static Qt3DRender::QGeometryRenderer *renderer(
const QgsPoint3DSymbol *symbol,
const QVector<QVector3D> &positions,
const QVector<QVector3D> &scales,
const QVector<QVector4D> rotations );
87 static Qt3DCore::QGeometry *symbolGeometry(
const QgsPoint3DSymbol *symbol );
92 QVector<QVector3D> positions;
93 QVector<QVector3D> scales;
94 QVector<QVector4D> rotations;
97 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
100 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
101 QVector3D mSymbolScale;
102 QQuaternion mSymbolRotation;
103 QVector3D mPointTranslation;
108 PointData outSelected;
112bool QgsInstancedPoint3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
114 mChunkOrigin = chunkExtent.
center();
115 mChunkExtent = chunkExtent;
117 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
118 attributeNames.unite( attrs );
119 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.
expressionContext() );
120 attributeNames.unite( attrs );
129 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
140 if ( hasDDTranslation )
145 translation =
QgsVector3D( translationX, translationY, translationZ );
148 const std::size_t oldSize = out.positions.size();
151 const std::size_t added = out.positions.size() - oldSize;
157 out.scales.resize( out.positions.size() );
158 QVector3D *outScale = out.scales.data() + oldSize;
164 for ( std::size_t i = 0; i < added; ++i )
166 ( *outScale++ ) = QVector3D(
static_cast< float >( scaleX ),
static_cast< float >( scaleY ),
static_cast< float >( scaleZ ) );
175 out.rotations.resize( out.positions.size() );
176 QVector4D *outRotation = out.rotations.data() + oldSize;
179 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
186 const QQuaternion finalQuat = QQuaternion::fromEulerAngles(
static_cast< float >( rotationX ),
static_cast< float >( rotationY ),
static_cast< float >( rotationZ ) );
187 const QVector4D finalVec4 = finalQuat.toVector4D();
189 for ( std::size_t i = 0; i < added; ++i )
191 ( *outRotation++ ) = finalVec4;
198void QgsInstancedPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
200 makeEntity( parent, context, outNormal,
false );
201 makeEntity( parent, context, outSelected,
true );
203 auto updateZRangeFromPointData = [
this](
const PointData &pointData ) {
204 const QVector3D *scales = pointData.scales.empty() ? nullptr : pointData.scales.constData();
205 for (
const QVector3D &pos : std::as_const( pointData.positions ) )
212 switch ( mSymbol->shape() )
216 const float length = mSymbol->shapeProperty( u
"length"_s ).toFloat();
217 minZ -= length * 0.5f;
218 maxZ += length * 0.5f;
224 const float radius = mSymbol->shapeProperty( u
"radius"_s ).toFloat();
232 const float length = mSymbol->shapeProperty( u
"length"_s ).toFloat();
233 minZ -= length * 0.5f;
234 maxZ += length * 0.5f;
240 const float size = mSymbol->shapeProperty( u
"size"_s ).toFloat();
248 const float radius = mSymbol->shapeProperty( u
"radius"_s ).toFloat();
258 const float size = mSymbol->shapeProperty( u
"size"_s ).toFloat();
272 const double zScale = ( *scales++ )[2];
278 minZ += pos.z() + mChunkOrigin.z();
279 maxZ += pos.z() + mChunkOrigin.z();
282 mZMin =
static_cast< float >( minZ );
284 mZMax =
static_cast< float >( maxZ );
288 updateZRangeFromPointData( outNormal );
289 updateZRangeFromPointData( outSelected );
292void QgsInstancedPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
294 if ( out.positions.isEmpty() )
303 QgsMaterial *mat = material( mSymbol.get(), materialContext, !out.scales.empty(), !out.rotations.empty() );
307 mat->addParameter(
new Qt3DRender::QParameter(
"symbolScale", mSymbolScale, mat ) );
308 mat->addParameter(
new Qt3DRender::QParameter(
"symbolRotation", mSymbolRotation.toVector4D(), mat ) );
311 QgsGeoTransform *tr =
new QgsGeoTransform;
312 tr->setGeoTranslation( mChunkOrigin );
315 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
316 entity->addComponent( renderer( mSymbol.get(), out.positions, out.scales, out.rotations ) );
317 entity->addComponent( mat );
318 entity->addComponent( tr );
319 entity->setParent( parent );
329 if ( hasDataDefinedScale )
331 if ( hasDataDefinedRotation )
334 const QString upAxis = symbol->
shapeProperty( u
"upAxis"_s ).toString();
335 const QString forwardAxis = symbol->
shapeProperty( u
"forwardAxis"_s ).toString();
337 const QMatrix4x4 meshTransform =
Qgs3DUtils::axisTransformMatrix( !upAxis.isEmpty() ? upAxis : u
"y"_s, !forwardAxis.isEmpty() ? forwardAxis : u
"-z"_s );
341 QgsHighlightMaterial *mat =
new QgsHighlightMaterial();
342 mat->setInstancingEnabled(
true, flags );
343 mat->setInstancingMeshTransform( meshTransform );
350 return handler->toInstancedMaterial( settings, materialContext, flags, meshTransform );
356Qt3DRender::QGeometryRenderer *QgsInstancedPoint3DSymbolHandler::renderer(
357 const QgsPoint3DSymbol *symbol,
const QVector<QVector3D> &positions,
const QVector<QVector3D> &scales,
const QVector<QVector4D> rotations
360 const std::size_t count = positions.count();
361 const std::size_t byteCount = positions.count() *
sizeof( QVector3D );
363 ba.resize( byteCount );
364 memcpy( ba.data(), positions.constData(), byteCount );
366 Qt3DCore::QBuffer *instanceBuffer =
new Qt3DCore::QBuffer();
367 instanceBuffer->setData( ba );
369 Qt3DCore::QAttribute *instanceTranslationAttribute =
new Qt3DCore::QAttribute;
370 instanceTranslationAttribute->setName( u
"instanceTranslation"_s );
371 instanceTranslationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
372 instanceTranslationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
373 instanceTranslationAttribute->setVertexSize( 3 );
374 instanceTranslationAttribute->setByteOffset( 0 );
375 instanceTranslationAttribute->setDivisor( 1 );
376 instanceTranslationAttribute->setBuffer( instanceBuffer );
377 instanceTranslationAttribute->setCount( count );
378 instanceTranslationAttribute->setByteStride( 3 *
sizeof(
float ) );
380 Qt3DCore::QGeometry *geometry = symbolGeometry( symbol );
381 geometry->addAttribute( instanceTranslationAttribute );
382 geometry->setBoundingVolumePositionAttribute( instanceTranslationAttribute );
384 if ( !scales.empty() )
386 auto scaleBuffer =
new Qt3DCore::QBuffer();
387 auto instanceScaleAttribute =
new Qt3DCore::QAttribute;
388 instanceScaleAttribute->setName( u
"instanceScale"_s );
389 instanceScaleAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
390 instanceScaleAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
391 instanceScaleAttribute->setVertexSize( 3 );
392 instanceScaleAttribute->setByteOffset( 0 );
393 instanceScaleAttribute->setDivisor( 1 );
394 instanceScaleAttribute->setByteStride( 3 *
sizeof(
float ) );
396 scaleBa.resize( byteCount );
397 memcpy( scaleBa.data(), scales.constData(), byteCount );
399 scaleBuffer->setData( scaleBa );
400 instanceScaleAttribute->setCount( count );
402 instanceScaleAttribute->setBuffer( scaleBuffer );
403 geometry->addAttribute( instanceScaleAttribute );
406 if ( !rotations.empty() )
408 auto rotationBuffer =
new Qt3DCore::QBuffer();
409 auto instanceRotationAttribute =
new Qt3DCore::QAttribute;
410 instanceRotationAttribute->setName( u
"instanceRotation"_s );
411 instanceRotationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
412 instanceRotationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
413 instanceRotationAttribute->setVertexSize( 4 );
414 instanceRotationAttribute->setByteOffset( 0 );
415 instanceRotationAttribute->setDivisor( 1 );
416 instanceRotationAttribute->setByteStride( 4 *
sizeof(
float ) );
418 QByteArray rotationBa;
419 const std::size_t rotationByteCount = positions.count() *
sizeof( QVector4D );
420 rotationBa.resize( rotationByteCount );
421 memcpy( rotationBa.data(), rotations.constData(), rotationByteCount );
422 rotationBuffer->setData( rotationBa );
423 instanceRotationAttribute->setCount( count );
425 instanceRotationAttribute->setBuffer( rotationBuffer );
426 geometry->addAttribute( instanceRotationAttribute );
429 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
430 renderer->setGeometry( geometry );
431 renderer->setInstanceCount( count );
436Qt3DCore::QGeometry *QgsInstancedPoint3DSymbolHandler::symbolGeometry(
const QgsPoint3DSymbol *symbol )
438 switch ( symbol->
shape() )
442 const float radius = symbol->
shapeProperty( u
"radius"_s ).toFloat();
443 const float length = symbol->
shapeProperty( u
"length"_s ).toFloat();
444 Qt3DExtras::QCylinderGeometry *g =
new Qt3DExtras::QCylinderGeometry;
447 g->setRadius( radius );
448 g->setLength( length );
454 const float radius = symbol->
shapeProperty( u
"radius"_s ).toFloat();
455 const int rings = symbol->
shapeProperty( u
"rings"_s ).toInt();
456 const int slices = symbol->
shapeProperty( u
"slices"_s ).toInt();
459 Qt3DExtras::QSphereGeometry *g =
new Qt3DExtras::QSphereGeometry;
460 g->setRadius( radius );
461 g->setRings( rings );
462 g->setSlices( slices );
463 g->setGenerateTangents( tangents );
469 const float length = symbol->
shapeProperty( u
"length"_s ).toFloat();
470 const float bottomRadius = symbol->
shapeProperty( u
"bottomRadius"_s ).toFloat();
471 const float topRadius = symbol->
shapeProperty( u
"topRadius"_s ).toFloat();
473 Qt3DExtras::QConeGeometry *g =
new Qt3DExtras::QConeGeometry;
474 g->setLength( length );
475 g->setBottomRadius( bottomRadius );
476 g->setTopRadius( topRadius );
484 const float size = symbol->
shapeProperty( u
"size"_s ).toFloat();
485 Qt3DExtras::QCuboidGeometry *g =
new Qt3DExtras::QCuboidGeometry;
486 g->setXExtent( size );
487 g->setYExtent( size );
488 g->setZExtent( size );
494 const float radius = symbol->
shapeProperty( u
"radius"_s ).toFloat();
495 const float minorRadius = symbol->
shapeProperty( u
"minorRadius"_s ).toFloat();
496 Qt3DExtras::QTorusGeometry *g =
new Qt3DExtras::QTorusGeometry;
497 g->setRadius( radius );
498 g->setMinorRadius( minorRadius );
504 const float size = symbol->
shapeProperty( u
"size"_s ).toFloat();
505 Qt3DExtras::QPlaneGeometry *g =
new Qt3DExtras::QPlaneGeometry;
507 g->setHeight( size );
513 const float depth = symbol->
shapeProperty( u
"depth"_s ).toFloat();
514 const QString text = symbol->
shapeProperty( u
"text"_s ).toString();
515 Qt3DExtras::QExtrudedTextGeometry *g =
new Qt3DExtras::QExtrudedTextGeometry;
516 g->setDepth( depth );
532class QgsModelPoint3DSymbolHandler :
public QgsFeature3DHandler
535 QgsModelPoint3DSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
536 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
537 , mSelectedIds( selectedIds )
540 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
541 void processFeature(
const QgsFeature &feature,
const Qgs3DRenderContext &context )
override;
542 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
545 void addInstancedEntities(
546 const QVector<QVector3D> &positions,
547 const QVector<QVector3D> &scales,
548 const QVector<QQuaternion> &rotations,
549 const QgsVector3D &chunkOrigin,
550 const QgsPoint3DSymbol *symbol,
551 Qt3DCore::QEntity *parent,
552 const QgsMaterialContext &materialContext,
553 bool useEmbeddedTexture
559 QVector<QVector3D> positions;
560 QVector<QVector3D> scales;
561 QVector<QQuaternion> rotations;
564 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
567 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
569 QVector3D mSymbolScale;
570 QQuaternion mSymbolRotation;
571 QVector3D mPointTranslation;
577 PointData outSelected;
580bool QgsModelPoint3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
583 Q_UNUSED( attributeNames )
585 mChunkOrigin = chunkExtent.
center();
586 mChunkExtent = chunkExtent;
588 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
589 attributeNames.unite( attrs );
597 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
608 if ( hasDDTranslation )
613 translation =
QgsVector3D( translationX, translationY, translationZ );
616 const std::size_t oldSize = out.positions.size();
618 const std::size_t added = out.positions.size() - oldSize;
623 out.scales.resize( out.positions.size() );
624 QVector3D *outScale = out.scales.data() + oldSize;
628 const QVector3D scale(
static_cast< float >( scaleX ),
static_cast< float >( scaleY ),
static_cast< float >( scaleZ ) );
629 for ( std::size_t i = 0; i < added; ++i )
630 ( *outScale++ ) = scale;
638 out.rotations.resize( out.positions.size() );
639 QQuaternion *outRotation = out.rotations.data() + oldSize;
640 const QVector3D baseEuler = mSymbolRotation.toEulerAngles();
644 const QQuaternion rotation = QQuaternion::fromEulerAngles(
static_cast< float >( rotationX ),
static_cast< float >( rotationY ),
static_cast< float >( rotationZ ) );
645 for ( std::size_t i = 0; i < added; ++i )
646 ( *outRotation++ ) = rotation;
652void QgsModelPoint3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
654 makeEntity( parent, context, outNormal,
false );
655 makeEntity( parent, context, outSelected,
true );
657 updateZRangeFromPositions( outNormal.positions );
658 updateZRangeFromPositions( outSelected.positions );
661 const float symbolHeight = mSymbol->transform().data()[14];
663 mZMin +=
static_cast<float>( symbolHeight + mChunkOrigin.z() );
664 mZMax +=
static_cast<float>( symbolHeight + mChunkOrigin.z() );
667void QgsModelPoint3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
669 if ( out.positions.isEmpty() )
675 const bool useEmbeddedTexture = !mSymbol->shapeProperty( u
"overwriteMaterial"_s ).toBool() && ( !settings || settings->
type() ==
"null"_L1 );
681 addInstancedEntities( out.positions, out.scales, out.rotations, mChunkOrigin, mSymbol.get(), parent, materialContext, useEmbeddedTexture );
684void QgsModelPoint3DSymbolHandler::addInstancedEntities(
685 const QVector<QVector3D> &positions,
686 const QVector<QVector3D> &scales,
687 const QVector<QQuaternion> &rotations,
690 Qt3DCore::QEntity *parent,
692 bool useEmbeddedTexture
697 std::vector<QgsMeshNodeData> meshes;
699 const QString suffix = QFileInfo( source ).suffix().toLower();
700 if ( suffix ==
"gltf"_L1 || suffix ==
"glb"_L1 )
702 QStringList gltfErrors;
703 for (
QgsMeshNodeData &m : QgsGltf3DUtils::buildGltfGeometries( source, materialContext, &gltfErrors, parent ) )
704 meshes.push_back( std::move( m ) );
705 if ( !gltfErrors.isEmpty() )
706 QgsDebugError( u
"GLTF instancing errors for '%1': %2"_s.arg( source, gltfErrors.join(
", "_L1 ) ) );
708 else if ( suffix ==
"obj"_L1 )
710 for (
QgsMeshNodeData &m : QgsObj3DUtils::buildObjGeometries( source, materialContext ) )
711 meshes.push_back( std::move( m ) );
715 QgsDebugError( u
"Unsupported model file suffix '%1' for source: %2"_s.arg( suffix, source ) );
719 if ( meshes.empty() )
721 QgsDebugMsgLevel( u
"No meshes loaded for model symbol source: %1"_s.arg( source ), 2 );
725 const int count = positions.size();
727 QByteArray translationData(
reinterpret_cast<const char *
>( positions.constData() ),
static_cast<qsizetype
>( count *
sizeof( QVector3D ) ) );
729 const QString upAxis = symbol->
shapeProperty( u
"upAxis"_s ).toString();
730 const QString forwardAxis = symbol->
shapeProperty( u
"forwardAxis"_s ).toString();
733 if ( !scales.empty() )
735 if ( !rotations.empty() )
740 Qt3DCore::QBuffer *translationBufferData =
new Qt3DCore::QBuffer( parent );
741 translationBufferData->setData( translationData );
743 Qt3DCore::QBuffer *scaleBufferData =
nullptr;
744 if ( !scales.empty() )
746 QByteArray scaleData(
reinterpret_cast<const char *
>( scales.constData() ),
static_cast<qsizetype
>( count *
sizeof( QVector3D ) ) );
747 scaleBufferData =
new Qt3DCore::QBuffer( parent );
748 scaleBufferData->setData( scaleData );
751 Qt3DCore::QBuffer *rotationBufferData =
nullptr;
752 if ( !rotations.empty() )
754 QVector<QVector4D> rotationVectors;
755 rotationVectors.reserve( count );
756 for (
const QQuaternion &q : rotations )
757 rotationVectors.append( q.toVector4D() );
758 QByteArray rotationData(
reinterpret_cast<const char *
>( rotationVectors.constData() ),
static_cast<qsizetype
>( count *
sizeof( QVector4D ) ) );
759 rotationBufferData =
new Qt3DCore::QBuffer( parent );
760 rotationBufferData->setData( rotationData );
765 Qt3DCore::QGeometry *geom = mesh.geometry.release();
767 Qt3DCore::QAttribute *translationAttribute =
new Qt3DCore::QAttribute;
768 translationAttribute->setName( u
"instanceTranslation"_s );
769 translationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
770 translationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
771 translationAttribute->setVertexSize( 3 );
772 translationAttribute->setByteOffset( 0 );
773 translationAttribute->setByteStride( 3 *
sizeof(
float ) );
774 translationAttribute->setDivisor( 1 );
775 translationAttribute->setCount(
static_cast<uint
>( count ) );
776 translationAttribute->setBuffer( translationBufferData );
777 geom->addAttribute( translationAttribute );
778 geom->setBoundingVolumePositionAttribute( translationAttribute );
780 if ( scaleBufferData )
782 Qt3DCore::QAttribute *scaleAttribute =
new Qt3DCore::QAttribute;
783 scaleAttribute->setName( u
"instanceScale"_s );
784 scaleAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
785 scaleAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
786 scaleAttribute->setVertexSize( 3 );
787 scaleAttribute->setByteOffset( 0 );
788 scaleAttribute->setByteStride( 3 *
sizeof(
float ) );
789 scaleAttribute->setDivisor( 1 );
790 scaleAttribute->setCount(
static_cast<uint
>( count ) );
791 scaleAttribute->setBuffer( scaleBufferData );
792 geom->addAttribute( scaleAttribute );
795 if ( rotationBufferData )
797 Qt3DCore::QAttribute *rotationAttribute =
new Qt3DCore::QAttribute;
798 rotationAttribute->setName( u
"instanceRotation"_s );
799 rotationAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
800 rotationAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
801 rotationAttribute->setVertexSize( 4 );
802 rotationAttribute->setByteOffset( 0 );
803 rotationAttribute->setByteStride( 4 *
sizeof(
float ) );
804 rotationAttribute->setDivisor( 1 );
805 rotationAttribute->setCount(
static_cast<uint
>( count ) );
806 rotationAttribute->setBuffer( rotationBufferData );
807 geom->addAttribute( rotationAttribute );
810 const QMatrix4x4 meshTransformCombined = meshTransform * mesh.meshTransform;
815 QgsHighlightMaterial *highlightMaterial =
new QgsHighlightMaterial();
816 highlightMaterial->setInstancingEnabled(
true, instancedFlags );
817 highlightMaterial->setInstancingMeshTransform( meshTransformCombined );
818 mat = highlightMaterial;
820 else if ( useEmbeddedTexture )
822 if ( QgsPhongTexturedMaterial *phongTexMat = qobject_cast<QgsPhongTexturedMaterial *>( mesh.material.get() ) )
824 phongTexMat->setInstancingEnabled(
true, instancedFlags );
825 phongTexMat->setInstancingMeshTransform( meshTransformCombined );
827 else if ( QgsPhongMaterial *phongMat = qobject_cast<QgsPhongMaterial *>( mesh.material.get() ) )
829 phongMat->setInstancingEnabled(
true, instancedFlags );
830 phongMat->setInstancingMeshTransform( meshTransformCombined );
832 else if ( QgsMetalRoughMaterial *pbrMat = qobject_cast<QgsMetalRoughMaterial *>( mesh.material.get() ) )
834 pbrMat->setInstancingEnabled(
true, instancedFlags );
835 pbrMat->setInstancingMeshTransform( meshTransformCombined );
837 else if ( QgsTextureMaterial *gltfTexMat = qobject_cast<QgsTextureMaterial *>( mesh.material.get() ) )
839 gltfTexMat->setInstancingEnabled(
true, instancedFlags );
840 gltfTexMat->setInstancingMeshTransform( meshTransformCombined );
842 mat = mesh.material.release();
848 mat = handler->toInstancedMaterial( settings, materialContext, instancedFlags, meshTransformCombined );
853 QgsMetalRoughMaterial *metal =
new QgsMetalRoughMaterial();
854 metal->setInstancingEnabled(
true, instancedFlags );
855 metal->setInstancingMeshTransform( meshTransformCombined );
859 mat->addParameter(
new Qt3DRender::QParameter(
"symbolScale", mSymbolScale, mat ) );
860 mat->addParameter(
new Qt3DRender::QParameter(
"symbolRotation", mSymbolRotation.toVector4D(), mat ) );
862 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
863 renderer->setGeometry( geom );
864 renderer->setInstanceCount(
static_cast<int>( count ) );
866 QgsGeoTransform *geoTransform =
new QgsGeoTransform;
867 geoTransform->setGeoTranslation( chunkOrigin );
869 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
870 entity->addComponent( renderer );
871 entity->addComponent( mat );
872 entity->addComponent( geoTransform );
873 entity->setParent( parent );
884class QgsPoint3DBillboardSymbolHandler :
public QgsFeature3DHandler
887 QgsPoint3DBillboardSymbolHandler(
const QgsPoint3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
888 : mSymbol( static_cast<QgsPoint3DSymbol *>( symbol->clone() ) )
889 , mSelectedIds( selectedIds )
892 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
893 void processFeature(
const QgsFeature &feature,
const Qgs3DRenderContext &context )
override;
894 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
900 QVector<QVector3D> positions;
903 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected );
906 std::unique_ptr<QgsPoint3DSymbol> mSymbol;
911 PointData outSelected;
914bool QgsPoint3DBillboardSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
917 Q_UNUSED( attributeNames )
919 mChunkOrigin = chunkExtent.
center();
920 mChunkExtent = chunkExtent;
927 PointData &out = mSelectedIds.contains( feature.
id() ) ? outSelected : outNormal;
936void QgsPoint3DBillboardSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
938 makeEntity( parent, context, outNormal,
false );
939 makeEntity( parent, context, outSelected,
true );
941 updateZRangeFromPositions( outNormal.positions );
942 updateZRangeFromPositions( outSelected.positions );
945 const float billboardHeight = mSymbol->billboardHeight();
947 mZMin +=
static_cast<float>( billboardHeight + mChunkOrigin.z() );
948 mZMax +=
static_cast<float>( billboardHeight + mChunkOrigin.z() );
951void QgsPoint3DBillboardSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PointData &out,
bool selected )
953 if ( out.positions.isEmpty() )
963 Qt3DRender::QGeometryRenderer *billboardGeometryRenderer =
new Qt3DRender::QGeometryRenderer;
964 billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
965 billboardGeometryRenderer->setGeometry( billboardGeometry );
966 billboardGeometryRenderer->setVertexCount( billboardGeometry->
count() );
982 QgsGeoTransform *billboardTransform =
new QgsGeoTransform;
983 billboardTransform->setGeoTranslation( mChunkOrigin +
QgsVector3D( 0, 0, mSymbol->billboardHeight() ) );
986 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
988 entity->addComponent( billboardMaterial );
989 entity->addComponent( billboardTransform );
990 entity->addComponent( billboardGeometryRenderer );
991 entity->setParent( parent );
998namespace Qgs3DSymbolImpl
1001 QgsFeature3DHandler *handlerForPoint3DSymbol(
const QgsVectorLayer *layer,
const QgsAbstract3DSymbol *symbol )
1003 const QgsPoint3DSymbol *pointSymbol =
dynamic_cast<const QgsPoint3DSymbol *
>( symbol );
1011 return new QgsPoint3DBillboardSymbolHandler( pointSymbol, layer->
selectedFeatureIds() );
1013 return new QgsInstancedPoint3DSymbolHandler( pointSymbol, layer->
selectedFeatureIds() );
@ ExtrudedText
Extruded text.
QFlags< InstancedMaterialFlag > InstancedMaterialFlags
@ DataDefinedRotation
Per-instance data-defined rotation.
@ DataDefinedScale
Per-instance data-defined scale.
Rendering context for preparation of 3D entities.
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 void extractPointPositions(const QgsFeature &f, const Qgs3DRenderContext &context, const QgsVector3D &chunkOrigin, Qgis::AltitudeClamping altClamp, QVector< QVector3D > &positions, const QgsVector3D &translation=QgsVector3D(0, 0, 0))
Calculates (x,y,z) positions of (multi)point from the given feature.
static QMatrix4x4 axisTransformMatrix(const QString &upAxis, const QString &forwardAxis)
Computes a 3x3 orientation matrix from the given up and forward axis strings.
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.
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.
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)
Pairs a Qt3D geometry with its material and transform.