QGIS API Documentation 4.3.0-Master (bf28115e945)
Loading...
Searching...
No Matches
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
17
18#include "qgs3d.h"
19#include "qgs3drendercontext.h"
20#include "qgs3dutils.h"
22#include "qgsgeotransform.h"
23#include "qgslinematerial_p.h"
24#include "qgslinestring.h"
25#include "qgslinevertexdata_p.h"
27#include "qgsmessagelog.h"
29#include "qgsmultilinestring.h"
30#include "qgsmultipolygon.h"
32#include "qgspolygon.h"
33#include "qgspolygon3dsymbol.h"
35#include "qgssymbollayerutils.h"
37#include "qgstessellator.h"
38#include "qgsvectorlayer.h"
39
40#include <QString>
41#include <Qt3DCore/QBuffer>
42#include <Qt3DRender/QAbstractTextureImage>
43#include <Qt3DRender/QCullFace>
44#include <Qt3DRender/QEffect>
45#include <Qt3DRender/QGeometryRenderer>
46#include <Qt3DRender/QTechnique>
47#include <Qt3DRender/QTexture>
48
49using namespace Qt::StringLiterals;
50
52
53
54class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
55{
56 public:
57 QgsPolygon3DSymbolHandler( const QgsPolygon3DSymbol *symbol, const QgsFeatureIds &selectedIds )
58 : mSymbol( static_cast<QgsPolygon3DSymbol *>( symbol->clone() ) )
59 , mSelectedIds( selectedIds )
60 {}
61
62 bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent ) override;
63 void processFeature( const QgsFeature &f, const Qgs3DRenderContext &context ) override;
64 void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) override;
65
66 private:
68 struct PolygonData
69 {
70 std::unique_ptr<QgsTessellator> tessellator;
71 QVector<QgsFeatureId> triangleIndexFids;
72 QVector<uint> triangleIndexStartingIndices;
73 QByteArray materialDataDefined;
74 QVector<float> ddTextureTransformData;
75 };
76
77 void processPolygon(
78 const QgsPolygon *poly,
79 QgsFeatureId fid,
80 float offset,
81 float extrusionHeight,
82 float dataDefinedTextureScale,
83 float dataDefinedTextureRotation,
84 float dataDefinedTextureOffsetX,
85 float dataDefinedTextureOffsetY,
86 const Qgs3DRenderContext &context,
87 PolygonData &out
88 );
89 void processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, PolygonData &out );
90 void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
91 QgsMaterial *material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const;
92
93 // input specific for this class
94 std::unique_ptr<QgsPolygon3DSymbol> mSymbol;
95 // inputs - generic
96 QgsFeatureIds mSelectedIds;
97 // outputs
98 PolygonData outNormal;
99 PolygonData outSelected;
100
101 QgsLineVertexData outEdges;
102
104 bool mWasClippedToExtent = false;
105};
106
107
108bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent )
109{
110 mChunkOrigin = chunkExtent.center();
111 mChunkOrigin.setZ( 0. ); // set the chunk origin to the bottom of the box, as the tessellator currently always considers origin z to be zero
112 mChunkExtent = chunkExtent;
113
114 outEdges.withAdjacency = true;
115 outEdges.init( mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), 0, context, mChunkOrigin );
116
117 const QgsAbstractMaterialSettings *materialSettings = mSymbol->materialSettings();
118 const bool requiresTextureCoordinates = materialSettings->requiresTextureCoordinates();
119 const bool requiresTangents = materialSettings->requiresTangents();
120
121 auto tessellator = std::make_unique<QgsTessellator>();
122 tessellator->setOrigin( mChunkOrigin );
123 tessellator->setAddNormals( true );
124 tessellator->setInvertNormals( mSymbol->invertNormals() );
125 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
126 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
127 tessellator->setAddTextureUVs( requiresTextureCoordinates );
128 tessellator->setAddTangents( requiresTangents );
129 tessellator->setTriangulationAlgorithm( Qgis::TriangulationAlgorithm::Earcut );
130
131 outNormal.tessellator = std::move( tessellator );
132
133 tessellator = std::make_unique<QgsTessellator>();
134 tessellator->setOrigin( mChunkOrigin );
135 tessellator->setAddNormals( true );
136 tessellator->setInvertNormals( mSymbol->invertNormals() );
137 tessellator->setBackFacesEnabled( mSymbol->addBackFaces() );
138 tessellator->setExtrusionFaces( mSymbol->extrusionFaces() );
139 tessellator->setAddTextureUVs( requiresTextureCoordinates );
140 tessellator->setAddTangents( requiresTangents );
141 tessellator->setTriangulationAlgorithm( Qgis::TriangulationAlgorithm::Earcut );
142
143 outSelected.tessellator = std::move( tessellator );
144
145 QSet<QString> attrs = mSymbol->dataDefinedProperties().referencedFields( context.expressionContext() );
146 attributeNames.unite( attrs );
147 attrs = materialSettings->dataDefinedProperties().referencedFields( context.expressionContext() );
148 attributeNames.unite( attrs );
149 return true;
150}
151
152void QgsPolygon3DSymbolHandler::processPolygon(
153 const QgsPolygon *poly,
154 QgsFeatureId fid,
155 float offset,
156 float extrusionHeight,
157 float dataDefinedTextureScale,
158 float dataDefinedTextureRotation,
159 float dataDefinedTextureOffsetX,
160 float dataDefinedTextureOffsetY,
161 const Qgs3DRenderContext &context,
162 PolygonData &out
163)
164{
165 std::unique_ptr<QgsPolygon> polyClone( poly->clone() );
166
167 if ( mSymbol->edgesEnabled() )
168 {
169 // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
170 const QgsAbstractGeometry *exteriorRing = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
171
172 // This geometry will take ownership of the cleaned exteriorRing abstract geom.
173 // We need this to live for as long as exteriorRing is used.
174 QgsGeometry cleanedExteriorRingGeometry;
175
176 if ( mWasClippedToExtent )
177 {
178 // if the polygon was clipped to the chunk extent, then we need to remove any part of the exterior ring that
179 // overlaps the chunk extent line, otherwise the chunk extents will appear as edges. Any interior rings
180 // that were intersected by the extent will now be part of the exterior ring of the clipped geometry.
181 const QVector< QgsPointXY > extentPoints {
182 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() },
183 { mChunkExtent.xMaximum(), mChunkExtent.yMinimum() },
184 { mChunkExtent.xMaximum(), mChunkExtent.yMaximum() },
185 { mChunkExtent.xMinimum(), mChunkExtent.yMaximum() },
186 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() }
187 };
188 auto extentLinestring = std::make_unique<QgsLineString>( extentPoints );
189
190 const QgsGeometry exteriorRingGeometry( exteriorRing->clone() );
191 // it should be safe to perform the diff without any tolerance, the extent line string is a simple rectangle (chunk perimeter)
192 cleanedExteriorRingGeometry = exteriorRingGeometry.difference( QgsGeometry( extentLinestring.release() ) );
193 // make sure the diff didn't produce some degenerate geometry
194 ( void ) cleanedExteriorRingGeometry.convertGeometryCollectionToSubclass( Qgis::GeometryType::Line );
195 exteriorRing = cleanedExteriorRingGeometry.constGet()->simplifiedTypeRef();
196 }
197
198 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( exteriorRing ) )
199 {
200 outEdges.addLineString( *line, offset );
201 }
202 // if geometry was clipped to the chunk extents, we might now have a multilinestring
203 else if ( const QgsMultiLineString *mline = qgsgeometry_cast<const QgsMultiLineString *>( exteriorRing ) )
204 {
205 for ( int i = 0; i < mline->numGeometries(); ++i )
206 {
207 const QgsLineString *line = mline->lineStringN( i );
208 outEdges.addLineString( *line, offset );
209 }
210 }
211
212 // if geometry was clipped to the chunk extents and the chunk extents intersected an interior ring, then
213 // that ring is now part of the exterior ring. Hence we don't need to treat interior rings any differently
214 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
215 outEdges.addLineString( *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), offset );
216
217 if ( extrusionHeight != 0.f )
218 {
219 // add roof and wall edges
220
221 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( exteriorRing ) )
222 {
223 outEdges.addLineString( *line, extrusionHeight + offset );
224 outEdges.addVerticalLines( *line, extrusionHeight, offset );
225 }
226 // if geometry was clipped to the chunk extents, we might now have a multilinestring
227 else if ( const QgsMultiLineString *mline = qgsgeometry_cast<const QgsMultiLineString *>( exteriorRing ) )
228 {
229 for ( int i = 0; i < mline->numGeometries(); ++i )
230 {
231 const QgsLineString *line = mline->lineStringN( i );
232 outEdges.addLineString( *line, extrusionHeight + offset );
233 outEdges.addVerticalLines( *line, extrusionHeight, offset );
234 }
235 }
236
237 // if geometry was clipped to the chunk extents and the chunk extents intersected an interior ring, then
238 // that ring is now part of the exterior ring. Hence we don't need to treat interior rings any differently
239 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
240 {
241 const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
242 outEdges.addLineString( *interior, extrusionHeight + offset );
243 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
244 }
245 }
246 }
247
248 Qgs3DUtils::clampAltitudes( polyClone.get(), mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), offset, context );
249
250 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
251 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
252 out.triangleIndexStartingIndices.append( startingTriangleIndex );
253 out.triangleIndexFids.append( fid );
254
255 const size_t oldVertexCount = out.tessellator->uniqueVertexCount();
256 out.tessellator->addPolygon( *polyClone, extrusionHeight );
257 if ( !out.tessellator->error().isEmpty() )
258 {
259 QgsMessageLog::logMessage( out.tessellator->error(), QObject::tr( "3D" ) );
260 }
261
262 const QgsPropertyCollection &dataDefinedProperties = mSymbol->materialSettings()->dataDefinedProperties();
263 if ( dataDefinedProperties.hasActiveProperties() )
264 {
265 const uint newUniqueVertices = out.tessellator->uniqueVertexCount() - oldVertexCount;
266 processMaterialDatadefined( newUniqueVertices, context.expressionContext(), out );
267
271 {
272 for ( uint i = 0; i < newUniqueVertices; ++i )
273 {
274 out.ddTextureTransformData << dataDefinedTextureOffsetX << dataDefinedTextureOffsetY << dataDefinedTextureScale << dataDefinedTextureRotation;
275 }
276 }
277 }
278}
279
280void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
281{
282 QByteArray bytes;
283 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( mSymbol->materialSettings() ) )
284 {
285 bytes = handler->dataDefinedVertexColorsAsByte( mSymbol->materialSettings(), context );
286 }
287 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
288}
289
290void QgsPolygon3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
291{
292 if ( f.geometry().isNull() )
293 return;
294
295 PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
296
297 QgsGeometry geom = f.geometry();
298 mWasClippedToExtent = clipGeometryIfTooLarge( geom );
299
300 if ( geom.isEmpty() )
301 return;
302
304
305 // segmentize curved geometries if necessary
307 {
308 geom = QgsGeometry( g->segmentize() );
309 g = geom.constGet()->simplifiedTypeRef();
310 }
311
312 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
313 const bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::Property::Height );
314 const bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::Property::ExtrusionHeight );
315
316 float offset = mSymbol->offset();
317 float extrusionHeight = mSymbol->extrusionHeight();
318 if ( hasDDHeight )
319 offset = static_cast<float>( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Height, context.expressionContext(), offset ) );
320 if ( hasDDExtrusion )
321 extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ExtrusionHeight, context.expressionContext(), extrusionHeight );
322
323 const QgsAbstractMaterialSettings *materialSettings = mSymbol->materialSettings();
324 const bool hasTextureScale = materialSettings
327 const bool hasTextureRotation = materialSettings
330 const bool hasTextureOffset = materialSettings
333 float dataDefinedTextureRotation = 0;
334 float dataDefinedTextureScale = 1;
335 float dataDefinedTextureOffsetX = 0;
336 float dataDefinedTextureOffsetY = 0;
337 if ( auto phongTexturedMaterial = dynamic_cast<const QgsPhongTexturedMaterialSettings * >( materialSettings ) )
338 {
339 dataDefinedTextureRotation = phongTexturedMaterial->textureRotation();
340 dataDefinedTextureScale = phongTexturedMaterial->textureScale();
341 dataDefinedTextureOffsetX = phongTexturedMaterial->textureOffset().x();
342 dataDefinedTextureOffsetY = phongTexturedMaterial->textureOffset().y();
343 }
344 else if ( auto metalRoughTexturedMaterial = dynamic_cast<const QgsMetalRoughTexturedMaterialSettings * >( materialSettings ) )
345 {
346 dataDefinedTextureRotation = metalRoughTexturedMaterial->textureRotation();
347 dataDefinedTextureScale = metalRoughTexturedMaterial->textureScale();
348 dataDefinedTextureOffsetX = metalRoughTexturedMaterial->textureOffset().x();
349 dataDefinedTextureOffsetY = metalRoughTexturedMaterial->textureOffset().y();
350 }
351
352 if ( hasTextureScale || hasTextureRotation || hasTextureOffset )
353 {
354 dataDefinedTextureRotation = static_cast< float >(
355 materialSettings->dataDefinedProperties().valueAsDouble( QgsAbstractMaterialSettings::Property::TextureRotation, context.expressionContext(), dataDefinedTextureRotation )
356 );
357 dataDefinedTextureScale = static_cast< float >(
358 100 / materialSettings->dataDefinedProperties().valueAsDouble( QgsAbstractMaterialSettings::Property::TextureScale, context.expressionContext(), 100 / dataDefinedTextureScale )
359 );
360
361 QVariant offsetValue = materialSettings->dataDefinedProperties().value( QgsAbstractMaterialSettings::Property::TextureOffset, context.expressionContext() );
362 bool ok = false;
363 const QPointF ddOffset = QgsSymbolLayerUtils::toPoint( offsetValue, &ok );
364 if ( ok )
365 {
366 dataDefinedTextureOffsetX = static_cast< float >( ddOffset.x() );
367 dataDefinedTextureOffsetY = static_cast< float >( ddOffset.y() );
368 }
369 }
370
371 if ( const QgsPolygon *poly = qgsgeometry_cast<const QgsPolygon *>( g ) )
372 {
373 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
374 }
375 else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast<const QgsMultiPolygon *>( g ) )
376 {
377 for ( int i = 0; i < mpoly->numGeometries(); ++i )
378 {
379 const QgsPolygon *poly = mpoly->polygonN( i );
380 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
381 }
382 }
384 {
385 for ( int i = 0; i < gc->numGeometries(); ++i )
386 {
387 const QgsAbstractGeometry *g2 = gc->geometryN( i );
389 {
390 const QgsPolygon *poly = static_cast<const QgsPolygon *>( g2 );
391 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
392 }
393 }
394 }
395 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
396 {
397 for ( int i = 0; i < polySurface->numPatches(); ++i )
398 {
399 const QgsPolygon *poly = polySurface->patchN( i );
400 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
401 }
402 }
403 else
404 qWarning() << "not a polygon";
405
406 mFeatureCount++;
407}
408
409void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
410{
411 // create entity for selected and not selected
412 makeEntity( parent, context, outNormal, false );
413 makeEntity( parent, context, outSelected, true );
414
415 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
416 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
417
418 // add entity for edges, but not when doing highlighting
419 if ( mSymbol->edgesEnabled() && !outEdges.indexes.isEmpty() && !mHighlightingEnabled )
420 {
421 QgsLineMaterial *mat = new QgsLineMaterial;
422 mat->setLineColor( mSymbol->edgeColor() );
423 mat->setLineWidth( mSymbol->edgeWidth() );
424
425 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
426 entity->setObjectName( parent->objectName() + "_EDGES" );
427
428 // geometry renderer
429 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
430 renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
431 renderer->setGeometry( outEdges.createGeometry( entity ) );
432 renderer->setVertexCount( outEdges.indexes.count() );
433 renderer->setPrimitiveRestartEnabled( true );
434 renderer->setRestartIndexValue( 0 );
435
436 // add transform (our geometry has coordinates relative to mChunkOrigin)
437 QgsGeoTransform *tr = new QgsGeoTransform;
438 tr->setGeoTranslation( mChunkOrigin );
439
440 // make entity
441 entity->addComponent( renderer );
442 entity->addComponent( mat );
443 entity->addComponent( tr );
444 entity->setParent( parent );
445 }
446}
447
448
449void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &polyData, bool selected )
450{
451 if ( polyData.tessellator->dataVerticesCount() == 0 )
452 return; // nothing to show - no need to create the entity
453
454 QgsMaterial *mat = material( mSymbol.get(), selected, context );
455
456 // extract vertex buffer data from tessellator
457 const QByteArray vertexBuffer = polyData.tessellator->vertexBuffer();
458 const QByteArray indexBuffer = polyData.tessellator->indexBuffer();
459 const int vertexCount = polyData.tessellator->uniqueVertexCount();
460 const size_t indexCount = polyData.tessellator->dataVerticesCount();
461
463 true,
464 mSymbol->invertNormals(),
465 mSymbol->addBackFaces(),
466 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTextureCoordinates(),
467 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTangents()
468 );
469
470 geometry->setVertexBufferData( vertexBuffer, vertexCount, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
471 geometry->setIndexBufferData( indexBuffer, indexCount );
472
473 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
474 {
475 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( mSymbol->materialSettings() ) )
476 {
477 handler->applyDataDefinedToGeometry( mSymbol->materialSettings(), geometry, vertexCount, polyData.materialDataDefined );
478 }
479
480 if ( !polyData.ddTextureTransformData.isEmpty() )
481 {
482 auto textureTransformBuffer = new Qt3DCore::QBuffer( geometry );
483
484 QByteArray byteArray(
485 reinterpret_cast<const char *>( polyData.ddTextureTransformData.data() ),
486 static_cast<int>( polyData.ddTextureTransformData.size() * sizeof( float ) )
487 ); // makes a deep copy
488 textureTransformBuffer->setData( byteArray );
489 auto textureTransformAttribute = new Qt3DCore::QAttribute( geometry );
490 textureTransformAttribute->setName( u"ddTextureTransform"_s );
491 textureTransformAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
492 textureTransformAttribute->setVertexSize( 4 );
493 textureTransformAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
494 textureTransformAttribute->setBuffer( textureTransformBuffer );
495 textureTransformAttribute->setByteStride( 4 * sizeof( float ) );
496 textureTransformAttribute->setCount( vertexCount );
497
498 geometry->addAttribute( textureTransformAttribute );
499 }
500 }
501 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
502 renderer->setGeometry( geometry );
503
504 // add transform (our geometry has coordinates relative to mChunkOrigin)
505 QgsGeoTransform *tr = new QgsGeoTransform;
506 tr->setGeoTranslation( mChunkOrigin );
507
508 // make entity
509 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
510 entity->setObjectName( parent->objectName() + "_CHUNK_MESH" );
511 entity->addComponent( renderer );
512 entity->addComponent( mat );
513 entity->addComponent( tr );
514 entity->setParent( parent );
515
516 if ( !selected )
517 renderer->setProperty( Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG, Qgs3DTypes::Main3DRenderer ); // temporary measure to distinguish between "selected" and "main"
518
519 // cppcheck wrongly believes entity will leak
520 // cppcheck-suppress memleak
521}
522
523// front/back side culling
524static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, QgsMaterial *material )
525{
526 const auto techniques = material->effect()->techniques();
527 for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
528 {
529 auto renderPasses = ( *tit )->renderPasses();
530 for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
531 {
532 Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
533 cullFace->setMode( Qgs3DUtils::qt3DcullingMode( cullingMode ) );
534 ( *rpit )->addRenderState( cullFace );
535 }
536 }
537}
538
539QgsMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const
540{
542 materialContext.setIsSelected( isSelected );
543 materialContext.setIsHighlighted( mHighlightingEnabled );
544
545 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
547 applyCullingMode( symbol->cullingMode(), material );
548 return material;
549}
550
551
552// --------------
553
554
555namespace Qgs3DSymbolImpl
556{
557
558
559 QgsFeature3DHandler *handlerForPolygon3DSymbol( const QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
560 {
561 const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast<const QgsPolygon3DSymbol *>( symbol );
562 if ( !polygonSymbol )
563 return nullptr;
564
565 return new QgsPolygon3DSymbolHandler( polygonSymbol, layer->selectedFeatureIds() );
566 }
567} // namespace Qgs3DSymbolImpl
568
@ Line
Lines.
Definition qgis.h:381
@ Triangles
Triangle based rendering (default).
Definition qgis.h:4389
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
Definition qgis.h:4395
@ Polygon
Polygon.
Definition qgis.h:298
Rendering context for preparation of 3D entities.
QgsExpressionContext & expressionContext()
Gets the expression context.
@ Main3DRenderer
Renderer for normal entities.
Definition qgs3dtypes.h:48
static const char * PROP_NAME_3D_RENDERER_FLAG
Qt property name to hold the 3D geometry renderer flag.
Definition qgs3dtypes.h:43
CullingMode
Triangle culling mode.
Definition qgs3dtypes.h:35
static Qt3DRender::QCullFace::CullingMode qt3DcullingMode(Qgs3DTypes::CullingMode mode)
Converts Qgs3DTypes::CullingMode mode into its Qt3D equivalent.
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.
static QgsMaterial * toMaterial(const QgsAbstractMaterialSettings *settings, Qgis::MaterialRenderingTechnique technique, const QgsMaterialContext &context)
Creates a new QgsMaterial object representing the material settings.
Definition qgs3d.cpp:154
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
Definition qgs3d.cpp:137
@ ExtrusionHeight
Extrusion height (zero means no extrusion).
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.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Abstract base class for material 3D handlers.
Abstract base class for material settings.
virtual QSet< QgsAbstractMaterialSettings::Property > supportedProperties() const
Returns the set of data-defined properties supported by this material.
virtual bool requiresTangents() const
Returns true if the material requires tangents generated during triangulation.
QgsPropertyCollection dataDefinedProperties() const
Returns the symbol material property collection, used for data defined overrides.
virtual bool requiresTextureCoordinates() const
Returns true if the material requires texture coordinates to be generated during triangulation.
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.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
QgsVector3D center() const
Returns the center of the box as a vector.
Definition qgsbox3d.cpp:124
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:60
QgsFeatureId id
Definition qgsfeature.h:63
QgsGeometry geometry
Definition qgsfeature.h:66
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.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Line string geometry type, with support for z-dimension and m-values.
Context settings for a material.
void setIsSelected(bool isSelected)
Sets whether the material should represent a selected state.
void setIsHighlighted(bool isHighlighted)
Sets whether the material should represent a highlighted state.
static QgsMaterialContext fromRenderContext(const Qgs3DRenderContext &context)
Constructs a material context from the settings in a 3D render context.
Base class for all materials used within QGIS 3D views.
Definition qgsmaterial.h:40
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A PBR metal rough shading material used for rendering with support for image texture maps.
Multi line string geometry collection.
Multi polygon geometry collection.
A Phong shading model with diffuse texture map.
3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
Qgs3DTypes::CullingMode cullingMode() const
Returns front/back culling mode.
QgsAbstractMaterialSettings * materialSettings() const override
Returns material settings used for shading of the symbol.
Polygon geometry type.
Definition qgspolygon.h:37
QgsPolygon * clone() const override
Clones the geometry by performing a deep copy.
Polyhedral surface geometry type.
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
bool hasActiveProperties() const final
Returns true if the collection has any active properties, or false if all properties within the colle...
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
Qt3DRender::QGeometry subclass that represents polygons tessellated into 3D geometry.
void setVertexBufferData(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.
void setIndexBufferData(const QByteArray &indexBufferData, size_t indexCount)
Sets index buffer data.
void setZ(double z)
Sets Z coordinate.
Definition qgsvector3d.h:80
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
static Q_INVOKABLE 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.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features