QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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"
32#include "qgspolygon.h"
34#include "qgsmessagelog.h"
35
36#include <Qt3DCore/QTransform>
37#include <Qt3DExtras/QPhongMaterial>
38#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
39#include <Qt3DRender/QAttribute>
40#include <Qt3DRender/QBuffer>
41
42typedef Qt3DRender::QAttribute Qt3DQAttribute;
43typedef Qt3DRender::QBuffer Qt3DQBuffer;
44typedef Qt3DRender::QGeometry Qt3DQGeometry;
45#else
46#include <Qt3DCore/QAttribute>
47#include <Qt3DCore/QBuffer>
48
49typedef Qt3DCore::QAttribute Qt3DQAttribute;
50typedef Qt3DCore::QBuffer Qt3DQBuffer;
51typedef Qt3DCore::QGeometry Qt3DQGeometry;
52#endif
53#include <Qt3DRender/QGeometryRenderer>
54
56
57// -----------
58
59
60class QgsBufferedLine3DSymbolHandler : public QgsFeature3DHandler
61{
62 public:
63 QgsBufferedLine3DSymbolHandler( const QgsLine3DSymbol *symbol, const QgsFeatureIds &selectedIds )
64 : mSymbol( static_cast< QgsLine3DSymbol *>( symbol->clone() ) )
65 , mSelectedIds( selectedIds ) {}
66
67 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin ) override;
68 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
69 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
70
71 private:
72
74 struct LineData
75 {
76 std::unique_ptr<QgsTessellator> tessellator;
77 QVector<QgsFeatureId> triangleIndexFids;
78 QVector<uint> triangleIndexStartingIndices;
79 };
80
81 void processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out );
82
83 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected );
84
85 // input specific for this class
86 std::unique_ptr< QgsLine3DSymbol > mSymbol;
87 // inputs - generic
88 QgsFeatureIds mSelectedIds;
89
91 QgsVector3D mChunkOrigin;
92
93 // outputs
94 LineData outNormal;
95 LineData outSelected;
96};
97
98
99
100bool QgsBufferedLine3DSymbolHandler::prepare( const Qgs3DRenderContext &, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin )
101{
102 Q_UNUSED( attributeNames )
103
104 mChunkOrigin = chunkOrigin;
105
106 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
107
108 outNormal.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true,
109 false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false,
110 3,
111 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
112 outSelected.tessellator.reset( new QgsTessellator( chunkOrigin.x(), chunkOrigin.y(), true,
113 false, false, false, texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false,
114 3,
115 texturedMaterialSettings ? texturedMaterialSettings->textureRotation() : 0 ) );
116
117 outNormal.tessellator->setOutputZUp( true );
118 outSelected.tessellator->setOutputZUp( true );
119
120 return true;
121}
122
123void QgsBufferedLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
124{
125 if ( f.geometry().isNull() )
126 return;
127
128 LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
129
130 QgsGeometry geom = f.geometry();
132
133 // segmentize curved geometries if necessary
135 {
136 geom = QgsGeometry( g->segmentize() );
137 g = geom.constGet()->simplifiedTypeRef();
138 }
139
140 // TODO: configurable
141 const int nSegments = 4;
143 const Qgis::JoinStyle joinStyle = Qgis::JoinStyle::Round;
144 const double mitreLimit = 0;
145
146 const QgsGeos engine( g );
147
148 double width = mSymbol->width();
149 if ( qgsDoubleNear( width, 0 ) )
150 {
151 // a zero-width buffered line should be treated like a "wall" or "fence" -- we fake this by bumping the width to a very tiny amount,
152 // so that we get a very narrow polygon shape to work with...
153 width = 0.001;
154 }
155
156 QgsAbstractGeometry *buffered = engine.buffer( width / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory
157 if ( !buffered )
158 return;
159
161 {
162 QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
163 processPolygon( polyBuffered, f.id(), mSymbol->offset(), mSymbol->extrusionHeight(), context, out );
164 }
165 else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == Qgis::WkbType::MultiPolygon )
166 {
167 QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered );
168 for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i )
169 {
170 QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( mpolyBuffered->polygonN( i ) )->clone(); // need to clone individual geometry parts
171 processPolygon( polyBuffered, f.id(), mSymbol->offset(), mSymbol->extrusionHeight(), context, out );
172 }
173 delete buffered;
174 }
175 mFeatureCount++;
176}
177
178void QgsBufferedLine3DSymbolHandler::processPolygon( QgsPolygon *polyBuffered, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, LineData &out )
179{
180 Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), height, context );
181
182 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
183 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
184 out.triangleIndexStartingIndices.append( startingTriangleIndex );
185 out.triangleIndexFids.append( fid );
186 out.tessellator->addPolygon( *polyBuffered, extrusionHeight );
187 if ( !out.tessellator->error().isEmpty() )
188 {
189 QgsMessageLog::logMessage( out.tessellator->error(), QObject::tr( "3D" ) );
190 }
191
192 delete polyBuffered;
193}
194
195void QgsBufferedLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
196{
197 // create entity for selected and not selected
198 makeEntity( parent, context, outNormal, false );
199 makeEntity( parent, context, outSelected, true );
200
201 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
202 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
203}
204
205
206void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, LineData &out, bool selected )
207{
208 if ( out.tessellator->dataVerticesCount() == 0 )
209 return; // nothing to show - no need to create the entity
210
211 QgsMaterialContext materialContext;
212 materialContext.setIsSelected( selected );
213 materialContext.setSelectionColor( context.selectionColor() );
214 QgsMaterial *mat = mSymbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
215
216 // extract vertex buffer data from tessellator
217 const QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
218 const int nVerts = data.count() / out.tessellator->stride();
219
220 const QgsPhongTexturedMaterialSettings *texturedMaterialSettings = dynamic_cast< const QgsPhongTexturedMaterialSettings * >( mSymbol->materialSettings() );
221
222 QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, false, false,
223 texturedMaterialSettings ? texturedMaterialSettings->requiresTextureCoordinates() : false );
224 geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
225
226 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
227 renderer->setGeometry( geometry );
228
229 // add transform (our geometry has coordinates relative to mChunkOrigin)
230 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
231 QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D();
232 tr->setTranslation( nodeTranslation );
233
234 // make entity
235 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
236 entity->addComponent( renderer );
237 entity->addComponent( mat );
238 entity->addComponent( tr );
239 entity->setParent( parent );
240
241 if ( !selected )
242 renderer->setProperty( Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG, Qgs3DTypes::Main3DRenderer ); // temporary measure to distinguish between "selected" and "main"
243
244 // cppcheck wrongly believes entity will leak
245 // cppcheck-suppress memleak
246}
247
248
249// --------------
250
251
252class QgsThickLine3DSymbolHandler : public QgsFeature3DHandler
253{
254 public:
255 QgsThickLine3DSymbolHandler( const QgsLine3DSymbol *symbol, const QgsFeatureIds &selectedIds )
256 : mSymbol( static_cast< QgsLine3DSymbol * >( symbol->clone() ) )
257 , mSelectedIds( selectedIds )
258 {
259 }
260
261 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin ) override;
262 void processFeature( const QgsFeature &feature, const Qgs3DRenderContext &context ) override;
263 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
264
265 private:
266
267
268 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected );
269 Qt3DExtras::QPhongMaterial *material( const QgsLine3DSymbol &symbol ) const;
270 void processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsLineVertexData &out );
271
272 // input specific for this class
273 std::unique_ptr< QgsLine3DSymbol > mSymbol;
274 // inputs - generic
275 QgsFeatureIds mSelectedIds;
276
278 QgsVector3D mChunkOrigin;
279
280 // outputs
281 QgsLineVertexData outNormal;
282 QgsLineVertexData outSelected;
283};
284
285
286
287bool QgsThickLine3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsVector3D &chunkOrigin )
288{
289 Q_UNUSED( attributeNames )
290
291 mChunkOrigin = chunkOrigin;
292
293 outNormal.withAdjacency = true;
294 outSelected.withAdjacency = true;
295 outNormal.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context, chunkOrigin );
296 outSelected.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), mSymbol->offset(), context, chunkOrigin );
297
298 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
299 attributeNames.unite( attrs );
300 attrs = mSymbol->materialSettings()->dataDefinedProperties().referencedFields( context.expressionContext() );
301 attributeNames.unite( attrs );
302
303 if ( mSymbol->materialSettings()->dataDefinedProperties().isActive( QgsAbstractMaterialSettings::Property::Ambient ) )
304 {
305 processMaterialDatadefined( outNormal.vertices.size(), context.expressionContext(), outNormal );
306 processMaterialDatadefined( outSelected.vertices.size(), context.expressionContext(), outSelected );
307 }
308
309 return true;
310}
311
312void QgsThickLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
313{
314 Q_UNUSED( context )
315 if ( f.geometry().isNull() )
316 return;
317
318 QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
319
320 const int oldVerticesCount = out.vertices.size();
321
322 QgsGeometry geom = f.geometry();
324
325 // segmentize curved geometries if necessary
327 {
328 geom = QgsGeometry( g->segmentize() );
329 g = geom.constGet()->simplifiedTypeRef();
330 }
331
332 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
333 {
334 out.addLineString( *ls );
335 }
336 else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) )
337 {
338 for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom )
339 {
340 const QgsLineString *ls = mls->lineStringN( nGeom );
341 out.addLineString( *ls );
342 }
343 }
344
345 if ( mSymbol->materialSettings()->dataDefinedProperties().isActive( QgsAbstractMaterialSettings::Property::Ambient ) )
346 processMaterialDatadefined( out.vertices.size() - oldVerticesCount, context.expressionContext(), out );
347
348 mFeatureCount++;
349}
350
351void QgsThickLine3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
352{
353 // create entity for selected and not selected
354 makeEntity( parent, context, outNormal, false );
355 makeEntity( parent, context, outSelected, true );
356
357 updateZRangeFromPositions( outNormal.vertices );
358 updateZRangeFromPositions( outSelected.vertices );
359}
360
361
362void QgsThickLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsLineVertexData &out, bool selected )
363{
364 if ( out.indexes.isEmpty() )
365 return;
366
367 // material (only ambient color is used for the color)
368 QgsMaterialContext materialContext;
369 materialContext.setIsSelected( selected );
370 materialContext.setSelectionColor( context.selectionColor() );
371 QgsMaterial *mat = mSymbol->materialSettings()->toMaterial( QgsMaterialSettingsRenderingTechnique::Lines, materialContext );
372 if ( !mat )
373 {
374 const QgsSimpleLineMaterialSettings defaultMaterial;
375 mat = defaultMaterial.toMaterial( QgsMaterialSettingsRenderingTechnique::Lines, materialContext );
376 }
377
378 if ( QgsLineMaterial *lineMaterial = dynamic_cast< QgsLineMaterial * >( mat ) )
379 lineMaterial->setLineWidth( mSymbol->width() );
380
381 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
382
383 // geometry renderer
384 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
385 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
386 Qt3DQGeometry *geometry = out.createGeometry( entity );
387
388 if ( mSymbol->materialSettings()->dataDefinedProperties().isActive( QgsAbstractMaterialSettings::Property::Ambient ) )
389 mSymbol->materialSettings()->applyDataDefinedToGeometry( geometry, out.vertices.size(), out.materialDataDefined );
390
391 renderer->setGeometry( geometry );
392
393 renderer->setVertexCount( out.indexes.count() );
394 renderer->setPrimitiveRestartEnabled( true );
395 renderer->setRestartIndexValue( 0 );
396
397 // add transform (our geometry has coordinates relative to mChunkOrigin)
398 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
399 QVector3D nodeTranslation = ( mChunkOrigin - context.origin() ).toVector3D();
400 tr->setTranslation( nodeTranslation );
401
402 // make entity
403 entity->addComponent( renderer );
404 entity->addComponent( mat );
405 entity->addComponent( tr );
406 entity->setParent( parent );
407}
408
409void QgsThickLine3DSymbolHandler::processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsLineVertexData &out )
410{
411 const QByteArray bytes = mSymbol->materialSettings()->dataDefinedVertexColorsAsByte( context );
412 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
413}
414
415
416// --------------
417
418
419namespace Qgs3DSymbolImpl
420{
421
422 QgsFeature3DHandler *handlerForLine3DSymbol( QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
423 {
424 const QgsLine3DSymbol *lineSymbol = dynamic_cast< const QgsLine3DSymbol * >( symbol );
425 if ( !lineSymbol )
426 return nullptr;
427
428 if ( lineSymbol->renderAsSimpleLines() )
429 return new QgsThickLine3DSymbolHandler( lineSymbol, layer->selectedFeatureIds() );
430 else
431 return new QgsBufferedLine3DSymbolHandler( lineSymbol, layer->selectedFeatureIds() );
432 }
433}
434
JoinStyle
Join styles for buffers.
Definition qgis.h:1968
@ Round
Use rounded joins.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1955
@ Round
Round cap.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
QColor selectionColor() const
Returns color used for selected features.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
@ Main3DRenderer
Renderer for normal entities.
Definition qgs3dtypes.h:49
static const char * PROP_NAME_3D_RENDERER_FLAG
Qt property name to hold the 3D geometry renderer flag.
Definition qgs3dtypes.h:44
static void clampAltitudes(QgsLineString *lineString, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, const QgsPoint &centroid, float offset, const Qgs3DRenderContext &context)
Clamps altitude of vertices of a linestring according to the settings.
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
@ Ambient
Ambient color (phong material)
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
int numGeometries() const
Returns the number of geometries within the collection.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition qgsgeos.h:137
bool renderAsSimpleLines() const
Returns whether the renderer will render data with simple lines (otherwise it uses buffer)
QgsAbstract3DSymbol * clone() const override SIP_FACTORY
Line string geometry type, with support for z-dimension and m-values.
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected state.
void setSelectionColor(const QColor &color)
Sets the color for representing materials in a selected state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation....
Polygon geometry type.
Definition qgspolygon.h:33
QgsMaterial * toMaterial(QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context) const override
Creates a new QgsMaterial object representing the material settings.
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.
Class that takes care of tessellation of polygons into triangles.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
static bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
@ Triangles
Triangle based rendering (default)
@ Lines
Line based rendering, requires line data.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5958
Qt3DCore::QGeometry Qt3DQGeometry
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Qt3DCore::QAttribute Qt3DQAttribute
Qt3DCore::QBuffer Qt3DQBuffer
Qt3DCore::QGeometry Qt3DQGeometry