41#include <Qt3DCore/QBuffer>
42#include <Qt3DRender/QAbstractTextureImage>
43#include <Qt3DRender/QCullFace>
44#include <Qt3DRender/QEffect>
45#include <Qt3DRender/QGeometryRenderer>
46#include <Qt3DRender/QTechnique>
47#include <Qt3DRender/QTexture>
49using namespace Qt::StringLiterals;
54class QgsPolygon3DSymbolHandler :
public QgsFeature3DHandler
57 QgsPolygon3DSymbolHandler(
const QgsPolygon3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
58 : mSymbol( static_cast<QgsPolygon3DSymbol *>( symbol->clone() ) )
59 , mSelectedIds( selectedIds )
62 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
63 void processFeature(
const QgsFeature &f,
const Qgs3DRenderContext &context )
override;
64 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
70 std::unique_ptr<QgsTessellator> tessellator;
71 QVector<QgsFeatureId> triangleIndexFids;
72 QVector<uint> triangleIndexStartingIndices;
73 QByteArray materialDataDefined;
74 QVector<float> ddTextureTransformData;
78 const QgsPolygon *poly,
81 float extrusionHeight,
82 float dataDefinedTextureScale,
83 float dataDefinedTextureRotation,
84 float dataDefinedTextureOffsetX,
85 float dataDefinedTextureOffsetY,
86 const Qgs3DRenderContext &context,
89 void processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, PolygonData &out );
90 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &out,
bool selected );
91 QgsMaterial *material(
const QgsPolygon3DSymbol *symbol,
bool isSelected,
const Qgs3DRenderContext &context )
const;
94 std::unique_ptr<QgsPolygon3DSymbol> mSymbol;
98 PolygonData outNormal;
99 PolygonData outSelected;
101 QgsLineVertexData outEdges;
104 bool mWasClippedToExtent =
false;
108bool QgsPolygon3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
110 mChunkOrigin = chunkExtent.
center();
111 mChunkOrigin.
setZ( 0. );
112 mChunkExtent = chunkExtent;
114 outEdges.withAdjacency =
true;
115 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context, mChunkOrigin );
121 auto tessellator = std::make_unique<QgsTessellator>();
122 tessellator->setOrigin( mChunkOrigin );
123 tessellator->setAddNormals(
true );
124 tessellator->setInvertNormals( mSymbol->invertNormals() );
125 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
126 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
127 tessellator->setAddTextureUVs( requiresTextureCoordinates );
128 tessellator->setAddTangents( requiresTangents );
131 outNormal.tessellator = std::move( tessellator );
133 tessellator = std::make_unique<QgsTessellator>();
134 tessellator->setOrigin( mChunkOrigin );
135 tessellator->setAddNormals(
true );
136 tessellator->setInvertNormals( mSymbol->invertNormals() );
137 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
138 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
139 tessellator->setAddTextureUVs( requiresTextureCoordinates );
140 tessellator->setAddTangents( requiresTangents );
143 outSelected.tessellator = std::move( tessellator );
145 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
146 attributeNames.unite( attrs );
148 attributeNames.unite( attrs );
152void QgsPolygon3DSymbolHandler::processPolygon(
156 float extrusionHeight,
157 float dataDefinedTextureScale,
158 float dataDefinedTextureRotation,
159 float dataDefinedTextureOffsetX,
160 float dataDefinedTextureOffsetY,
165 std::unique_ptr<QgsPolygon> polyClone( poly->
clone() );
167 if ( mSymbol->edgesEnabled() )
176 if ( mWasClippedToExtent )
181 const QVector< QgsPointXY > extentPoints {
182 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() },
183 { mChunkExtent.xMaximum(), mChunkExtent.yMinimum() },
184 { mChunkExtent.xMaximum(), mChunkExtent.yMaximum() },
185 { mChunkExtent.xMinimum(), mChunkExtent.yMaximum() },
186 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() }
188 auto extentLinestring = std::make_unique<QgsLineString>( extentPoints );
192 cleanedExteriorRingGeometry = exteriorRingGeometry.
difference(
QgsGeometry( extentLinestring.release() ) );
200 outEdges.addLineString( *line, offset );
205 for (
int i = 0; i < mline->numGeometries(); ++i )
208 outEdges.addLineString( *line, offset );
214 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
215 outEdges.addLineString( *
static_cast<const QgsLineString *
>( polyClone->interiorRing( i ) ), offset );
217 if ( extrusionHeight != 0.f )
223 outEdges.addLineString( *line, extrusionHeight + offset );
224 outEdges.addVerticalLines( *line, extrusionHeight, offset );
229 for (
int i = 0; i < mline->numGeometries(); ++i )
232 outEdges.addLineString( *line, extrusionHeight + offset );
233 outEdges.addVerticalLines( *line, extrusionHeight, offset );
239 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
242 outEdges.addLineString( *interior, extrusionHeight + offset );
243 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
250 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
251 const uint startingTriangleIndex =
static_cast<uint
>( out.tessellator->dataVerticesCount() / 3 );
252 out.triangleIndexStartingIndices.append( startingTriangleIndex );
253 out.triangleIndexFids.append( fid );
255 const size_t oldVertexCount = out.tessellator->uniqueVertexCount();
256 out.tessellator->addPolygon( *polyClone, extrusionHeight );
257 if ( !out.tessellator->error().isEmpty() )
262 const QgsPropertyCollection &dataDefinedProperties = mSymbol->materialSettings()->dataDefinedProperties();
265 const uint newUniqueVertices = out.tessellator->uniqueVertexCount() - oldVertexCount;
266 processMaterialDatadefined( newUniqueVertices, context.
expressionContext(), out );
272 for ( uint i = 0; i < newUniqueVertices; ++i )
274 out.ddTextureTransformData << dataDefinedTextureOffsetX << dataDefinedTextureOffsetY << dataDefinedTextureScale << dataDefinedTextureRotation;
280void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
285 bytes = handler->dataDefinedVertexColorsAsByte( mSymbol->materialSettings(), context );
287 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
295 PolygonData &out = mSelectedIds.contains( f.
id() ) ? outSelected : outNormal;
298 mWasClippedToExtent = clipGeometryIfTooLarge( geom );
316 float offset = mSymbol->offset();
317 float extrusionHeight = mSymbol->extrusionHeight();
320 if ( hasDDExtrusion )
324 const bool hasTextureScale = materialSettings
327 const bool hasTextureRotation = materialSettings
330 const bool hasTextureOffset = materialSettings
333 float dataDefinedTextureRotation = 0;
334 float dataDefinedTextureScale = 1;
335 float dataDefinedTextureOffsetX = 0;
336 float dataDefinedTextureOffsetY = 0;
339 dataDefinedTextureRotation = phongTexturedMaterial->textureRotation();
340 dataDefinedTextureScale = phongTexturedMaterial->textureScale();
341 dataDefinedTextureOffsetX = phongTexturedMaterial->textureOffset().x();
342 dataDefinedTextureOffsetY = phongTexturedMaterial->textureOffset().y();
346 dataDefinedTextureRotation = metalRoughTexturedMaterial->textureRotation();
347 dataDefinedTextureScale = metalRoughTexturedMaterial->textureScale();
348 dataDefinedTextureOffsetX = metalRoughTexturedMaterial->textureOffset().x();
349 dataDefinedTextureOffsetY = metalRoughTexturedMaterial->textureOffset().y();
352 if ( hasTextureScale || hasTextureRotation || hasTextureOffset )
354 dataDefinedTextureRotation =
static_cast< float >(
357 dataDefinedTextureScale =
static_cast< float >(
366 dataDefinedTextureOffsetX =
static_cast< float >( ddOffset.x() );
367 dataDefinedTextureOffsetY =
static_cast< float >( ddOffset.y() );
373 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
377 for (
int i = 0; i < mpoly->numGeometries(); ++i )
379 const QgsPolygon *poly = mpoly->polygonN( i );
380 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
385 for (
int i = 0; i < gc->numGeometries(); ++i )
391 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
397 for (
int i = 0; i < polySurface->numPatches(); ++i )
399 const QgsPolygon *poly = polySurface->patchN( i );
400 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
404 qWarning() <<
"not a polygon";
409void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
412 makeEntity( parent, context, outNormal,
false );
413 makeEntity( parent, context, outSelected,
true );
415 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
416 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
419 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() && !mHighlightingEnabled )
421 QgsLineMaterial *mat =
new QgsLineMaterial;
422 mat->setLineColor( mSymbol->edgeColor() );
423 mat->setLineWidth( mSymbol->edgeWidth() );
425 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
426 entity->setObjectName( parent->objectName() +
"_EDGES" );
429 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
430 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
431 renderer->setGeometry( outEdges.createGeometry( entity ) );
432 renderer->setVertexCount( outEdges.indexes.count() );
433 renderer->setPrimitiveRestartEnabled(
true );
434 renderer->setRestartIndexValue( 0 );
437 QgsGeoTransform *tr =
new QgsGeoTransform;
438 tr->setGeoTranslation( mChunkOrigin );
441 entity->addComponent( renderer );
442 entity->addComponent( mat );
443 entity->addComponent( tr );
444 entity->setParent( parent );
449void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &polyData,
bool selected )
451 if ( polyData.tessellator->dataVerticesCount() == 0 )
454 QgsMaterial *mat = material( mSymbol.get(), selected, context );
457 const QByteArray vertexBuffer = polyData.tessellator->vertexBuffer();
458 const QByteArray indexBuffer = polyData.tessellator->indexBuffer();
459 const int vertexCount = polyData.tessellator->uniqueVertexCount();
460 const size_t indexCount = polyData.tessellator->dataVerticesCount();
464 mSymbol->invertNormals(),
465 mSymbol->addBackFaces(),
466 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTextureCoordinates(),
467 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTangents()
470 geometry->
setVertexBufferData( vertexBuffer, vertexCount, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
473 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
477 handler->applyDataDefinedToGeometry( mSymbol->materialSettings(), geometry, vertexCount, polyData.materialDataDefined );
480 if ( !polyData.ddTextureTransformData.isEmpty() )
482 auto textureTransformBuffer =
new Qt3DCore::QBuffer( geometry );
484 QByteArray byteArray(
485 reinterpret_cast<const char *
>( polyData.ddTextureTransformData.data() ),
486 static_cast<int>( polyData.ddTextureTransformData.size() *
sizeof(
float ) )
488 textureTransformBuffer->setData( byteArray );
489 auto textureTransformAttribute =
new Qt3DCore::QAttribute( geometry );
490 textureTransformAttribute->setName( u
"ddTextureTransform"_s );
491 textureTransformAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
492 textureTransformAttribute->setVertexSize( 4 );
493 textureTransformAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
494 textureTransformAttribute->setBuffer( textureTransformBuffer );
495 textureTransformAttribute->setByteStride( 4 *
sizeof(
float ) );
496 textureTransformAttribute->setCount( vertexCount );
498 geometry->addAttribute( textureTransformAttribute );
501 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
502 renderer->setGeometry( geometry );
505 QgsGeoTransform *tr =
new QgsGeoTransform;
506 tr->setGeoTranslation( mChunkOrigin );
509 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
510 entity->setObjectName( parent->objectName() +
"_CHUNK_MESH" );
511 entity->addComponent( renderer );
512 entity->addComponent( mat );
513 entity->addComponent( tr );
514 entity->setParent( parent );
526 const auto techniques = material->effect()->techniques();
527 for (
auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
529 auto renderPasses = ( *tit )->renderPasses();
530 for (
auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
532 Qt3DRender::QCullFace *cullFace =
new Qt3DRender::QCullFace;
534 ( *rpit )->addRenderState( cullFace );
545 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
547 applyCullingMode( symbol->
cullingMode(), material );
555namespace Qgs3DSymbolImpl
559 QgsFeature3DHandler *handlerForPolygon3DSymbol(
const QgsVectorLayer *layer,
const QgsAbstract3DSymbol *symbol )
561 const QgsPolygon3DSymbol *polygonSymbol =
dynamic_cast<const QgsPolygon3DSymbol *
>( symbol );
562 if ( !polygonSymbol )
@ Triangles
Triangle based rendering (default).
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
Rendering context for preparation of 3D entities.
QgsExpressionContext & expressionContext()
Gets the expression context.
@ Main3DRenderer
Renderer for normal entities.
static const char * PROP_NAME_3D_RENDERER_FLAG
Qt property name to hold the 3D geometry renderer flag.
CullingMode
Triangle culling mode.
static Qt3DRender::QCullFace::CullingMode qt3DcullingMode(Qgs3DTypes::CullingMode mode)
Converts Qgs3DTypes::CullingMode mode into its Qt3D equivalent.
static void clampAltitudes(QgsLineString *lineString, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, const QgsPoint ¢roid, float offset, const Qgs3DRenderContext &context)
Clamps altitude of vertices of a linestring according to the settings.
static QgsMaterial * toMaterial(const QgsAbstractMaterialSettings *settings, Qgis::MaterialRenderingTechnique technique, const QgsMaterialContext &context)
Creates a new QgsMaterial object representing the material settings.
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
@ ExtrusionHeight
Extrusion height (zero means no extrusion).
@ Height
Height (altitude).
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Abstract base class for material 3D handlers.
Abstract base class for material settings.
virtual QSet< QgsAbstractMaterialSettings::Property > supportedProperties() const
Returns the set of data-defined properties supported by this material.
virtual bool requiresTangents() const
Returns true if the material requires tangents generated during triangulation.
@ TextureOffset
Texture offset.
@ TextureRotation
Texture rotation.
@ TextureScale
Texture scale.
QgsPropertyCollection dataDefinedProperties() const
Returns the symbol material property collection, used for data defined overrides.
virtual bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation.
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.
A 3-dimensional box composed of x, y, z coordinates.
QgsVector3D center() const
Returns the center of the box as a vector.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Line string geometry type, with support for z-dimension and m-values.
Context settings for a material.
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected 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.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A PBR metal rough shading material used for rendering with support for image texture maps.
Multi line string geometry collection.
Multi polygon geometry collection.
A Phong shading model with diffuse texture map.
3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
QgsAbstractMaterialSettings * materialSettings() const override
Returns material settings used for shading of the symbol.
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
Polyhedral surface geometry type.
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
bool hasActiveProperties() const final
Returns true if the collection has any active properties, or false if all properties within the colle...
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
Qt3DRender::QGeometry subclass that represents polygons tessellated into 3D geometry.
void setVertexBufferData(const QByteArray &vertexBufferData, int vertexCount, const QVector< QgsFeatureId > &triangleIndexFids, const QVector< uint > &triangleIndexStartingIndices)
Initializes vertex buffer (and other members) from data that were already tessellated.
void setIndexBufferData(const QByteArray &indexBufferData, size_t indexCount)
Sets index buffer data.
void setZ(double z)
Sets Z coordinate.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features