QGIS API Documentation  3.6.0-Noosa (5873452)
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 
23 #include <Qt3DCore/QTransform>
24 #include <Qt3DExtras/QPhongMaterial>
25 #include <Qt3DRender/QEffect>
26 #include <Qt3DRender/QTechnique>
27 #include <Qt3DRender/QCullFace>
28 #include <Qt3DRender/QGeometryRenderer>
29 
30 #include "qgsvectorlayer.h"
31 #include "qgsmultipolygon.h"
32 
33 
35 
36 
37 class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
38 {
39  public:
40  QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol &symbol, const QgsFeatureIds &selectedIds )
41  : mSymbol( symbol ), mSelectedIds( selectedIds ) {}
42 
43  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
44  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
45  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
46 
47  private:
48 
50  struct PolygonData
51  {
52  QList<QgsPolygon *> polygons;
53  QList<QgsFeatureId> fids;
54  QList<float> extrusionHeightPerPolygon; // will stay empty if not needed per polygon
55  };
56 
57  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
58  Qt3DExtras::QPhongMaterial *material( const QgsPolygon3DSymbol &symbol ) const;
59 
60  // input specific for this class
61  const QgsPolygon3DSymbol &mSymbol;
62  // inputs - generic
63  QgsFeatureIds mSelectedIds;
64 
65  // outputs
66  PolygonData outNormal;
67  PolygonData outSelected;
68 };
69 
70 
71 bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
72 {
73  QSet<QString> attrs = mSymbol.dataDefinedProperties().referencedFields( context.expressionContext() );
74  attributeNames.unite( attrs );
75  return true;
76 }
77 
78 void QgsPolygon3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
79 {
80  if ( f.geometry().isNull() )
81  return;
82 
83  PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
84 
85  QgsGeometry geom = f.geometry();
86 
87  // segmentize curved geometries if necessary
88  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
89  geom = QgsGeometry( geom.constGet()->segmentize() );
90 
91  const QgsAbstractGeometry *g = geom.constGet();
92 
93  const QgsPropertyCollection &ddp = mSymbol.dataDefinedProperties();
94  bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
95  bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight );
96 
97  float height = mSymbol.height();
98  float extrusionHeight = mSymbol.extrusionHeight();
99  if ( hasDDHeight )
100  height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, context.expressionContext(), height );
101  if ( hasDDExtrusion )
102  extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, context.expressionContext(), extrusionHeight );
103 
104  if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) )
105  {
106  QgsPolygon *polyClone = poly->clone();
107  Qgs3DUtils::clampAltitudes( polyClone, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), height, context.map() );
108  out.polygons.append( polyClone );
109  out.fids.append( f.id() );
110  if ( hasDDExtrusion )
111  out.extrusionHeightPerPolygon.append( extrusionHeight );
112  }
113  else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) )
114  {
115  for ( int i = 0; i < mpoly->numGeometries(); ++i )
116  {
117  const QgsAbstractGeometry *g2 = mpoly->geometryN( i );
118  Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon );
119  QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
120  Qgs3DUtils::clampAltitudes( polyClone, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), height, context.map() );
121  out.polygons.append( polyClone );
122  out.fids.append( f.id() );
123  if ( hasDDExtrusion )
124  out.extrusionHeightPerPolygon.append( extrusionHeight );
125  }
126  }
127  else
128  qDebug() << "not a polygon";
129 }
130 
131 
132 void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
133 {
134  // create entity for selected and not selected
135  makeEntity( parent, context, outNormal, false );
136  makeEntity( parent, context, outSelected, true );
137 }
138 
139 
140 void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected )
141 {
142  if ( out.polygons.isEmpty() )
143  return; // nothing to show - no need to create the entity
144 
145  Qt3DExtras::QPhongMaterial *mat = material( mSymbol );
146  if ( selected )
147  {
148  // update the material with selection colors
149  mat->setDiffuse( context.map().selectionColor() );
150  mat->setAmbient( context.map().selectionColor().darker() );
151  }
152 
153  QgsPointXY origin( context.map().origin().x(), context.map().origin().y() );
155  geometry->setInvertNormals( mSymbol.invertNormals() );
156  geometry->setAddBackFaces( mSymbol.addBackFaces() );
157  geometry->setPolygons( out.polygons, out.fids, origin, mSymbol.extrusionHeight(), out.extrusionHeightPerPolygon );
158 
159  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
160  renderer->setGeometry( geometry );
161 
162  // make entity
163  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
164  entity->addComponent( renderer );
165  entity->addComponent( mat );
166  entity->setParent( parent );
167 
168  if ( !selected )
169  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
170 }
171 
172 
173 static Qt3DRender::QCullFace::CullingMode _qt3DcullingMode( Qgs3DTypes::CullingMode mode )
174 {
175  switch ( mode )
176  {
177  case Qgs3DTypes::NoCulling: return Qt3DRender::QCullFace::NoCulling;
178  case Qgs3DTypes::Front: return Qt3DRender::QCullFace::Front;
179  case Qgs3DTypes::Back: return Qt3DRender::QCullFace::Back;
180  case Qgs3DTypes::FrontAndBack: return Qt3DRender::QCullFace::FrontAndBack;
181  }
182  return Qt3DRender::QCullFace::NoCulling;
183 }
184 
185 Qt3DExtras::QPhongMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol &symbol ) const
186 {
187  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
188 
189  // front/back side culling
190  auto techniques = material->effect()->techniques();
191  for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
192  {
193  auto renderPasses = ( *tit )->renderPasses();
194  for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
195  {
196  Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
197  cullFace->setMode( _qt3DcullingMode( symbol.cullingMode() ) );
198  ( *rpit )->addRenderState( cullFace );
199  }
200  }
201 
202  material->setAmbient( symbol.material().ambient() );
203  material->setDiffuse( symbol.material().diffuse() );
204  material->setSpecular( symbol.material().specular() );
205  material->setShininess( symbol.material().shininess() );
206  return material;
207 }
208 
209 
210 // --------------
211 
212 
213 namespace Qgs3DSymbolImpl
214 {
215 
216 
217  QgsFeature3DHandler *handlerForPolygon3DSymbol( QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
218  {
219  return new QgsPolygon3DSymbolHandler( symbol, layer->selectedFeatureIds() );
220  }
221 
222  Qt3DCore::QEntity *entityForPolygon3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol )
223  {
224  QgsFeature3DHandler *handler = handlerForPolygon3DSymbol( layer, symbol );
225  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
226  delete handler;
227  return e;
228  }
229 
230 }
231 
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.
A class to represent a 2D point.
Definition: qgspointxy.h:43
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:106
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.
Will render only front faces of triangles (recommended when input data are consistent) ...
Definition: qgs3dtypes.h:53
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
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
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.
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:608
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:174
QColor diffuse() const
Returns diffuse color component.
QgsGeometry geometry
Definition: qgsfeature.h:67
Polygon geometry type.
Definition: qgspolygon.h:31
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:429
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.