QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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"
21#include "qgs3drendercontext.h"
22#include "qgs3dutils.h"
23#include "qgstessellator.h"
25
26#include <Qt3DCore/QTransform>
27#include <Qt3DRender/QMaterial>
28#include <Qt3DExtras/QPhongMaterial>
29
30#include <Qt3DExtras/QDiffuseMapMaterial>
31#include <Qt3DRender/QAbstractTextureImage>
32#include <Qt3DRender/QTexture>
33
34#include <Qt3DRender/QEffect>
35#include <Qt3DRender/QTechnique>
36#include <Qt3DRender/QCullFace>
37#include <Qt3DRender/QGeometryRenderer>
38
39#include "qgsvectorlayer.h"
40#include "qgslinestring.h"
41#include "qgsmultipolygon.h"
42#include "qgspolygon.h"
43#include "qgsmessagelog.h"
44
45#include "qgslinevertexdata_p.h"
46#include "qgslinematerial_p.h"
47
49
50
51class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
52{
53 public:
54 QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol *symbol, const QgsFeatureIds &selectedIds )
55 : mSymbol( static_cast< QgsPolygon3DSymbol *>( symbol->clone() ) )
56 , mSelectedIds( selectedIds ) {}
57
58 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
59 void processFeature( const QgsFeature &f, const Qgs3DRenderContext &context ) override;
60 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
61
62 private:
63
65 struct PolygonData
66 {
67 std::unique_ptr<QgsTessellator> tessellator;
68 QVector<QgsFeatureId> triangleIndexFids;
69 QVector<uint> triangleIndexStartingIndices;
70 QByteArray materialDataDefined;
71 };
72
73 void processPolygon( const QgsPolygon *poly, QgsFeatureId fid, float offset, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out );
74 void processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, PolygonData &out );
75 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
76 Qt3DRender::QMaterial *material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const;
77
78 // input specific for this class
79 std::unique_ptr< QgsPolygon3DSymbol > mSymbol;
80 // inputs - generic
81 QgsFeatureIds mSelectedIds;
82
83 // outputs
84 PolygonData outNormal;
85 PolygonData outSelected;
86
87 QgsLineVertexData outEdges;
88};
89
90
91bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
92{
93 outEdges.withAdjacency = true;
94 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context );
95
96 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
97
98 outNormal.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false,
99 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
100 mSymbol->renderedFacade(),
101 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
102 outSelected.tessellator.reset( new QgsTessellator( context.origin().x(), context.origin().y(), true, mSymbol->invertNormals(),
103 mSymbol->addBackFaces(), false,
104 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
105 mSymbol->renderedFacade(),
106 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
107
108 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
109 attributeNames.unite( attrs );
110 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.expressionContext() );
111 attributeNames.unite( attrs );
112 return true;
113}
114
115void QgsPolygon3DSymbolHandler::processPolygon( const QgsPolygon *poly, QgsFeatureId fid, float offset, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out )
116{
117 std::unique_ptr< QgsPolygon > polyClone( poly->clone() );
118
119 const uint oldVerticesCount = out.tessellator->dataVerticesCount();
120 if ( mSymbol->edgesEnabled() )
121 {
122 // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
123 outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->exteriorRing() ), offset );
124 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
125 outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), offset );
126
127 if ( extrusionHeight )
128 {
129 // add roof and wall edges
130 const QgsLineString *exterior = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
131 outEdges.addLineString( *exterior, extrusionHeight + offset );
132 outEdges.addVerticalLines( *exterior, extrusionHeight, offset );
133 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
134 {
135 const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
136 outEdges.addLineString( *interior, extrusionHeight + offset );
137 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
138 }
139 }
140 }
141
142 Qgs3DUtils::clampAltitudes( polyClone.get(), mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), offset, context );
143
144 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
145 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
146 out.triangleIndexStartingIndices.append( startingTriangleIndex );
147 out.triangleIndexFids.append( fid );
148 out.tessellator->addPolygon( *polyClone, extrusionHeight );
149 if ( !out.tessellator->error().isEmpty() )
150 {
151 QgsMessageLog::logMessage( out.tessellator->error(), QObject::tr( "3D" ) );
152 }
153
154 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
155 processMaterialDatadefined( out.tessellator->dataVerticesCount() - oldVerticesCount, context.expressionContext(), out );
156}
157
158void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
159{
160 const QByteArray bytes = mSymbol->materialSettings()->dataDefinedVertexColorsAsByte( context );
161 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
162}
163
164void QgsPolygon3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
165{
166 if ( f.geometry().isNull() )
167 return;
168
169 PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
170
171 QgsGeometry geom = f.geometry();
173
174 // segmentize curved geometries if necessary
176 {
177 geom = QgsGeometry( g->segmentize() );
178 g = geom.constGet()->simplifiedTypeRef();
179 }
180
181 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
182 const bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::Property::Height );
183 const bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::Property::ExtrusionHeight );
184
185 float offset = mSymbol->offset();
186 float extrusionHeight = mSymbol->extrusionHeight();
187 if ( hasDDHeight )
188 offset = static_cast<float>( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Height, context.expressionContext(), offset ) );
189 if ( hasDDExtrusion )
190 extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ExtrusionHeight, context.expressionContext(), extrusionHeight );
191
192 if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
193 {
194 processPolygon( poly, f.id(), offset, extrusionHeight, context, out );
195 }
196 else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
197 {
198 for ( int i = 0; i < mpoly->numGeometries(); ++i )
199 {
200 const QgsPolygon *poly = mpoly->polygonN( i );
201 processPolygon( poly, f.id(), offset, extrusionHeight, context, out );
202 }
203 }
204 else if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( g ) )
205 {
206 for ( int i = 0; i < gc->numGeometries(); ++i )
207 {
208 const QgsAbstractGeometry *g2 = gc->geometryN( i );
210 {
211 const QgsPolygon *poly = static_cast< const QgsPolygon *>( g2 );
212 processPolygon( poly, f.id(), offset, extrusionHeight, context, out );
213 }
214 }
215 }
216 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast< const QgsPolyhedralSurface *>( g ) )
217 {
218 for ( int i = 0; i < polySurface->numPatches(); ++i )
219 {
220 const QgsPolygon *poly = polySurface->patchN( i );
221 processPolygon( poly, f.id(), offset, extrusionHeight, context, out );
222 }
223 }
224 else
225 qWarning() << "not a polygon";
226
227 mFeatureCount++;
228}
229
230void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
231{
232 // create entity for selected and not selected
233 makeEntity( parent, context, outNormal, false );
234 makeEntity( parent, context, outSelected, true );
235
236 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
237 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
238
239 // add entity for edges
240 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() )
241 {
242 QgsLineMaterial *mat = new QgsLineMaterial;
243 mat->setLineColor( mSymbol->edgeColor() );
244 mat->setLineWidth( mSymbol->edgeWidth() );
245
246 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
247 entity->setObjectName( parent->objectName() + "_EDGES" );
248
249 // geometry renderer
250 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
251 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
252 renderer->setGeometry( outEdges.createGeometry( entity ) );
253 renderer->setVertexCount( outEdges.indexes.count() );
254 renderer->setPrimitiveRestartEnabled( true );
255 renderer->setRestartIndexValue( 0 );
256
257 // make entity
258 entity->addComponent( renderer );
259 entity->addComponent( mat );
260 entity->setParent( parent );
261 }
262}
263
264
265void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &polyData, bool selected )
266{
267 if ( polyData.tessellator->dataVerticesCount() == 0 )
268 return; // nothing to show - no need to create the entity
269
270 Qt3DRender::QMaterial *mat = material( mSymbol.get(), selected, context );
271
272 // extract vertex buffer data from tessellator
273 const QByteArray data( reinterpret_cast<const char *>( polyData.tessellator->data().constData() ), static_cast<int>( polyData.tessellator->data().count() * sizeof( float ) ) );
274 const int nVerts = data.count() / polyData.tessellator->stride();
275
276 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
277
278 QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, mSymbol->invertNormals(), mSymbol->addBackFaces(),
279 texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates() );
280
281 geometry->setData( data, nVerts, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
282
283 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
284 mSymbol->materialSettings()->applyDataDefinedToGeometry( geometry, nVerts, polyData.materialDataDefined );
285
286 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
287 renderer->setGeometry( geometry );
288
289 // make entity
290 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
291 entity->setObjectName( parent->objectName() + "_CHUNK_MESH" );
292 entity->addComponent( renderer );
293 entity->addComponent( mat );
294 entity->setParent( parent );
295
296 if ( !selected )
297 renderer->setProperty( Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG, Qgs3DTypes::Main3DRenderer ); // temporary measure to distinguish between "selected" and "main"
298
299// cppcheck wrongly believes entity will leak
300// cppcheck-suppress memleak
301}
302
303// front/back side culling
304static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, Qt3DRender::QMaterial *material )
305{
306 const auto techniques = material->effect()->techniques();
307 for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
308 {
309 auto renderPasses = ( *tit )->renderPasses();
310 for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
311 {
312 Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
313 cullFace->setMode( Qgs3DUtils::qt3DcullingMode( cullingMode ) );
314 ( *rpit )->addRenderState( cullFace );
315 }
316 }
317}
318
319Qt3DRender::QMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const
320{
321 QgsMaterialContext materialContext;
322 materialContext.setIsSelected( isSelected );
323 materialContext.setSelectionColor( context.selectionColor() );
324
325 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
326 Qt3DRender::QMaterial *material = symbol->materialSettings()->toMaterial( dataDefined ?
328 materialContext );
329 applyCullingMode( symbol->cullingMode(), material );
330 return material;
331}
332
333
334// --------------
335
336
337namespace Qgs3DSymbolImpl
338{
339
340
341 QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
342 {
343 const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast< const QgsPolygon3DSymbol * >( symbol );
344 if ( !polygonSymbol )
345 return nullptr;
346
347 return new QgsPolygon3DSymbolHandler( polygonSymbol, layer->selectedFeatureIds() );
348 }
349}
350
@ Polygon
Polygon.
QColor selectionColor() const
Returns color used for selected features.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
@ 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 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 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:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
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.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
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:33
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 a integer key value.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
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.
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.
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
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