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