QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 
201 
202 // --------------
203 
204 
205 class QgsSimpleLine3DSymbolHandler : public QgsFeature3DHandler
206 {
207  public:
208  QgsSimpleLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
209  : mSymbol( symbol ), mSelectedIds( selectedIds )
210  {
211  }
212 
213  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
214  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
215  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
216 
217  private:
218 
219  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
220  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
221 
222  // input specific for this class
223  const QgsLine3DSymbol &mSymbol;
224  // inputs - generic
225  QgsFeatureIds mSelectedIds;
226 
227  // outputs
228  QgsLineVertexData outNormal;
229  QgsLineVertexData outSelected;
230 };
231 
232 
233 
234 bool QgsSimpleLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
235 {
236  Q_UNUSED( attributeNames )
237 
238  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
239  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
240 
241  return true;
242 }
243 
244 void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
245 {
246  Q_UNUSED( context )
247  if ( f.geometry().isNull() )
248  return;
249 
250  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
251 
252  QgsGeometry geom = f.geometry();
253  const QgsAbstractGeometry *g = geom.constGet();
254  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
255  {
256  out.addLineString( *ls );
257  }
258  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
259  {
260  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
261  {
262  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
263  out.addLineString( *ls );
264  }
265  }
266 }
267 
268 void QgsSimpleLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
269 {
270  // create entity for selected and not selected
271  makeEntity( parent, context, outNormal, false );
272  makeEntity( parent, context, outSelected, true );
273 
274  updateZRangeFromPositions( outNormal.vertices );
275  updateZRangeFromPositions( outSelected.vertices );
276 }
277 
278 
279 void QgsSimpleLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
280 {
281  if ( out.indexes.isEmpty() )
282  return;
283 
284  // material (only ambient color is used for the color)
285 
286  Qt3DExtras::QPhongMaterial *mat = _material( mSymbol );
287  if ( selected )
288  {
289  // update the material with selection colors
290  mat->setAmbient( context.map().selectionColor() );
291  }
292 
293  // geometry renderer
294 
295  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
296 
297  Qt3DRender::QGeometry *geom = out.createGeometry( entity );
298 
299  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
300  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip );
301  renderer->setGeometry( geom );
302  renderer->setVertexCount( out.indexes.count() );
303  renderer->setPrimitiveRestartEnabled( true );
304  renderer->setRestartIndexValue( 0 );
305 
306  // make entity
307  entity->addComponent( renderer );
308  entity->addComponent( mat );
309  entity->setParent( parent );
310 }
311 
312 
313 
314 // --------------
315 
316 
317 class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
318 {
319  public:
320  QgsThickLine3DSymbolHandler( const QgsLine3DSymbol &symbol, const QgsFeatureIds &selectedIds )
321  : mSymbol( symbol ), mSelectedIds( selectedIds )
322  {
323  }
324 
325  bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) override;
326  void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) override;
327  void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
328 
329  private:
330 
331 
332  void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
333  Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
334 
335  // input specific for this class
336  const QgsLine3DSymbol &mSymbol;
337  // inputs - generic
338  QgsFeatureIds mSelectedIds;
339 
340  // outputs
341  QgsLineVertexData outNormal;
342  QgsLineVertexData outSelected;
343 };
344 
345 
346 
347 bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames )
348 {
349  Q_UNUSED( attributeNames )
350 
351  outNormal.withAdjacency = true;
352  outSelected.withAdjacency = true;
353  outNormal.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
354  outSelected.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
355 
356  return true;
357 }
358 
359 void QgsThickLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context )
360 {
361  Q_UNUSED( context )
362  if ( f.geometry().isNull() )
363  return;
364 
365  QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
366 
367  QgsGeometry geom = f.geometry();
368  const QgsAbstractGeometry *g = geom.constGet();
369  if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
370  {
371  out.addLineString( *ls );
372  }
373  else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
374  {
375  for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
376  {
377  const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) );
378  out.addLineString( *ls );
379  }
380  }
381 }
382 
383 void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
384 {
385  // create entity for selected and not selected
386  makeEntity( parent, context, outNormal, false );
387  makeEntity( parent, context, outSelected, true );
388 
389  updateZRangeFromPositions( outNormal.vertices );
390  updateZRangeFromPositions( outSelected.vertices );
391 }
392 
393 
394 void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
395 {
396  if ( out.indexes.isEmpty() )
397  return;
398 
399  // material (only ambient color is used for the color)
400 
401  QgsLineMaterial *mat = new QgsLineMaterial;
402  mat->setLineColor( mSymbol.material().ambient() );
403  mat->setLineWidth( mSymbol.width() );
404  if ( selected )
405  {
406  // update the material with selection colors
407  mat->setLineColor( context.map().selectionColor() );
408  }
409 
410  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
411 
412  // geometry renderer
413  Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
414  renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
415  renderer->setGeometry( out.createGeometry( entity ) );
416  renderer->setVertexCount( out.indexes.count() );
417  renderer->setPrimitiveRestartEnabled( true );
418  renderer->setRestartIndexValue( 0 );
419 
420  // make entity
421  entity->addComponent( renderer );
422  entity->addComponent( mat );
423  entity->setParent( parent );
424 }
425 
426 
427 // --------------
428 
429 
430 namespace Qgs3DSymbolImpl
431 {
432 
433  QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
434  {
435  if ( symbol.renderAsSimpleLines() )
436  return new QgsThickLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
437  //return new QgsSimpleLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
438  else
439  return new QgsBufferedLine3DSymbolHandler( symbol, layer->selectedFeatureIds() );
440  }
441 
442  Qt3DCore::QEntity *entityForLine3DSymbol( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol )
443  {
444  QgsFeature3DHandler *handler = handlerForLine3DSymbol( layer, symbol );
445  Qt3DCore::QEntity *e = entityFromHandler( handler, map, layer );
446  delete handler;
447  return e;
448  }
449 }
450 
QgsFeatureId id
Definition: qgsfeature.h:64
float shininess() const
Returns shininess of the surface.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QColor specular() const
Returns specular color component.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
Multi line string geometry collection.
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
EndCapStyle
End cap styles for buffers.
Definition: qgsgeometry.h:1101
3 Definition of the world
Class that takes care of tessellation of polygons into triangles.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
QgsPhongMaterialSettings material() const
Returns material used for shading of the symbol.
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
3 3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer wi...
Abstract base class for all geometries.
Does vector analysis using the geos library and handles import, export, exception handling*...
Definition: qgsgeos.h:103
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
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.
int numGeometries() const
Returns the number of geometries within the collection.
Use rounded joins.
Definition: qgsgeometry.h:1112
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer) ...
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
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QColor ambient() const
Returns ambient color component.
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
JoinStyle
Join styles for buffers.
Definition: qgsgeometry.h:1110
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.