QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsline3dsymbol_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsline3dsymbol_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 "qgsline3dsymbol_p.h"
17 
18 #include "qgsline3dsymbol.h"
19 #include "qgslinematerial_p.h"
20 #include "qgslinevertexdata_p.h"
22 #include "qgstessellator.h"
23 #include "qgs3dmapsettings.h"
24 //#include "qgsterraingenerator.h"
25 #include "qgs3dutils.h"
26 
27 #include "qgsvectorlayer.h"
28 #include "qgsmultilinestring.h"
29 #include "qgsmultipolygon.h"
30 #include "qgsgeos.h"
31 
32 #include <Qt3DExtras/QPhongMaterial>
33 #include <Qt3DRender/QAttribute>
34 #include <Qt3DRender/QBuffer>
35 #include <Qt3DRender/QGeometryRenderer>
36 
38 
39 
40 static Qt3DExtras::QPhongMaterial *_material( const QgsLine3DSymbol &symbol )
41 {
42  Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
43 
44  material->setAmbient( symbol.material().ambient() );
45  material->setDiffuse( symbol.material().diffuse() );
46  material->setSpecular( symbol.material().specular() );
47  material->setShininess( symbol.material().shininess() );
48 
49  return material;
50 }
51 
52 
53 // -----------
54 
55 
56 class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler
57 {
58  public:
59  QgsBufferedLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
60  : mSymbol( symbol ), mSelectedIds( selectedIds ) {}
61 
62  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
63  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
64  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
65 
66  private:
67 
69  struct LineData
70  {
71  std::unique_ptr<QgsTessellator> tessellator;
72  QVector<QgsFeatureId> triangleIndexFids;
73  QVector<uint> triangleIndexStartingIndices;
74  };
75 
76  void processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out );
77 
78  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
79 
80  // input specific for this class
81  const QgsLine3DSymbol &mSymbol;
82  // inputs - generic
83  QgsFeatureIds mSelectedIds;
84 
85  // outputs
86  LineData outNormal;
87  LineData outSelected;
88 };
89 
90 
91 
92 bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
93 {
94  Q_UNUSED( attributeNames )
95 
96  outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true ) );
97  outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true ) );
98 
99  return true;
100 }
101 
102 void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
103 {
104  if ( f.geometry().isNull() )
105  return;
106 
107  LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
108 
109  QgsGeometry geom = f.geometry();
110 
111  // segmentize curved geometries if necessary
112  if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
113  geom = QgsGeometry( geom.constGet()->segmentize() );
114 
115  const QgsAbstractGeometry *g = geom.constGet();
116 
117  // TODO: configurable
118  const int nSegments = 4;
121  const double mitreLimit = 0;
122 
123  QgsGeos engine( g );
124  QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory
125 
126  if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon )
127  {
128  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
129  processPolygon( polyBuffered, f.id(), mSymbol.height(), mSymbol.extrusionHeight(), context, out );
130  }
131  else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
132  {
133  QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
134  for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
135  {
136  QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i );
137  Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon );
138  QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
139  processPolygon( polyBuffered, f.id(), mSymbol.height(), mSymbol.extrusionHeight(), context, out );
140  }
141  delete buffered;
142  }
143 }
144 
145 void QgsBufferedLine3DSymbolHandler::processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out )
146 {
147  Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), height, context.map() );
148 
149  Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
150  uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
151  out.triangleIndexStartingIndices.append( startingTriangleIndex );
152  out.triangleIndexFids.append( fid );
153  out.tessellator->addPolygon( *polyBuffered, extrusionHeight );
154  delete polyBuffered;
155 }
156 
157 void QgsBufferedLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
158 {
159  // create entity for selected and not selected
160  makeEntity( parent, context, outNormal, false );
161  makeEntity( parent, context, outSelected, true );
162 
163  mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
164  mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
165 }
166 
167 
168 void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
169 {
170  if ( out.tessellator->dataVerticesCount() == 0 )
171  return; // nothing to show - no need to create the entity
172 
173  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
174  if ( selected )
175  {
176  // update the material with selection colors
177  mat->setDiffuse( context.map().selectionColor() );
178  mat->setAmbient( context.map().selectionColor().darker() );
179  }
180 
181  // extract vertex buffer data from tessellator
182  QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
183  int nVerts = data.count() / out.tessellator->stride();
184 
186  geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
187 
188  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
189  renderer->setGeometry( geometry );
190 
191  // make entity
192  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
193  entity->addComponent( renderer );
194  entity->addComponent( mat );
195  entity->setParent( parent );
196 
197  if ( !selected )
198  entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( QStringLiteral( "main" ) ); // temporary measure to distinguish between "selected" and "main"
199 
200  // cppcheck wrongly believes entity will leak
201  // cppcheck-suppress memleak
202 }
203 
204 
205 // --------------
206 
207 
208 class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
209 {
210  public:
211  QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
212  : mSymbol( symbol ), mSelectedIds( selectedIds )
213  {
214  }
215 
216  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
217  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
218  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
219 
220  private:
221 
222  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
223  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
224 
225  // input specific for this class
226  const QgsLine3DSymbol &mSymbol;
227  // inputs - generic
228  QgsFeatureIds mSelectedIds;
229 
230  // outputs
231  QgsLineVertexData outNormal;
232  QgsLineVertexData outSelected;
233 };
234 
235 
236 
237 bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
238 {
239  Q_UNUSED( attributeNames )
240 
241  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
242  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
243 
244  return true;
245 }
246 
247 void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
248 {
249  Q_UNUSED( context )
250  if ( f.geometry().isNull() )
251  return;
252 
253  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
254 
255  QgsGeometry geom = f.geometry();
256  const QgsAbstractGeometry *g = geom.constGet();
257  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
258  {
259  out.addLineString( *ls );
260  }
261  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
262  {
263  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
264  {
265  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
266  out.addLineString( *ls );
267  }
268  }
269 }
270 
271 void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
272 {
273  // create entity for selected and not selected
274  makeEntity( parent, context, outNormal, false );
275  makeEntity( parent, context, outSelected, true );
276 
277  updateZRangeFromPositions( outNormal.vertices );
278  updateZRangeFromPositions( outSelected.vertices );
279 }
280 
281 
282 void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
283 {
284  if ( out.indexes.isEmpty() )
285  return;
286 
287  // material (only ambient color is used for the color)
288 
289  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
290  if ( selected )
291  {
292  // update the material with selection colors
293  mat->setAmbient( context.map().selectionColor() );
294  }
295 
296  // geometry renderer
297 
298  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
299 
300  Qt3DRender::QGeometry *geom = out.createGeometry( entity );
301 
302  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
303  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
304  renderer->setGeometry( geom );
305  renderer->setVertexCount( out.indexes.count() );
306  renderer->setPrimitiveRestartEnabled( true );
307  renderer->setRestartIndexValue( 0 );
308 
309  // make entity
310  entity->addComponent( renderer );
311  entity->addComponent( mat );
312  entity->setParent( parent );
313 }
314 
315 
316 
317 // --------------
318 
319 
320 class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
321 {
322  public:
323  QgsThickLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
324  : mSymbol( symbol ), mSelectedIds( selectedIds )
325  {
326  }
327 
328  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
329  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
330  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
331 
332  private:
333 
334 
335  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
336  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
337 
338  // input specific for this class
339  const QgsLine3DSymbol &mSymbol;
340  // inputs - generic
341  QgsFeatureIds mSelectedIds;
342 
343  // outputs
344  QgsLineVertexData outNormal;
345  QgsLineVertexData outSelected;
346 };
347 
348 
349 
350 bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
351 {
352  Q_UNUSED( attributeNames )
353 
354  outNormal.withAdjacency = true;
355  outSelected.withAdjacency = true;
356  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
357  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
358 
359  return true;
360 }
361 
362 void QgsThickLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
363 {
364  Q_UNUSED( context )
365  if ( f.geometry().isNull() )
366  return;
367 
368  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
369 
370  QgsGeometry geom = f.geometry();
371  const QgsAbstractGeometry *g = geom.constGet();
372  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
373  {
374  out.addLineString( *ls );
375  }
376  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
377  {
378  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
379  {
380  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
381  out.addLineString( *ls );
382  }
383  }
384 }
385 
386 void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
387 {
388  // create entity for selected and not selected
389  makeEntity( parent, context, outNormal, false );
390  makeEntity( parent, context, outSelected, true );
391 
392  updateZRangeFromPositions( outNormal.vertices );
393  updateZRangeFromPositions( outSelected.vertices );
394 }
395 
396 
397 void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
398 {
399  if ( out.indexes.isEmpty() )
400  return;
401 
402  // material (only ambient color is used for the color)
403 
404  QgsLineMaterial *mat = new QgsLineMaterial;
405  mat->setLineColor( mSymbol.material().ambient() );
406  mat->setLineWidth( mSymbol.width() );
407  if ( selected )
408  {
409  // update the material with selection colors
410  mat->setLineColor( context.map().selectionColor() );
411  }
412 
413  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
414 
415  // geometry renderer
416  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
417  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
418  renderer->setGeometry( out.createGeometry( entity ) );
419  renderer->setVertexCount( out.indexes.count() );
420  renderer->setPrimitiveRestartEnabled( true );
421  renderer->setRestartIndexValue( 0 );
422 
423  // make entity
424  entity->addComponent( renderer );
425  entity->addComponent( mat );
426  entity->setParent( parent );
427 }
428 
429 
430 // --------------
431 
432 
433 namespace Qgs3DSymbolImpl
434 {
435 
436  QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
437  {
438  if ( symbol.renderAsSimpleLines() )
439  return new QgsThickLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
440  //return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
441  else
442  return new QgsBufferedLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
443  }
444 
445  Qt3DCore::QEntity *entityForLine3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
446  {
447  QgsFeature3DHandler *handler = handlerForLine3DSymbol( layer, symbol );
448  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
449  delete handler;
450  return e;
451  }
452 }
453 
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
QgsGeometry::CapRound
@ CapRound
Round cap.
Definition: qgsgeometry.h:1118
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
QgsWkbTypes::MultiPolygon
@ MultiPolygon
Definition: qgswkbtypes.h:77
qgsline3dsymbol.h
QgsGeometryCollection::numGeometries
int numGeometries() const
Returns the number of geometries within the collection.
Definition: qgsgeometrycollection.h:51
QgsMultiLineString
Multi line string geometry collection.
Definition: qgsmultilinestring.h:29
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
QgsGeometry::EndCapStyle
EndCapStyle
End cap styles for buffers.
Definition: qgsgeometry.h:1116
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
QgsLine3DSymbol::renderAsSimpleLines
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer)
Definition: qgsline3dsymbol.h:86
qgslinematerial_p.h
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
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
QgsVectorLayer::selectedFeatureIds
const Q_INVOKABLE QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3433
QgsGeos
Definition: qgsgeos.h:103
QgsLine3DSymbol
Definition: qgsline3dsymbol.h:35
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
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsLine3DSymbol::material
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
Definition: qgsline3dsymbol.h:91
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
qgs3dmapsettings.h
QgsGeometryCollection::geometryN
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Definition: qgsgeometrycollection.h:79
QgsGeometry
Definition: qgsgeometry.h:122
QgsTessellator
Definition: qgstessellator.h:40
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsGeometry::JoinStyleRound
@ JoinStyleRound
Use rounded joins.
Definition: qgsgeometry.h:1127
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
QgsGeometry::JoinStyle
JoinStyle
Join styles for buffers.
Definition: qgsgeometry.h:1125
QgsPhongMaterialSettings::shininess
float shininess() const
Returns shininess of the surface.
Definition: qgsphongmaterialsettings.h:52
QgsFeature
Definition: qgsfeature.h:55
qgslinevertexdata_p.h
qgsline3dsymbol_p.h
QgsTessellatedPolygonGeometry
Definition: qgstessellatedpolygongeometry.h:42
QgsWkbTypes::flatType
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:701
qgsmultilinestring.h
qgsgeos.h
QgsFeatureId
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25