41#include <Qt3DCore/QBuffer>
42#include <Qt3DExtras/QPhongMaterial>
43#include <Qt3DRender/QAbstractTextureImage>
44#include <Qt3DRender/QCullFace>
45#include <Qt3DRender/QEffect>
46#include <Qt3DRender/QGeometryRenderer>
47#include <Qt3DRender/QTechnique>
48#include <Qt3DRender/QTexture>
50using namespace Qt::StringLiterals;
55class QgsPolygon3DSymbolHandler :
public QgsFeature3DHandler
58 QgsPolygon3DSymbolHandler(
const QgsPolygon3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
59 : mSymbol( static_cast<QgsPolygon3DSymbol *>( symbol->clone() ) )
60 , mSelectedIds( selectedIds )
63 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
64 void processFeature(
const QgsFeature &f,
const Qgs3DRenderContext &context )
override;
65 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
71 std::unique_ptr<QgsTessellator> tessellator;
72 QVector<QgsFeatureId> triangleIndexFids;
73 QVector<uint> triangleIndexStartingIndices;
74 QByteArray materialDataDefined;
75 QVector<float> ddTextureTransformData;
79 const QgsPolygon *poly,
82 float extrusionHeight,
83 float dataDefinedTextureScale,
84 float dataDefinedTextureRotation,
85 float dataDefinedTextureOffsetX,
86 float dataDefinedTextureOffsetY,
87 const Qgs3DRenderContext &context,
90 void processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, PolygonData &out );
91 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &out,
bool selected );
92 QgsMaterial *material(
const QgsPolygon3DSymbol *symbol,
bool isSelected,
const Qgs3DRenderContext &context )
const;
95 std::unique_ptr<QgsPolygon3DSymbol> mSymbol;
99 PolygonData outNormal;
100 PolygonData outSelected;
102 QgsLineVertexData outEdges;
105 bool mWasClippedToExtent =
false;
109bool QgsPolygon3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
111 mChunkOrigin = chunkExtent.
center();
112 mChunkOrigin.
setZ( 0. );
113 mChunkExtent = chunkExtent;
115 outEdges.withAdjacency =
true;
116 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context, mChunkOrigin );
122 auto tessellator = std::make_unique<QgsTessellator>();
123 tessellator->setOrigin( mChunkOrigin );
124 tessellator->setAddNormals(
true );
125 tessellator->setInvertNormals( mSymbol->invertNormals() );
126 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
127 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
128 tessellator->setAddTextureUVs( requiresTextureCoordinates );
129 tessellator->setAddTangents( requiresTangents );
132 outNormal.tessellator = std::move( tessellator );
134 tessellator = std::make_unique<QgsTessellator>();
135 tessellator->setOrigin( mChunkOrigin );
136 tessellator->setAddNormals(
true );
137 tessellator->setInvertNormals( mSymbol->invertNormals() );
138 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
139 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
140 tessellator->setAddTextureUVs( requiresTextureCoordinates );
141 tessellator->setAddTangents( requiresTangents );
144 outSelected.tessellator = std::move( tessellator );
146 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
147 attributeNames.unite( attrs );
149 attributeNames.unite( attrs );
153void QgsPolygon3DSymbolHandler::processPolygon(
157 float extrusionHeight,
158 float dataDefinedTextureScale,
159 float dataDefinedTextureRotation,
160 float dataDefinedTextureOffsetX,
161 float dataDefinedTextureOffsetY,
166 std::unique_ptr<QgsPolygon> polyClone( poly->
clone() );
168 if ( mSymbol->edgesEnabled() )
177 if ( mWasClippedToExtent )
182 const QVector< QgsPointXY > extentPoints {
183 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() },
184 { mChunkExtent.xMaximum(), mChunkExtent.yMinimum() },
185 { mChunkExtent.xMaximum(), mChunkExtent.yMaximum() },
186 { mChunkExtent.xMinimum(), mChunkExtent.yMaximum() },
187 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() }
189 auto extentLinestring = std::make_unique<QgsLineString>( extentPoints );
193 cleanedExteriorRingGeometry = exteriorRingGeometry.
difference(
QgsGeometry( extentLinestring.release() ) );
201 outEdges.addLineString( *line, offset );
206 for (
int i = 0; i < mline->numGeometries(); ++i )
209 outEdges.addLineString( *line, offset );
215 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
216 outEdges.addLineString( *
static_cast<const QgsLineString *
>( polyClone->interiorRing( i ) ), offset );
218 if ( extrusionHeight != 0.f )
224 outEdges.addLineString( *line, extrusionHeight + offset );
225 outEdges.addVerticalLines( *line, extrusionHeight, offset );
230 for (
int i = 0; i < mline->numGeometries(); ++i )
233 outEdges.addLineString( *line, extrusionHeight + offset );
234 outEdges.addVerticalLines( *line, extrusionHeight, offset );
240 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
243 outEdges.addLineString( *interior, extrusionHeight + offset );
244 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
251 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
252 const uint startingTriangleIndex =
static_cast<uint
>( out.tessellator->dataVerticesCount() / 3 );
253 out.triangleIndexStartingIndices.append( startingTriangleIndex );
254 out.triangleIndexFids.append( fid );
256 const size_t oldVertexCount = out.tessellator->uniqueVertexCount();
257 out.tessellator->addPolygon( *polyClone, extrusionHeight );
258 if ( !out.tessellator->error().isEmpty() )
263 const QgsPropertyCollection &dataDefinedProperties = mSymbol->materialSettings()->dataDefinedProperties();
266 const uint newUniqueVertices = out.tessellator->uniqueVertexCount() - oldVertexCount;
267 processMaterialDatadefined( newUniqueVertices, context.
expressionContext(), out );
273 for ( uint i = 0; i < newUniqueVertices; ++i )
275 out.ddTextureTransformData << dataDefinedTextureOffsetX << dataDefinedTextureOffsetY << dataDefinedTextureScale << dataDefinedTextureRotation;
281void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
286 bytes = handler->dataDefinedVertexColorsAsByte( mSymbol->materialSettings(), context );
288 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
296 PolygonData &out = mSelectedIds.contains( f.
id() ) ? outSelected : outNormal;
299 mWasClippedToExtent = clipGeometryIfTooLarge( geom );
317 float offset = mSymbol->offset();
318 float extrusionHeight = mSymbol->extrusionHeight();
321 if ( hasDDExtrusion )
325 const bool hasTextureScale = materialSettings
328 const bool hasTextureRotation = materialSettings
331 const bool hasTextureOffset = materialSettings
334 float dataDefinedTextureRotation = 0;
335 float dataDefinedTextureScale = 1;
336 float dataDefinedTextureOffsetX = 0;
337 float dataDefinedTextureOffsetY = 0;
340 dataDefinedTextureRotation = phongTexturedMaterial->textureRotation();
341 dataDefinedTextureScale = phongTexturedMaterial->textureScale();
342 dataDefinedTextureOffsetX = phongTexturedMaterial->textureOffset().x();
343 dataDefinedTextureOffsetY = phongTexturedMaterial->textureOffset().y();
347 dataDefinedTextureRotation = metalRoughTexturedMaterial->textureRotation();
348 dataDefinedTextureScale = metalRoughTexturedMaterial->textureScale();
349 dataDefinedTextureOffsetX = metalRoughTexturedMaterial->textureOffset().x();
350 dataDefinedTextureOffsetY = metalRoughTexturedMaterial->textureOffset().y();
353 if ( hasTextureScale || hasTextureRotation || hasTextureOffset )
355 dataDefinedTextureRotation =
static_cast< float >(
358 dataDefinedTextureScale =
static_cast< float >(
367 dataDefinedTextureOffsetX =
static_cast< float >( ddOffset.x() );
368 dataDefinedTextureOffsetY =
static_cast< float >( ddOffset.y() );
374 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
378 for (
int i = 0; i < mpoly->numGeometries(); ++i )
380 const QgsPolygon *poly = mpoly->polygonN( i );
381 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
386 for (
int i = 0; i < gc->numGeometries(); ++i )
392 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
398 for (
int i = 0; i < polySurface->numPatches(); ++i )
400 const QgsPolygon *poly = polySurface->patchN( i );
401 processPolygon( poly, f.
id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
405 qWarning() <<
"not a polygon";
410void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
413 makeEntity( parent, context, outNormal,
false );
414 makeEntity( parent, context, outSelected,
true );
416 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
417 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
420 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() && !mHighlightingEnabled )
422 QgsLineMaterial *mat =
new QgsLineMaterial;
423 mat->setLineColor( mSymbol->edgeColor() );
424 mat->setLineWidth( mSymbol->edgeWidth() );
426 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
427 entity->setObjectName( parent->objectName() +
"_EDGES" );
430 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
431 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
432 renderer->setGeometry( outEdges.createGeometry( entity ) );
433 renderer->setVertexCount( outEdges.indexes.count() );
434 renderer->setPrimitiveRestartEnabled(
true );
435 renderer->setRestartIndexValue( 0 );
438 QgsGeoTransform *tr =
new QgsGeoTransform;
439 tr->setGeoTranslation( mChunkOrigin );
442 entity->addComponent( renderer );
443 entity->addComponent( mat );
444 entity->addComponent( tr );
445 entity->setParent( parent );
450void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &polyData,
bool selected )
452 if ( polyData.tessellator->dataVerticesCount() == 0 )
455 QgsMaterial *mat = material( mSymbol.get(), selected, context );
458 const QByteArray vertexBuffer = polyData.tessellator->vertexBuffer();
459 const QByteArray indexBuffer = polyData.tessellator->indexBuffer();
460 const int vertexCount = polyData.tessellator->uniqueVertexCount();
461 const size_t indexCount = polyData.tessellator->dataVerticesCount();
465 mSymbol->invertNormals(),
466 mSymbol->addBackFaces(),
467 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTextureCoordinates(),
468 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTangents()
471 geometry->
setVertexBufferData( vertexBuffer, vertexCount, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
474 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
478 handler->applyDataDefinedToGeometry( mSymbol->materialSettings(), geometry, vertexCount, polyData.materialDataDefined );
481 if ( !polyData.ddTextureTransformData.isEmpty() )
483 auto textureTransformBuffer =
new Qt3DCore::QBuffer( geometry );
485 QByteArray byteArray(
486 reinterpret_cast<const char *
>( polyData.ddTextureTransformData.data() ),
487 static_cast<int>( polyData.ddTextureTransformData.size() *
sizeof(
float ) )
489 textureTransformBuffer->setData( byteArray );
490 auto textureTransformAttribute =
new Qt3DCore::QAttribute( geometry );
491 textureTransformAttribute->setName( u
"ddTextureTransform"_s );
492 textureTransformAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
493 textureTransformAttribute->setVertexSize( 4 );
494 textureTransformAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
495 textureTransformAttribute->setBuffer( textureTransformBuffer );
496 textureTransformAttribute->setByteStride( 4 *
sizeof(
float ) );
497 textureTransformAttribute->setCount( vertexCount );
499 geometry->addAttribute( textureTransformAttribute );
502 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
503 renderer->setGeometry( geometry );
506 QgsGeoTransform *tr =
new QgsGeoTransform;
507 tr->setGeoTranslation( mChunkOrigin );
510 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
511 entity->setObjectName( parent->objectName() +
"_CHUNK_MESH" );
512 entity->addComponent( renderer );
513 entity->addComponent( mat );
514 entity->addComponent( tr );
515 entity->setParent( parent );
527 const auto techniques = material->effect()->techniques();
528 for (
auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
530 auto renderPasses = ( *tit )->renderPasses();
531 for (
auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
533 Qt3DRender::QCullFace *cullFace =
new Qt3DRender::QCullFace;
535 ( *rpit )->addRenderState( cullFace );
546 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
548 applyCullingMode( symbol->
cullingMode(), material );
556namespace Qgs3DSymbolImpl
560 QgsFeature3DHandler *handlerForPolygon3DSymbol(
const QgsVectorLayer *layer,
const QgsAbstract3DSymbol *symbol )
562 const QgsPolygon3DSymbol *polygonSymbol =
dynamic_cast<const QgsPolygon3DSymbol *
>( symbol );
563 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