QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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() ) );
97  for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
98  outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ) );
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 );
105  outEdges.addVerticalLines( *exterior, extrusionHeight );
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 );
110  outEdges.addVerticalLines( *interior, extrusionHeight );
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 }
251 
252 
253 static Qt3DRender::QCullFace::CullingMode _qt3DcullingMode( Qgs3DTypes::CullingMode mode )
254 {
255  switch ( mode )
256  {
257  case Qgs3DTypes::NoCulling: return Qt3DRender::QCullFace::NoCulling;
258  case Qgs3DTypes::Front: return Qt3DRender::QCullFace::Front;
259  case Qgs3DTypes::Back: return Qt3DRender::QCullFace::Back;
260  case Qgs3DTypes::FrontAndBack: return Qt3DRender::QCullFace::FrontAndBack;
261  }
262  return Qt3DRender::QCullFace::NoCulling;
263 }
264 
265 Qt3DExtras::QPhongMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol &symbol ) const
266 {
267  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
268 
269  // front/back side culling
270  auto techniques = material->effect()->techniques();
271  for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
272  {
273  auto renderPasses = ( *tit )->renderPasses();
274  for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
275  {
276  Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
277  cullFace->setMode( _qt3DcullingMode( symbol.cullingMode() ) );
278  ( *rpit )->addRenderState( cullFace );
279  }
280  }
281 
282  material->setAmbient( symbol.material().ambient() );
283  material->setDiffuse( symbol.material().diffuse() );
284  material->setSpecular( symbol.material().specular() );
285  material->setShininess( symbol.material().shininess() );
286  return material;
287 }
288 
289 
290 // --------------
291 
292 
293 namespace Qgs3DSymbolImpl
294 {
295 
296 
297  QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
298  {
299  return new QgsPolygon3DSymbolHandler( symbol, layer->selectedFeatureIds() );
300  }
301 
302  Qt3DCore::QEntity *entityForPolygon3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
303  {
304  QgsFeature3DHandler *handler = handlerForPolygon3DSymbol( layer, symbol );
305  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
306  delete handler;
307  return e;
308  }
309 
310 }
311 
QgsFeatureId id
Definition: qgsfeature.h:64
float shininess() const
Returns shininess of the surface.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void setInvertNormals(bool invert)
Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwi...
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...
QColor specular() const
Returns specular color component.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
3 3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls)...
CullingMode
Triangle culling mode.
Definition: qgs3dtypes.h:49
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
Extrusion height (zero means no extrusion)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
Will not render anything.
Definition: qgs3dtypes.h:54
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
3 Definition of the world
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Class that takes care of tessellation of polygons into triangles.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Will render only front faces of triangles (recommended when input data are consistent) ...
Definition: qgs3dtypes.h:53
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Will render only back faces of triangles.
Definition: qgs3dtypes.h:52
Will render both front and back faces of triangles.
Definition: qgs3dtypes.h:51
Geometry collection.
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
void setAddBackFaces(bool add)
Sets whether also triangles facing the other side will be created.
Abstract base class for all geometries.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
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.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const override
Returns the set of any fields referenced by the active properties from the collection.
Multi polygon geometry collection.
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:755
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspolygon.cpp:42
QColor ambient() const
Returns ambient color component.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
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
QColor diffuse() const
Returns diffuse color component.
QgsGeometry geometry
Definition: qgsfeature.h:67
Polygon geometry type.
Definition: qgspolygon.h:31
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.