QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
16 #include "qgspolygon3dsymbol_p.h"
17 
18 #include "qgspolygon3dsymbol.h"
20 #include "qgs3dmapsettings.h"
21 #include "qgs3dutils.h"
22 #include "qgstessellator.h"
23 
24 #include <Qt3DCore/QTransform>
25 #include <Qt3DExtras/QPhongMaterial>
26 #include <Qt3DRender/QEffect>
27 #include <Qt3DRender/QTechnique>
28 #include <Qt3DRender/QCullFace>
29 #include <Qt3DRender/QGeometryRenderer>
30 
31 #include "qgsvectorlayer.h"
32 #include "qgslinestring.h"
33 #include "qgsmultipolygon.h"
34 
35 #include "qgslinevertexdata_p.h"
36 #include "qgslinematerial_p.h"
37 
39 
40 
41 class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
42 {
43  public:
44  QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol &symbol, const QgsFeatureIds &selectedIds )
45  : mSymbol( symbol ), mSelectedIds( selectedIds ) {}
46 
47  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
48  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
49  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
50 
51  private:
52 
54  struct PolygonData
55  {
56  std::unique_ptr<QgsTessellator> tessellator;
57  QVector<QgsFeatureId> triangleIndexFids;
58  QVector<uint> triangleIndexStartingIndices;
59  };
60 
61  void processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out );
62  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
63  Qt3DExtras::QPhongMaterial *material( const QgsPolygon3DSymbol &symbol ) const;
64 
65  // input specific for this class
66  const QgsPolygon3DSymbol &mSymbol;
67  // inputs - generic
68  QgsFeatureIds mSelectedIds;
69 
70  // outputs
71  PolygonData outNormal;
72  PolygonData outSelected;
73 
74  QgsLineVertexData outEdges;
75 };
76 
77 
78 bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
79 {
80  outEdges.withAdjacency = true;
81  outEdges.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
82 
83  outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces() ) );
84  outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces() ) );
85 
86  QSet<QString> attrs = mSymbol.dataDefinedProperties().referencedFields( context.expressionContext() );
87  attributeNames.unite( attrs );
88  return true;
89 }
90 
91 void QgsPolygon3DSymbolHandler::processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out )
92 {
93  if ( mSymbol.edgesEnabled() )
94  {
95  // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
96  outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->exteriorRing() ), height );
97  for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
98  outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), height );
99 
100  if ( extrusionHeight )
101  {
102  // add roof and wall edges
103  const QgsLineString *exterior = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
104  outEdges.addLineString( *exterior, extrusionHeight + height );
105  outEdges.addVerticalLines( *exterior, extrusionHeight, height );
106  for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
107  {
108  const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
109  outEdges.addLineString( *interior, extrusionHeight + height );
110  outEdges.addVerticalLines( *interior, extrusionHeight, height );
111  }
112  }
113  }
114 
115  Qgs3DUtils::clampAltitudes( polyClone, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), height, context.map() );
116 
117  Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
118  uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
119  out.triangleIndexStartingIndices.append( startingTriangleIndex );
120  out.triangleIndexFids.append( fid );
121  out.tessellator->addPolygon( *polyClone, extrusionHeight );
122  delete polyClone;
123 }
124 
125 void QgsPolygon3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
126 {
127  if ( f.geometry().isNull() )
128  return;
129 
130  PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
131 
132  QgsGeometry geom = f.geometry();
133 
134  // segmentize curved geometries if necessary
135  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
136  geom = QgsGeometry( geom.constGet()->segmentize() );
137 
138  const QgsAbstractGeometry *g = geom.constGet();
139 
140  const QgsPropertyCollection &ddp = mSymbol.dataDefinedProperties();
141  bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
142  bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );
143 
144  float height = mSymbol.height();
145  float extrusionHeight = mSymbol.extrusionHeight();
146  if ( hasDDHeight )
147  height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, context.expressionContext(), height );
148  if ( hasDDExtrusion )
149  extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, context.expressionContext(), extrusionHeight );
150 
151  if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
152  {
153  QgsPolygon *polyClone = poly->clone();
154  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
155  }
156  else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
157  {
158  for ( int i = 0; i < mpoly->numGeometries(); ++i )
159  {
160  const QgsAbstractGeometry *g2 = mpoly->geometryN( i );
161  Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon );
162  QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
163  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
164  }
165  }
166  else if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( g ) )
167  {
168  for ( int i = 0; i < gc->numGeometries(); ++i )
169  {
170  const QgsAbstractGeometry *g2 = gc->geometryN( i );
172  {
173  QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
174  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
175  }
176  }
177  }
178  else
179  qDebug() << "not a polygon";
180 }
181 
182 
183 void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
184 {
185  // create entity for selected and not selected
186  makeEntity( parent, context, outNormal, false );
187  makeEntity( parent, context, outSelected, true );
188 
189  mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
190  mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
191 
192  // add entity for edges
193  if ( mSymbol.edgesEnabled() && !outEdges.indexes.isEmpty() )
194  {
195  QgsLineMaterial *mat = new QgsLineMaterial;
196  mat->setLineColor( mSymbol.edgeColor() );
197  mat->setLineWidth( mSymbol.edgeWidth() );
198 
199  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
200 
201  // geometry renderer
202  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
203  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
204  renderer->setGeometry( outEdges.createGeometry( entity ) );
205  renderer->setVertexCount( outEdges.indexes.count() );
206  renderer->setPrimitiveRestartEnabled( true );
207  renderer->setRestartIndexValue( 0 );
208 
209  // make entity
210  entity->addComponent( renderer );
211  entity->addComponent( mat );
212  entity->setParent( parent );
213  }
214 }
215 
216 
217 void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected )
218 {
219  if ( out.tessellator->dataVerticesCount() == 0 )
220  return; // nothing to show - no need to create the entity
221 
222  Qt3DExtras::QPhongMaterial *mat = material( mSymbol );
223  if ( selected )
224  {
225  // update the material with selection colors
226  mat->setDiffuse( context.map().selectionColor() );
227  mat->setAmbient( context.map().selectionColor().darker() );
228  }
229 
230  // extract vertex buffer data from tessellator
231  QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
232  int nVerts = data.count() / out.tessellator->stride();
233 
235  geometry->setInvertNormals( mSymbol.invertNormals() );
236  geometry->setAddBackFaces( mSymbol.addBackFaces() );
237  geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
238 
239  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
240  renderer->setGeometry( geometry );
241 
242  // make entity
243  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
244  entity->addComponent( renderer );
245  entity->addComponent( mat );
246  entity->setParent( parent );
247 
248  if ( !selected )
249  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
250 // cppcheck wrongly believes entity will leak
251 // cppcheck-suppress memleak
252 }
253 
254 
255 static Qt3DRender::QCullFace::CullingMode _qt3DcullingMode( Qgs3DTypes::CullingMode mode )
256 {
257  switch ( mode )
258  {
259  case Qgs3DTypes::NoCulling: return Qt3DRender::QCullFace::NoCulling;
260  case Qgs3DTypes::Front: return Qt3DRender::QCullFace::Front;
261  case Qgs3DTypes::Back: return Qt3DRender::QCullFace::Back;
262  case Qgs3DTypes::FrontAndBack: return Qt3DRender::QCullFace::FrontAndBack;
263  }
264  return Qt3DRender::QCullFace::NoCulling;
265 }
266 
267 Qt3DExtras::QPhongMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol &symbol ) const
268 {
269  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
270 
271  // front/back side culling
272  auto techniques = material->effect()->techniques();
273  for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
274  {
275  auto renderPasses = ( *tit )->renderPasses();
276  for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
277  {
278  Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
279  cullFace->setMode( _qt3DcullingMode( symbol.cullingMode() ) );
280  ( *rpit )->addRenderState( cullFace );
281  }
282  }
283 
284  material->setAmbient( symbol.material().ambient() );
285  material->setDiffuse( symbol.material().diffuse() );
286  material->setSpecular( symbol.material().specular() );
287  material->setShininess( symbol.material().shininess() );
288  return material;
289 }
290 
291 
292 // --------------
293 
294 
295 namespace Qgs3DSymbolImpl
296 {
297 
298 
299  QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
300  {
301  return new QgsPolygon3DSymbolHandler( symbol, layer->selectedFeatureIds() );
302  }
303 
304  Qt3DCore::QEntity *entityForPolygon3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
305  {
306  QgsFeature3DHandler *handler = handlerForPolygon3DSymbol( layer, symbol );
307  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
308  delete handler;
309  return e;
310  }
311 
312 }
313 
QgsPhongMaterialSettings::ambient
QColor ambient() const
Returns ambient color component.
Definition: qgsphongmaterialsettings.h:46
QgsPhongMaterialSettings::diffuse
QColor diffuse() const
Returns diffuse color component.
Definition: qgsphongmaterialsettings.h:48
QgsAbstractPropertyCollection::valueAsDouble
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.
Definition: qgspropertycollection.cpp:66
qgstessellatedpolygongeometry.h
QgsPhongMaterialSettings::specular
QColor specular() const
Returns specular color component.
Definition: qgsphongmaterialsettings.h:50
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:189
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:33
qgslinestring.h
QgsPolygon::clone
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspolygon.cpp:54
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:86
QgsPolygon3DSymbol
Definition: qgspolygon3dsymbol.h:36
QgsAbstract3DSymbol::PropertyHeight
@ PropertyHeight
Height (altitude)
Definition: qgsabstract3dsymbol.h:59
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
Qgs3DUtils::clampAltitudes
static void clampAltitudes(QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:267
QgsTessellatedPolygonGeometry::setInvertNormals
void setInvertNormals(bool invert)
Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwi...
Definition: qgstessellatedpolygongeometry.h:52
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:99
qgslinematerial_p.h
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:35
Qgs3DTypes::CullingMode
CullingMode
Triangle culling mode.
Definition: qgs3dtypes.h:62
QgsAbstract3DSymbol::PropertyExtrusionHeight
@ PropertyExtrusionHeight
Extrusion height (zero means no extrusion)
Definition: qgsabstract3dsymbol.h:60
qgsmultipolygon.h
qgs3dutils.h
QgsTessellatedPolygonGeometry::setData
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.
Definition: qgstessellatedpolygongeometry.cpp:93
Qgs3DTypes::Front
@ Front
Will render only back faces of triangles.
Definition: qgs3dtypes.h:65
QgsPolygon3DSymbol::material
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
Definition: qgspolygon3dsymbol.h:82
QgsVectorLayer::selectedFeatureIds
const Q_INVOKABLE QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3433
Qgs3DTypes::FrontAndBack
@ FrontAndBack
Will not render anything.
Definition: qgs3dtypes.h:67
qgstessellator.h
Qgs3DMapSettings
Definition: qgs3dmapsettings.h:51
QgsMultiPolygon
Multi polygon geometry collection.
Definition: qgsmultipolygon.h:29
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsPropertyCollection::referencedFields
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.
Definition: qgspropertycollection.cpp:244
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:71
QgsAbstractGeometry::segmentize
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Definition: qgsabstractgeometry.cpp:310
qgsvectorlayer.h
QgsPropertyCollection
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition: qgspropertycollection.h:318
QgsTessellatedPolygonGeometry::setAddBackFaces
void setAddBackFaces(bool add)
Sets whether also triangles facing the other side will be created.
Definition: qgstessellatedpolygongeometry.h:64
qgs3dmapsettings.h
qgspolygon3dsymbol_p.h
qgspolygon3dsymbol.h
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:76
Qgs3DTypes::Back
@ Back
Will render only front faces of triangles (recommended when input data are consistent)
Definition: qgs3dtypes.h:66
QgsAbstract3DSymbol::dataDefinedProperties
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
Definition: qgsabstract3dsymbol.h:67
QgsGeometry
Definition: qgsgeometry.h:122
QgsTessellator
Definition: qgstessellator.h:40
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsPolygon3DSymbol::cullingMode
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
Definition: qgspolygon3dsymbol.h:87
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:73
QgsWkbTypes::isCurvedType
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:880
QgsPhongMaterialSettings::shininess
float shininess() const
Returns shininess of the surface.
Definition: qgsphongmaterialsettings.h:52
QgsFeature
Definition: qgsfeature.h:55
QgsPropertyCollection::isActive
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Definition: qgspropertycollection.cpp:258
qgslinevertexdata_p.h
Qgs3DTypes::NoCulling
@ NoCulling
Will render both front and back faces of triangles.
Definition: qgs3dtypes.h:64
QgsTessellatedPolygonGeometry
Definition: qgstessellatedpolygongeometry.h:42
QgsWkbTypes::flatType
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:701
QgsFeatureId
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25