QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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"
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 
42 #include "qgslinevertexdata_p.h"
43 #include "qgslinematerial_p.h"
44 
45 #include "qgsimagetexture.h"
46 
48 
49 
50 class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
51 {
52  public:
53  QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol *symbol, const QgsFeatureIds &selectedIds )
54  : mSymbol( static_cast< QgsPolygon3DSymbol *>( symbol->clone() ) )
55  , mSelectedIds( selectedIds ) {}
56 
57  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
58  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
59  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
60 
61  private:
62 
64  struct PolygonData
65  {
66  std::unique_ptr<QgsTessellator> tessellator;
67  QVector<QgsFeatureId> triangleIndexFids;
68  QVector<uint> triangleIndexStartingIndices;
69  };
70 
71  void processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out );
72  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
73  Qt3DRender::QMaterial *material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const;
74 
75  // input specific for this class
76  std::unique_ptr< QgsPolygon3DSymbol > mSymbol;
77  // inputs - generic
78  QgsFeatureIds mSelectedIds;
79 
80  // outputs
81  PolygonData outNormal;
82  PolygonData outSelected;
83 
84  QgsLineVertexData outEdges;
85 };
86 
87 
88 bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
89 {
90  outEdges.withAdjacency = true;
91  outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, &context.map() );
92 
93  const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->material() );
94 
95  outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol->invertNormals(), mSymbol->addBackFaces(), false,
96  texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
97  mSymbol->renderedFacade(),
98  texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
99  outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol->invertNormals(),
100  mSymbol->addBackFaces(), false,
101  texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates(),
102  mSymbol->renderedFacade(),
103  texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
104 
105  QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
106  attributeNames.unite( attrs );
107  return true;
108 }
109 
110 void QgsPolygon3DSymbolHandler::processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out )
111 {
112  if ( mSymbol->edgesEnabled() )
113  {
114  // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
115  outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->exteriorRing() ), height );
116  for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
117  outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), height );
118 
119  if ( extrusionHeight )
120  {
121  // add roof and wall edges
122  const QgsLineString *exterior = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
123  outEdges.addLineString( *exterior, extrusionHeight + height );
124  outEdges.addVerticalLines( *exterior, extrusionHeight, height );
125  for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
126  {
127  const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
128  outEdges.addLineString( *interior, extrusionHeight + height );
129  outEdges.addVerticalLines( *interior, extrusionHeight, height );
130  }
131  }
132  }
133 
134  Qgs3DUtils::clampAltitudes( polyClone, mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), height, context.map() );
135 
136  Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
137  uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
138  out.triangleIndexStartingIndices.append( startingTriangleIndex );
139  out.triangleIndexFids.append( fid );
140  out.tessellator->addPolygon( *polyClone, extrusionHeight );
141  delete polyClone;
142 }
143 
144 void QgsPolygon3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
145 {
146  if ( f.geometry().isNull() )
147  return;
148 
149  PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
150 
151  QgsGeometry geom = f.geometry();
152 
153  // segmentize curved geometries if necessary
154  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
155  geom = QgsGeometry( geom.constGet()->segmentize() );
156 
157  const QgsAbstractGeometry *g = geom.constGet();
158 
159  const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
160  bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
161  bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );
162 
163  float height = mSymbol->height();
164  float extrusionHeight = mSymbol->extrusionHeight();
165  if ( hasDDHeight )
166  height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, context.expressionContext(), height );
167  if ( hasDDExtrusion )
168  extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, context.expressionContext(), extrusionHeight );
169 
170  if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
171  {
172  QgsPolygon *polyClone = poly->clone();
173  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
174  }
175  else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
176  {
177  for ( int i = 0; i < mpoly->numGeometries(); ++i )
178  {
179  QgsPolygon *polyClone = static_cast< const QgsPolygon *>( mpoly->polygonN( i ) )->clone();
180  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
181  }
182  }
183  else if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( g ) )
184  {
185  for ( int i = 0; i < gc->numGeometries(); ++i )
186  {
187  const QgsAbstractGeometry *g2 = gc->geometryN( i );
189  {
190  QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
191  processPolygon( polyClone, f.id(), height, extrusionHeight, context, out );
192  }
193  }
194  }
195  else
196  qDebug() << "not a polygon";
197 }
198 
199 
200 void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
201 {
202  // create entity for selected and not selected
203  makeEntity( parent, context, outNormal, false );
204  makeEntity( parent, context, outSelected, true );
205 
206  mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
207  mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
208 
209  // add entity for edges
210  if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() )
211  {
212  QgsLineMaterial *mat = new QgsLineMaterial;
213  mat->setLineColor( mSymbol->edgeColor() );
214  mat->setLineWidth( mSymbol->edgeWidth() );
215 
216  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
217 
218  // geometry renderer
219  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
220  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
221  renderer->setGeometry( outEdges.createGeometry( entity ) );
222  renderer->setVertexCount( outEdges.indexes.count() );
223  renderer->setPrimitiveRestartEnabled( true );
224  renderer->setRestartIndexValue( 0 );
225 
226  // make entity
227  entity->addComponent( renderer );
228  entity->addComponent( mat );
229  entity->setParent( parent );
230  }
231 }
232 
233 
234 void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected )
235 {
236  if ( out.tessellator->dataVerticesCount() == 0 )
237  return; // nothing to show - no need to create the entity
238 
239  Qt3DRender::QMaterial *mat = material( mSymbol.get(), selected, context );
240 
241  // extract vertex buffer data from tessellator
242  QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
243  int nVerts = data.count() / out.tessellator->stride();
244 
245  const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->material() );
246 
247  QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, mSymbol->invertNormals(), mSymbol->addBackFaces(),
248  texturedMaterialSettings && texturedMaterialSettings->requiresTextureCoordinates() );
249  geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
250 
251  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
252  renderer->setGeometry( geometry );
253 
254  // make entity
255  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
256  entity->addComponent( renderer );
257  entity->addComponent( mat );
258  entity->setParent( parent );
259 
260  if ( !selected )
261  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
262 // cppcheck wrongly believes entity will leak
263 // cppcheck-suppress memleak
264 }
265 
266 
267 static Qt3DRender::QCullFace::CullingMode _qt3DcullingMode( Qgs3DTypes::CullingMode mode )
268 {
269  switch ( mode )
270  {
271  case Qgs3DTypes::NoCulling: return Qt3DRender::QCullFace::NoCulling;
272  case Qgs3DTypes::Front: return Qt3DRender::QCullFace::Front;
273  case Qgs3DTypes::Back: return Qt3DRender::QCullFace::Back;
274  case Qgs3DTypes::FrontAndBack: return Qt3DRender::QCullFace::FrontAndBack;
275  }
276  return Qt3DRender::QCullFace::NoCulling;
277 }
278 
279 // front/back side culling
280 static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, Qt3DRender::QMaterial *material )
281 {
282  auto techniques = material->effect()->techniques();
283  for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
284  {
285  auto renderPasses = ( *tit )->renderPasses();
286  for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
287  {
288  Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
289  cullFace->setMode( _qt3DcullingMode( cullingMode ) );
290  ( *rpit )->addRenderState( cullFace );
291  }
292  }
293 }
294 
295 Qt3DRender::QMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const
296 {
297  QgsMaterialContext materialContext;
298  materialContext.setIsSelected( isSelected );
299  materialContext.setSelectionColor( context.map().selectionColor() );
300 
301  Qt3DRender::QMaterial *material = symbol->material()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
302  applyCullingMode( symbol->cullingMode(), material );
303  return material;
304 }
305 
306 
307 // --------------
308 
309 
310 namespace Qgs3DSymbolImpl
311 {
312 
313 
314  QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
315  {
316  const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast< const QgsPolygon3DSymbol * >( symbol );
317  if ( !polygonSymbol )
318  return nullptr;
319 
320  return new QgsPolygon3DSymbolHandler( polygonSymbol, layer->selectedFeatureIds() );
321  }
322 
323  Qt3DCore::QEntity *entityForPolygon3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
324  {
325  QgsFeature3DHandler *handler = handlerForPolygon3DSymbol( layer, &symbol );
326  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
327  delete handler;
328  return e;
329  }
330 
331 }
332 
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
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
qgstessellatedpolygongeometry.h
QgsPhongTexturedMaterialSettings::textureRotation
float textureRotation() const
Returns the texture rotation, in degrees.
Definition: qgsphongtexturedmaterialsettings.cpp:61
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:34
qgslinestring.h
QgsPolygon::clone
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspolygon.cpp:54
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:87
qgsphongtexturedmaterialsettings.h
QgsGeometry::isNull
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsPolygon3DSymbol::material
QgsAbstractMaterialSettings * material() const
Returns material used for shading of the symbol.
Definition: qgspolygon3dsymbol.cpp:130
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
QgsMaterialContext::setSelectionColor
void setSelectionColor(const QColor &color)
Sets the color for representing materials in a selected state.
Definition: qgsabstractmaterialsettings.h:84
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:67
QgsPolygon3DSymbol
3 3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
Definition: qgspolygon3dsymbol.h:38
QgsAbstract3DSymbol::PropertyHeight
@ PropertyHeight
Height (altitude)
Definition: qgsabstract3dsymbol.h:70
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
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
QgsMaterialContext
3 Context settings for a material.
Definition: qgsabstractmaterialsettings.h:55
qgslinematerial_p.h
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:77
QgsAbstract3DSymbol
3 Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
Definition: qgsabstract3dsymbol.h:46
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:36
Qgs3DTypes::CullingMode
CullingMode
Triangle culling mode.
Definition: qgs3dtypes.h:50
QgsAbstract3DSymbol::PropertyExtrusionHeight
@ PropertyExtrusionHeight
Extrusion height (zero means no extrusion)
Definition: qgsabstract3dsymbol.h:71
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:193
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:112
Qgs3DTypes::Front
@ Front
Will render only back faces of triangles.
Definition: qgs3dtypes.h:52
QgsVectorLayer::selectedFeatureIds
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3441
Qgs3DTypes::FrontAndBack
@ FrontAndBack
Will not render anything.
Definition: qgs3dtypes.h:54
qgstessellator.h
Qgs3DMapSettings
3 Definition of the world
Definition: qgs3dmapsettings.h:54
QgsMultiPolygon
Multi polygon geometry collection.
Definition: qgsmultipolygon.h:32
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:100
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:74
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
QgsPhongTexturedMaterialSettings
3 A phong shading model with diffuse texture map.
Definition: qgsphongtexturedmaterialsettings.h:36
QgsPropertyCollection
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition: qgspropertycollection.h:319
qgs3dmapsettings.h
QgsAbstractMaterialSettings::toMaterial
virtual Qt3DRender::QMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const =0
Creates a new QMaterial object representing the material settings.
qgspolygon3dsymbol_p.h
QgsWkbTypes::isCurvedType
static bool isCurvedType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:881
qgspolygon3dsymbol.h
QgsMaterialContext::setIsSelected
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected state.
Definition: qgsabstractmaterialsettings.h:70
Qgs3DTypes::Back
@ Back
Will render only front faces of triangles (recommended when input data are consistent)
Definition: qgs3dtypes.h:53
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsTessellator
Class that takes care of tessellation of polygons into triangles.
Definition: qgstessellator.h:41
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsPolygon3DSymbol::cullingMode
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
Definition: qgspolygon3dsymbol.h:89
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:74
qgsimagetexture.h
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsPropertyCollection::isActive
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Definition: qgspropertycollection.cpp:268
qgslinevertexdata_p.h
Qgs3DTypes::NoCulling
@ NoCulling
Will render both front and back faces of triangles.
Definition: qgs3dtypes.h:51
QgsTessellatedPolygonGeometry
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry.
Definition: qgstessellatedpolygongeometry.h:45
QgsPhongTexturedMaterialSettings::requiresTextureCoordinates
bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation....
Definition: qgsphongtexturedmaterialsettings.h:85
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28