QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgspolygon3dsymbol_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspolygon3dsymbol_p.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgspolygon3dsymbol.h"
20#include "qgs3dmapsettings.h"
21#include "qgs3dutils.h"
22#include "qgstessellator.h"
24
25#include <Qt3DCore/QTransform>
26#include <Qt3DRender/QMaterial>
27#include <Qt3DExtras/QPhongMaterial>
28
29#include <Qt3DExtras/QDiffuseMapMaterial>
30#include <Qt3DRender/QAbstractTextureImage>
31#include <Qt3DRender/QTexture>
32
33#include <Qt3DRender/QEffect>
34#include <Qt3DRender/QTechnique>
35#include <Qt3DRender/QCullFace>
36#include <Qt3DRender/QGeometryRenderer>
37
38#include "qgsvectorlayer.h"
39#include "qgslinestring.h"
40#include "qgsmultipolygon.h"
41#include "qgspolygon.h"
42
43#include "qgslinevertexdata_p.h"
44#include "qgslinematerial_p.h"
45
47
48
49class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
50{
51 public:
52 QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol *symbol, const QgsFeatureIds &selectedIds )
53 : mSymbol( static_cast< QgsPolygon3DSymbol *>( symbol->clone() ) )
54 , mSelectedIds( selectedIds ) {}
55
56 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
57 void processFeature( const QgsFeature &f, const Qgs3DRenderContext &context ) override;
58 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
59
60 private:
61
63 struct PolygonData
64 {
65 std::unique_ptr<QgsTessellator> tessellator;
66 QVector<QgsFeatureId> triangleIndexFids;
67 QVector<uint> triangleIndexStartingIndices;
68 QByteArray materialDataDefined;
69 };
70
71 void processPolygon( const QgsPolygon *poly, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out );
72 void processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, PolygonData &out );
73 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
74 Qt3DRender::QMaterial *material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const;
75
76 // input specific for this class
77 std::unique_ptr< QgsPolygon3DSymbol > mSymbol;
78 // inputs - generic
79 QgsFeatureIds mSelectedIds;
80
81 // outputs
82 PolygonData outNormal;
83 PolygonData outSelected;
84
85 QgsLineVertexData outEdges;
86};
87
88
89bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
90{
91 outEdges.withAdjacency = true;
92 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, &context.map() );
93
94 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
95
96 outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false,
97 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
98 mSymbol->renderedFacade(),
99 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
100 outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol->invertNormals(),
101 mSymbol->addBackFaces(), false,
102 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
103 mSymbol->renderedFacade(),
104 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
105
106 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
107 attributeNames.unite( attrs );
108 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.expressionContext() );
109 attributeNames.unite( attrs );
110 return true;
111}
112
113void QgsPolygon3DSymbolHandler::processPolygon( const QgsPolygon *poly, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out )
114{
115 std::unique_ptr< QgsPolygon > polyClone( poly->clone() );
116
117 const uint oldVerticesCount = out.tessellator->dataVerticesCount();
118 if ( mSymbol->edgesEnabled() )
119 {
120 // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
121 outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->exteriorRing() ), height );
122 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
123 outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), height );
124
125 if ( extrusionHeight )
126 {
127 // add roof and wall edges
128 const QgsLineString *exterior = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
129 outEdges.addLineString( *exterior, extrusionHeight + height );
130 outEdges.addVerticalLines( *exterior, extrusionHeight, height );
131 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
132 {
133 const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
134 outEdges.addLineString( *interior, extrusionHeight + height );
135 outEdges.addVerticalLines( *interior, extrusionHeight, height );
136 }
137 }
138 }
139
140 Qgs3DUtils::clampAltitudes( polyClone.get(), mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), height, context.map() );
141
142 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
143 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
144 out.triangleIndexStartingIndices.append( startingTriangleIndex );
145 out.triangleIndexFids.append( fid );
146 out.tessellator->addPolygon( *polyClone, extrusionHeight );
147
148 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
149 processMaterialDatadefined( out.tessellator->dataVerticesCount() - oldVerticesCount, context.expressionContext(), out );
150}
151
152void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
153{
154 const QByteArray bytes = mSymbol->materialSettings()->dataDefinedVertexColorsAsByte( context );
155 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
156}
157
158void QgsPolygon3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
159{
160 if ( f.geometry().isNull() )
161 return;
162
163 PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
164
165 QgsGeometry geom = f.geometry();
167
168 // segmentize curved geometries if necessary
170 {
171 geom = QgsGeometry( g->segmentize() );
172 g = geom.constGet()->simplifiedTypeRef();
173 }
174
175 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
176 const bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
177 const bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );
178
179 float height = mSymbol->height();
180 float extrusionHeight = mSymbol->extrusionHeight();
181 if ( hasDDHeight )
182 height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, context.expressionContext(), height );
183 if ( hasDDExtrusion )
184 extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, context.expressionContext(), extrusionHeight );
185
186 if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
187 {
188 processPolygon( poly, f.id(), height, extrusionHeight, context, out );
189 }
190 else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
191 {
192 for ( int i = 0; i < mpoly->numGeometries(); ++i )
193 {
194 const QgsPolygon *poly = mpoly->polygonN( i );
195 processPolygon( poly, f.id(), height, extrusionHeight, context, out );
196 }
197 }
198 else if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( g ) )
199 {
200 for ( int i = 0; i < gc->numGeometries(); ++i )
201 {
202 const QgsAbstractGeometry *g2 = gc->geometryN( i );
204 {
205 const QgsPolygon *poly = static_cast< const QgsPolygon *>( g2 );
206 processPolygon( poly, f.id(), height, extrusionHeight, context, out );
207 }
208 }
209 }
210 else
211 qWarning() << "not a polygon";
212
213 mFeatureCount++;
214}
215
216void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
217{
218 // create entity for selected and not selected
219 makeEntity( parent, context, outNormal, false );
220 makeEntity( parent, context, outSelected, true );
221
222 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
223 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
224
225 // add entity for edges
226 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() )
227 {
228 QgsLineMaterial *mat = new QgsLineMaterial;
229 mat->setLineColor( mSymbol->edgeColor() );
230 mat->setLineWidth( mSymbol->edgeWidth() );
231
232 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
233 entity->setObjectName( parent->objectName() + "_EDGES" );
234
235 // geometry renderer
236 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
237 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
238 renderer->setGeometry( outEdges.createGeometry( entity ) );
239 renderer->setVertexCount( outEdges.indexes.count() );
240 renderer->setPrimitiveRestartEnabled( true );
241 renderer->setRestartIndexValue( 0 );
242
243 // make entity
244 entity->addComponent( renderer );
245 entity->addComponent( mat );
246 entity->setParent( parent );
247 }
248}
249
250
251void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &polyData, bool selected )
252{
253 if ( polyData.tessellator->dataVerticesCount() == 0 )
254 return; // nothing to show - no need to create the entity
255
256 Qt3DRender::QMaterial *mat = material( mSymbol.get(), selected, context );
257
258 // extract vertex buffer data from tessellator
259 const QByteArray data( reinterpret_cast<const char *>( polyData.tessellator->data().constData() ), static_cast<int>( polyData.tessellator->data().count() * sizeof( float ) ) );
260 const int nVerts = data.count() / polyData.tessellator->stride();
261
262 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
263
264 QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, mSymbol->invertNormals(), mSymbol->addBackFaces(),
265 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates() );
266
267 geometry->setData( data, nVerts, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
268
269 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
270 mSymbol->materialSettings()->applyDataDefinedToGeometry( geometry, nVerts, polyData.materialDataDefined );
271
272 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
273 renderer->setGeometry( geometry );
274
275 // make entity
276 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
277 entity->setObjectName( parent->objectName() + "_CHUNK_MESH" );
278 entity->addComponent( renderer );
279 entity->addComponent( mat );
280 entity->setParent( parent );
281
282 if ( !selected )
283 renderer->setProperty( Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG, Qgs3DTypes::Main3DRenderer ); // temporary measure to distinguish between "selected" and "main"
284
285// cppcheck wrongly believes entity will leak
286// cppcheck-suppress memleak
287}
288
289// front/back side culling
290static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, Qt3DRender::QMaterial *material )
291{
292 const auto techniques = material->effect()->techniques();
293 for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
294 {
295 auto renderPasses = ( *tit )->renderPasses();
296 for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
297 {
298 Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
299 cullFace->setMode( Qgs3DUtils::qt3DcullingMode( cullingMode ) );
300 ( *rpit )->addRenderState( cullFace );
301 }
302 }
303}
304
305Qt3DRender::QMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const
306{
307 QgsMaterialContext materialContext;
308 materialContext.setIsSelected( isSelected );
309 materialContext.setSelectionColor( context.map().selectionColor() );
310
311 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
312 Qt3DRender::QMaterial *material = symbol->materialSettings()->toMaterial( dataDefined ?
314 materialContext );
315 applyCullingMode( symbol->cullingMode(), material );
316 return material;
317}
318
319
320// --------------
321
322
323namespace Qgs3DSymbolImpl
324{
325
326
327 QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
328 {
329 const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast< const QgsPolygon3DSymbol * >( symbol );
330 if ( !polygonSymbol )
331 return nullptr;
332
333 return new QgsPolygon3DSymbolHandler( polygonSymbol, layer->selectedFeatureIds() );
334 }
335
336 Qt3DCore::QEntity *entityForPolygon3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
337 {
338 QgsFeature3DHandler *handler = handlerForPolygon3DSymbol( layer, &symbol );
339 Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
340 delete handler;
341 return e;
342 }
343
344}
345
@ Polygon
Polygon.
@ Main3DRenderer
Renderer for normal entities.
Definition qgs3dtypes.h:49
static const char * PROP_NAME_3D_RENDERER_FLAG
Qt property name to hold the 3D geometry renderer flag.
Definition qgs3dtypes.h:44
CullingMode
Triangle culling mode.
Definition qgs3dtypes.h:36
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 &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
@ PropertyHeight
Height (altitude)
@ PropertyExtrusionHeight
Extrusion height (zero means no extrusion)
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 Qt3DRender::QMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const =0
Creates a new QMaterial object representing the material settings.
QgsPropertyCollection dataDefinedProperties() const
Returns the symbol material property collection, used for data defined overrides.
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.
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...
Definition qgsfeature.h:56
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
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.
Line string geometry type, with support for z-dimension and m-values.
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.
Multi polygon geometry collection.
bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation....
float textureRotation() const
Returns the texture rotation, in degrees.
QgsAbstractMaterialSettings * materialSettings() const
Returns material settings used for shading of the symbol.
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
Polygon geometry type.
Definition qgspolygon.h:34
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const override
Returns the set of any fields referenced by the active properties from the collection.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void setData(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.
Class that takes care of tessellation of polygons into triangles.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
static 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.
QgsMaterialSettingsRenderingTechnique
Material rendering techniques 3.
@ Triangles
Triangle based rendering (default)
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features