36#include <Qt3DExtras/QDiffuseMapMaterial>
37#include <Qt3DExtras/QPhongMaterial>
38#include <Qt3DRender/QAbstractTextureImage>
39#include <Qt3DRender/QCullFace>
40#include <Qt3DRender/QEffect>
41#include <Qt3DRender/QGeometryRenderer>
42#include <Qt3DRender/QTechnique>
43#include <Qt3DRender/QTexture>
48class QgsPolygon3DSymbolHandler :
public QgsFeature3DHandler
51 QgsPolygon3DSymbolHandler(
const QgsPolygon3DSymbol *symbol,
const QgsFeatureIds &selectedIds )
52 : mSymbol( static_cast<QgsPolygon3DSymbol *>( symbol->clone() ) )
53 , mSelectedIds( selectedIds ) {}
55 bool prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
override;
56 void processFeature(
const QgsFeature &f,
const Qgs3DRenderContext &context )
override;
57 void finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
override;
63 std::unique_ptr<QgsTessellator> tessellator;
64 QVector<QgsFeatureId> triangleIndexFids;
65 QVector<uint> triangleIndexStartingIndices;
66 QByteArray materialDataDefined;
69 void processPolygon(
const QgsPolygon *poly,
QgsFeatureId fid,
float offset,
float extrusionHeight,
const Qgs3DRenderContext &context, PolygonData &out );
70 void processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, PolygonData &out );
71 void makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &out,
bool selected );
72 QgsMaterial *material(
const QgsPolygon3DSymbol *symbol,
bool isSelected,
const Qgs3DRenderContext &context )
const;
75 std::unique_ptr<QgsPolygon3DSymbol> mSymbol;
79 PolygonData outNormal;
80 PolygonData outSelected;
82 QgsLineVertexData outEdges;
85 bool mWasClippedToExtent =
false;
89bool QgsPolygon3DSymbolHandler::prepare(
const Qgs3DRenderContext &context, QSet<QString> &attributeNames,
const QgsBox3D &chunkExtent )
91 mChunkOrigin = chunkExtent.
center();
92 mChunkOrigin.
setZ( 0. );
93 mChunkExtent = chunkExtent;
95 outEdges.withAdjacency =
true;
96 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context, mChunkOrigin );
100 auto tessellator = std::make_unique<QgsTessellator>();
101 tessellator->setOrigin( mChunkOrigin );
102 tessellator->setAddNormals(
true );
103 tessellator->setInvertNormals( mSymbol->invertNormals() );
104 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
105 tessellator->setOutputZUp(
true );
106 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
107 tessellator->setTextureRotation( texturedMaterialSettings ?
static_cast<float>( texturedMaterialSettings->
textureRotation() ) : 0.f );
109 tessellator->setOutputZUp(
true );
111 outNormal.tessellator = std::move( tessellator );
113 tessellator = std::make_unique<QgsTessellator>();
114 tessellator->setOrigin( mChunkOrigin );
115 tessellator->setAddNormals(
true );
116 tessellator->setInvertNormals( mSymbol->invertNormals() );
117 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
118 tessellator->setOutputZUp(
true );
119 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
120 tessellator->setTextureRotation( texturedMaterialSettings ?
static_cast<float>( texturedMaterialSettings->
textureRotation() ) : 0.f );
122 tessellator->setOutputZUp(
true );
124 outSelected.tessellator = std::move( tessellator );
126 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.
expressionContext() );
127 attributeNames.unite( attrs );
128 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.
expressionContext() );
129 attributeNames.unite( attrs );
135 std::unique_ptr<QgsPolygon> polyClone( poly->
clone() );
137 if ( mSymbol->edgesEnabled() )
146 if ( mWasClippedToExtent )
151 const QVector< QgsPointXY > extentPoints {
152 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() },
153 { mChunkExtent.xMaximum(), mChunkExtent.yMinimum() },
154 { mChunkExtent.xMaximum(), mChunkExtent.yMaximum() },
155 { mChunkExtent.xMinimum(), mChunkExtent.yMaximum() },
156 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() }
158 auto extentLinestring = std::make_unique<QgsLineString>( extentPoints );
162 cleanedExteriorRingGeometry = exteriorRingGeometry.
difference(
QgsGeometry( extentLinestring.release() ) );
170 outEdges.addLineString( *line, offset );
175 for (
int i = 0; i < mline->numGeometries(); ++i )
178 outEdges.addLineString( *line, offset );
184 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
185 outEdges.addLineString( *
static_cast<const QgsLineString *
>( polyClone->interiorRing( i ) ), offset );
187 if ( extrusionHeight != 0.f )
193 outEdges.addLineString( *line, extrusionHeight + offset );
194 outEdges.addVerticalLines( *line, extrusionHeight, offset );
199 for (
int i = 0; i < mline->numGeometries(); ++i )
202 outEdges.addLineString( *line, extrusionHeight + offset );
203 outEdges.addVerticalLines( *line, extrusionHeight, offset );
209 for (
int i = 0; i < polyClone->numInteriorRings(); ++i )
212 outEdges.addLineString( *interior, extrusionHeight + offset );
213 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
220 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
221 const uint startingTriangleIndex =
static_cast<uint
>( out.tessellator->dataVerticesCount() / 3 );
222 out.triangleIndexStartingIndices.append( startingTriangleIndex );
223 out.triangleIndexFids.append( fid );
225 const size_t oldVertexCount = out.tessellator->uniqueVertexCount();
226 out.tessellator->addPolygon( *polyClone, extrusionHeight );
227 if ( !out.tessellator->error().isEmpty() )
232 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
234 const uint newUniqueVertices = out.tessellator->uniqueVertexCount() - oldVertexCount;
235 processMaterialDatadefined( newUniqueVertices, context.
expressionContext(), out );
239void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount,
const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
241 const QByteArray bytes = mSymbol->materialSettings()->dataDefinedVertexColorsAsByte( context );
242 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
250 PolygonData &out = mSelectedIds.contains( f.
id() ) ? outSelected : outNormal;
253 mWasClippedToExtent = clipGeometryIfTooLarge( geom );
271 float offset = mSymbol->offset();
272 float extrusionHeight = mSymbol->extrusionHeight();
275 if ( hasDDExtrusion )
280 processPolygon( poly, f.
id(), offset, extrusionHeight, context, out );
284 for (
int i = 0; i < mpoly->numGeometries(); ++i )
286 const QgsPolygon *poly = mpoly->polygonN( i );
287 processPolygon( poly, f.
id(), offset, extrusionHeight, context, out );
292 for (
int i = 0; i < gc->numGeometries(); ++i )
298 processPolygon( poly, f.
id(), offset, extrusionHeight, context, out );
304 for (
int i = 0; i < polySurface->numPatches(); ++i )
306 const QgsPolygon *poly = polySurface->patchN( i );
307 processPolygon( poly, f.
id(), offset, extrusionHeight, context, out );
311 qWarning() <<
"not a polygon";
316void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context )
319 makeEntity( parent, context, outNormal,
false );
320 makeEntity( parent, context, outSelected,
true );
322 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
323 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
326 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() && !mHighlightingEnabled )
328 QgsLineMaterial *mat =
new QgsLineMaterial;
329 mat->setLineColor( mSymbol->edgeColor() );
330 mat->setLineWidth( mSymbol->edgeWidth() );
332 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
333 entity->setObjectName( parent->objectName() +
"_EDGES" );
336 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
337 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
338 renderer->setGeometry( outEdges.createGeometry( entity ) );
339 renderer->setVertexCount( outEdges.indexes.count() );
340 renderer->setPrimitiveRestartEnabled(
true );
341 renderer->setRestartIndexValue( 0 );
344 QgsGeoTransform *tr =
new QgsGeoTransform;
345 tr->setGeoTranslation( mChunkOrigin );
348 entity->addComponent( renderer );
349 entity->addComponent( mat );
350 entity->addComponent( tr );
351 entity->setParent( parent );
356void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent,
const Qgs3DRenderContext &context, PolygonData &polyData,
bool selected )
358 if ( polyData.tessellator->dataVerticesCount() == 0 )
361 QgsMaterial *mat = material( mSymbol.get(), selected, context );
364 const QByteArray vertexBuffer = polyData.tessellator->vertexBuffer();
365 const QByteArray indexBuffer = polyData.tessellator->indexBuffer();
366 const int vertexCount = polyData.tessellator->uniqueVertexCount();
367 const size_t indexCount = polyData.tessellator->dataVerticesCount();
373 geometry->
setVertexBufferData( vertexBuffer, vertexCount, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
376 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
377 mSymbol->materialSettings()->applyDataDefinedToGeometry( geometry, vertexCount, polyData.materialDataDefined );
378 Qt3DRender::QGeometryRenderer *renderer =
new Qt3DRender::QGeometryRenderer;
379 renderer->setGeometry( geometry );
382 QgsGeoTransform *tr =
new QgsGeoTransform;
383 tr->setGeoTranslation( mChunkOrigin );
386 Qt3DCore::QEntity *entity =
new Qt3DCore::QEntity;
387 entity->setObjectName( parent->objectName() +
"_CHUNK_MESH" );
388 entity->addComponent( renderer );
389 entity->addComponent( mat );
390 entity->addComponent( tr );
391 entity->setParent( parent );
403 const auto techniques = material->effect()->techniques();
404 for (
auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
406 auto renderPasses = ( *tit )->renderPasses();
407 for (
auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
409 Qt3DRender::QCullFace *cullFace =
new Qt3DRender::QCullFace;
411 ( *rpit )->addRenderState( cullFace );
423 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
425 applyCullingMode( symbol->
cullingMode(), material );
433namespace Qgs3DSymbolImpl
437 QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer,
const QgsAbstract3DSymbol *symbol )
439 const QgsPolygon3DSymbol *polygonSymbol =
dynamic_cast<const QgsPolygon3DSymbol *
>( symbol );
440 if ( !polygonSymbol )
Rendering context for preparation of 3D entities.
QColor selectionColor() const
Returns color used for selected features.
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.
@ 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.
virtual QgsMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const =0
Creates a new QgsMaterial object representing the material settings.
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.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
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,...
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 setSelectionColor(const QColor &color)
Sets the color for representing materials in a selected state.
void setIsHighlighted(bool isHighlighted)
Sets whether the material should represent a highlighted state.
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).
Multi line string geometry collection.
Multi polygon geometry collection.
A Phong shading model with diffuse texture map.
bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation....
double textureRotation() const
Returns the texture rotation, in degrees.
3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
QgsAbstractMaterialSettings * materialSettings() const
Returns material settings used for shading of the symbol.
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
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.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
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.
@ Triangles
Triangle based rendering (default).
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features