QGIS API Documentation 4.3.0-Master (6a28b93578f)
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#include "qgsvertexid.h"
40
41#include <QString>
42#include <Qt3DCore/QBuffer>
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.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
152static void addClosedRingEdges( QgsLineVertexData &edges, const QgsLineString &ring, float extraHeightOffset )
153{
154 std::unique_ptr<QgsLineString> openRing( ring.clone() );
155 openRing->deleteVertex( QgsVertexId( 0, 0, openRing->numPoints() - 1 ) );
156 edges.addLineString( *openRing, extraHeightOffset, true );
157}
158
159void QgsPolygon3DSymbolHandler::processPolygon(
160 const QgsPolygon *poly,
161 QgsFeatureId fid,
162 float offset,
163 float extrusionHeight,
164 float dataDefinedTextureScale,
165 float dataDefinedTextureRotation,
166 float dataDefinedTextureOffsetX,
167 float dataDefinedTextureOffsetY,
168 const Qgs3DRenderContext &context,
169 PolygonData &out
170)
171{
172 std::unique_ptr<QgsPolygon> polyClone( poly->clone() );
173
174 if ( mSymbol->edgesEnabled() )
175 {
176 // add edges before the polygon gets the Z values modified because addLineString() does its own altitude handling
177 const QgsAbstractGeometry *exteriorRing = static_cast<const QgsLineString *>( polyClone->exteriorRing() );
178
179 // This geometry will take ownership of the cleaned exteriorRing abstract geom.
180 // We need this to live for as long as exteriorRing is used.
181 QgsGeometry cleanedExteriorRingGeometry;
182
183 if ( mWasClippedToExtent )
184 {
185 // if the polygon was clipped to the chunk extent, then we need to remove any part of the exterior ring that
186 // overlaps the chunk extent line, otherwise the chunk extents will appear as edges. Any interior rings
187 // that were intersected by the extent will now be part of the exterior ring of the clipped geometry.
188 const QVector< QgsPointXY > extentPoints {
189 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() },
190 { mChunkExtent.xMaximum(), mChunkExtent.yMinimum() },
191 { mChunkExtent.xMaximum(), mChunkExtent.yMaximum() },
192 { mChunkExtent.xMinimum(), mChunkExtent.yMaximum() },
193 { mChunkExtent.xMinimum(), mChunkExtent.yMinimum() }
194 };
195 auto extentLinestring = std::make_unique<QgsLineString>( extentPoints );
196
197 const QgsGeometry exteriorRingGeometry( exteriorRing->clone() );
198 // it should be safe to perform the diff without any tolerance, the extent line string is a simple rectangle (chunk perimeter)
199 cleanedExteriorRingGeometry = exteriorRingGeometry.difference( QgsGeometry( extentLinestring.release() ) );
200 // make sure the diff didn't produce some degenerate geometry
201 ( void ) cleanedExteriorRingGeometry.convertGeometryCollectionToSubclass( Qgis::GeometryType::Line );
202 exteriorRing = cleanedExteriorRingGeometry.constGet()->simplifiedTypeRef();
203 }
204
205 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( exteriorRing ) )
206 {
207 if ( mWasClippedToExtent )
208 outEdges.addLineString( *line, offset );
209 else
210 addClosedRingEdges( outEdges, *line, offset );
211 }
212 // if geometry was clipped to the chunk extents, we might now have a multilinestring
213 else if ( const QgsMultiLineString *mline = qgsgeometry_cast<const QgsMultiLineString *>( exteriorRing ) )
214 {
215 for ( int i = 0; i < mline->numGeometries(); ++i )
216 {
217 // these are always open arcs left over from clipping to the chunk extent, never closed rings
218 const QgsLineString *line = mline->lineStringN( i );
219 outEdges.addLineString( *line, offset );
220 }
221 }
222
223 // if geometry was clipped to the chunk extents and the chunk extents intersected an interior ring, then
224 // that ring is now part of the exterior ring, so remaining interior rings here are always complete,
225 // unclipped closed rings
226 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
227 addClosedRingEdges( outEdges, *static_cast<const QgsLineString *>( polyClone->interiorRing( i ) ), offset );
228
229 if ( extrusionHeight != 0.f )
230 {
231 // add roof and wall edges
232
233 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( exteriorRing ) )
234 {
235 // when clipped to the chunk extent, this may now be an open arc rather than a closed ring
236 if ( mWasClippedToExtent )
237 outEdges.addLineString( *line, extrusionHeight + offset );
238 else
239 addClosedRingEdges( outEdges, *line, extrusionHeight + offset );
240 outEdges.addVerticalLines( *line, extrusionHeight, offset );
241 }
242 // if geometry was clipped to the chunk extents, we might now have a multilinestring
243 else if ( const QgsMultiLineString *mline = qgsgeometry_cast<const QgsMultiLineString *>( exteriorRing ) )
244 {
245 for ( int i = 0; i < mline->numGeometries(); ++i )
246 {
247 // these are always open arcs left over from clipping to the chunk extent, never closed rings
248 const QgsLineString *line = mline->lineStringN( i );
249 outEdges.addLineString( *line, extrusionHeight + offset );
250 outEdges.addVerticalLines( *line, extrusionHeight, offset );
251 }
252 }
253
254 // if geometry was clipped to the chunk extents and the chunk extents intersected an interior ring, then
255 // that ring is now part of the exterior ring, so remaining interior rings here are always complete,
256 // unclipped closed rings
257 for ( int i = 0; i < polyClone->numInteriorRings(); ++i )
258 {
259 const QgsLineString *interior = static_cast<const QgsLineString *>( polyClone->interiorRing( i ) );
260 addClosedRingEdges( outEdges, *interior, extrusionHeight + offset );
261 outEdges.addVerticalLines( *interior, extrusionHeight, offset );
262 }
263 }
264 }
265
266 Qgs3DUtils::clampAltitudes( polyClone.get(), mSymbol->altitudeClamping(), mSymbol->altitudeBinding(), offset, context );
267
268 Q_ASSERT( out.tessellator->dataVerticesCount() % 3 == 0 );
269 const uint startingTriangleIndex = static_cast<uint>( out.tessellator->dataVerticesCount() / 3 );
270 out.triangleIndexStartingIndices.append( startingTriangleIndex );
271 out.triangleIndexFids.append( fid );
272
273 const size_t oldVertexCount = out.tessellator->uniqueVertexCount();
274 out.tessellator->addPolygon( *polyClone, extrusionHeight );
275 if ( !out.tessellator->error().isEmpty() )
276 {
277 QgsMessageLog::logMessage( out.tessellator->error(), QObject::tr( "3D" ) );
278 }
279
280 const QgsPropertyCollection &dataDefinedProperties = mSymbol->materialSettings()->dataDefinedProperties();
281 if ( dataDefinedProperties.hasActiveProperties() )
282 {
283 const uint newUniqueVertices = out.tessellator->uniqueVertexCount() - oldVertexCount;
284 processMaterialDatadefined( newUniqueVertices, context.expressionContext(), out );
285
289 {
290 for ( uint i = 0; i < newUniqueVertices; ++i )
291 {
292 out.ddTextureTransformData << dataDefinedTextureOffsetX << dataDefinedTextureOffsetY << dataDefinedTextureScale << dataDefinedTextureRotation;
293 }
294 }
295 }
296}
297
298void QgsPolygon3DSymbolHandler::processMaterialDatadefined( uint verticesCount, const QgsExpressionContext &context, QgsPolygon3DSymbolHandler::PolygonData &out )
299{
300 QByteArray bytes;
301 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( mSymbol->materialSettings() ) )
302 {
303 bytes = handler->dataDefinedVertexColorsAsByte( mSymbol->materialSettings(), context );
304 }
305 out.materialDataDefined.append( bytes.repeated( verticesCount ) );
306}
307
308void QgsPolygon3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3DRenderContext &context )
309{
310 if ( f.geometry().isNull() )
311 return;
312
313 PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;
314
315 QgsGeometry geom = f.geometry();
316 mWasClippedToExtent = clipGeometryIfTooLarge( geom );
317
318 if ( geom.isEmpty() )
319 return;
320
322
323 // segmentize curved geometries if necessary
325 {
326 geom = QgsGeometry( g->segmentize() );
327 g = geom.constGet()->simplifiedTypeRef();
328 }
329
330 const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
331 const bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::Property::Height );
332 const bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::Property::ExtrusionHeight );
333
334 float offset = mSymbol->offset();
335 float extrusionHeight = mSymbol->extrusionHeight();
336 if ( hasDDHeight )
337 offset = static_cast<float>( ddp.valueAsDouble( QgsAbstract3DSymbol::Property::Height, context.expressionContext(), offset ) );
338 if ( hasDDExtrusion )
339 extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::Property::ExtrusionHeight, context.expressionContext(), extrusionHeight );
340
341 const QgsAbstractMaterialSettings *materialSettings = mSymbol->materialSettings();
342 const bool hasTextureScale = materialSettings
345 const bool hasTextureRotation = materialSettings
348 const bool hasTextureOffset = materialSettings
351 float dataDefinedTextureRotation = 0;
352 float dataDefinedTextureScale = 1;
353 float dataDefinedTextureOffsetX = 0;
354 float dataDefinedTextureOffsetY = 0;
355 if ( auto phongTexturedMaterial = dynamic_cast<const QgsPhongTexturedMaterialSettings * >( materialSettings ) )
356 {
357 dataDefinedTextureRotation = phongTexturedMaterial->textureRotation();
358 dataDefinedTextureScale = phongTexturedMaterial->textureScale();
359 dataDefinedTextureOffsetX = phongTexturedMaterial->textureOffset().x();
360 dataDefinedTextureOffsetY = phongTexturedMaterial->textureOffset().y();
361 }
362 else if ( auto metalRoughTexturedMaterial = dynamic_cast<const QgsMetalRoughTexturedMaterialSettings * >( materialSettings ) )
363 {
364 dataDefinedTextureRotation = metalRoughTexturedMaterial->textureRotation();
365 dataDefinedTextureScale = metalRoughTexturedMaterial->textureScale();
366 dataDefinedTextureOffsetX = metalRoughTexturedMaterial->textureOffset().x();
367 dataDefinedTextureOffsetY = metalRoughTexturedMaterial->textureOffset().y();
368 }
369
370 if ( hasTextureScale || hasTextureRotation || hasTextureOffset )
371 {
372 dataDefinedTextureRotation = static_cast< float >(
373 materialSettings->dataDefinedProperties().valueAsDouble( QgsAbstractMaterialSettings::Property::TextureRotation, context.expressionContext(), dataDefinedTextureRotation )
374 );
375 dataDefinedTextureScale = static_cast< float >(
376 100 / materialSettings->dataDefinedProperties().valueAsDouble( QgsAbstractMaterialSettings::Property::TextureScale, context.expressionContext(), 100 / dataDefinedTextureScale )
377 );
378
379 QVariant offsetValue = materialSettings->dataDefinedProperties().value( QgsAbstractMaterialSettings::Property::TextureOffset, context.expressionContext() );
380 bool ok = false;
381 const QPointF ddOffset = QgsSymbolLayerUtils::toPoint( offsetValue, &ok );
382 if ( ok )
383 {
384 dataDefinedTextureOffsetX = static_cast< float >( ddOffset.x() );
385 dataDefinedTextureOffsetY = static_cast< float >( ddOffset.y() );
386 }
387 }
388
389 if ( const QgsPolygon *poly = qgsgeometry_cast<const QgsPolygon *>( g ) )
390 {
391 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
392 }
393 else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast<const QgsMultiPolygon *>( g ) )
394 {
395 for ( int i = 0; i < mpoly->numGeometries(); ++i )
396 {
397 const QgsPolygon *poly = mpoly->polygonN( i );
398 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
399 }
400 }
402 {
403 for ( int i = 0; i < gc->numGeometries(); ++i )
404 {
405 const QgsAbstractGeometry *g2 = gc->geometryN( i );
407 {
408 const QgsPolygon *poly = static_cast<const QgsPolygon *>( g2 );
409 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
410 }
411 }
412 }
413 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
414 {
415 for ( int i = 0; i < polySurface->numPatches(); ++i )
416 {
417 const QgsPolygon *poly = polySurface->patchN( i );
418 processPolygon( poly, f.id(), offset, extrusionHeight, dataDefinedTextureScale, dataDefinedTextureRotation, dataDefinedTextureOffsetX, dataDefinedTextureOffsetY, context, out );
419 }
420 }
421 else
422 qWarning() << "not a polygon";
423
424 mFeatureCount++;
425}
426
427void QgsPolygon3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
428{
429 // create entity for selected and not selected
430 makeEntity( parent, context, outNormal, false );
431 makeEntity( parent, context, outSelected, true );
432
433 mZMin = std::min( outNormal.tessellator->zMinimum(), outSelected.tessellator->zMinimum() );
434 mZMax = std::max( outNormal.tessellator->zMaximum(), outSelected.tessellator->zMaximum() );
435
436 // add entity for edges, but not when doing highlighting
437 if ( mSymbol->edgesEnabled() && !outEdges.pointsA.isEmpty() && !mHighlightingEnabled )
438 {
439 QgsLineMaterial *mat = new QgsLineMaterial;
440 mat->setLineColor( mSymbol->edgeColor() );
441 mat->setLineWidth( mSymbol->edgeWidth() );
442
443 Qt3DCore::QEntity *entity = outEdges.createSegmentEntity( mat );
444 entity->setObjectName( parent->objectName() + "_EDGES" );
445
446 // add transform (our geometry has coordinates relative to mChunkOrigin)
447 QgsGeoTransform *tr = new QgsGeoTransform;
448 tr->setGeoTranslation( mChunkOrigin );
449
450 entity->addComponent( tr );
451 entity->setParent( parent );
452
453 if ( Qt3DCore::QEntity *joinEntity = outEdges.createJoinEntity( mat ) )
454 {
455 joinEntity->setObjectName( parent->objectName() + "_EDGES_JOINS" );
456 QgsGeoTransform *joinTr = new QgsGeoTransform;
457 joinTr->setGeoTranslation( mChunkOrigin );
458 joinEntity->addComponent( joinTr );
459 joinEntity->setParent( parent );
460 }
461 }
462}
463
464
465void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &polyData, bool selected )
466{
467 if ( polyData.tessellator->dataVerticesCount() == 0 )
468 return; // nothing to show - no need to create the entity
469
470 QgsMaterial *mat = material( mSymbol.get(), selected, context );
471
472 // extract vertex buffer data from tessellator
473 const QByteArray vertexBuffer = polyData.tessellator->vertexBuffer();
474 const QByteArray indexBuffer = polyData.tessellator->indexBuffer();
475 const int vertexCount = polyData.tessellator->uniqueVertexCount();
476 const size_t indexCount = polyData.tessellator->dataVerticesCount();
477
479 true,
480 mSymbol->invertNormals(),
481 mSymbol->addBackFaces(),
482 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTextureCoordinates(),
483 mSymbol->materialSettings() && mSymbol->materialSettings()->requiresTangents()
484 );
485
486 geometry->setVertexBufferData( vertexBuffer, vertexCount, polyData.triangleIndexFids, polyData.triangleIndexStartingIndices );
487 geometry->setIndexBufferData( indexBuffer, indexCount );
488
489 if ( mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties() )
490 {
491 if ( const QgsAbstractMaterial3DHandler *handler = Qgs3D::handlerForMaterialSettings( mSymbol->materialSettings() ) )
492 {
493 handler->applyDataDefinedToGeometry( mSymbol->materialSettings(), geometry, vertexCount, polyData.materialDataDefined );
494 }
495
496 if ( !polyData.ddTextureTransformData.isEmpty() )
497 {
498 auto textureTransformBuffer = new Qt3DCore::QBuffer( geometry );
499
500 QByteArray byteArray(
501 reinterpret_cast<const char *>( polyData.ddTextureTransformData.data() ),
502 static_cast<int>( polyData.ddTextureTransformData.size() * sizeof( float ) )
503 ); // makes a deep copy
504 textureTransformBuffer->setData( byteArray );
505 auto textureTransformAttribute = new Qt3DCore::QAttribute( geometry );
506 textureTransformAttribute->setName( u"ddTextureTransform"_s );
507 textureTransformAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
508 textureTransformAttribute->setVertexSize( 4 );
509 textureTransformAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
510 textureTransformAttribute->setBuffer( textureTransformBuffer );
511 textureTransformAttribute->setByteStride( 4 * sizeof( float ) );
512 textureTransformAttribute->setCount( vertexCount );
513
514 geometry->addAttribute( textureTransformAttribute );
515 }
516 }
517 Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
518 renderer->setGeometry( geometry );
519
520 // add transform (our geometry has coordinates relative to mChunkOrigin)
521 QgsGeoTransform *tr = new QgsGeoTransform;
522 tr->setGeoTranslation( mChunkOrigin );
523
524 // make entity
525 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
526 entity->setObjectName( parent->objectName() + "_CHUNK_MESH" );
527 entity->addComponent( renderer );
528 entity->addComponent( mat );
529 entity->addComponent( tr );
530 entity->setParent( parent );
531
532 if ( !selected )
533 renderer->setProperty( Qgs3DTypes::PROP_NAME_3D_RENDERER_FLAG, Qgs3DTypes::Main3DRenderer ); // temporary measure to distinguish between "selected" and "main"
534
535 // cppcheck wrongly believes entity will leak
536 // cppcheck-suppress memleak
537}
538
539// front/back side culling
540static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, QgsMaterial *material )
541{
542 const auto techniques = material->effect()->techniques();
543 for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
544 {
545 auto renderPasses = ( *tit )->renderPasses();
546 for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
547 {
548 Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
549 cullFace->setMode( Qgs3DUtils::qt3DcullingMode( cullingMode ) );
550 ( *rpit )->addRenderState( cullFace );
551 }
552 }
553}
554
555QgsMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol *symbol, bool isSelected, const Qgs3DRenderContext &context ) const
556{
558 materialContext.setIsSelected( isSelected );
559 materialContext.setIsHighlighted( mHighlightingEnabled );
560
561 const bool dataDefined = mSymbol->materialSettings()->dataDefinedProperties().hasActiveProperties();
563 applyCullingMode( symbol->cullingMode(), material );
564 return material;
565}
566
567
568// --------------
569
570
571namespace Qgs3DSymbolImpl
572{
573
574
575 QgsFeature3DHandler *handlerForPolygon3DSymbol( const QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol )
576 {
577 const QgsPolygon3DSymbol *polygonSymbol = dynamic_cast<const QgsPolygon3DSymbol *>( symbol );
578 if ( !polygonSymbol )
579 return nullptr;
580
581 return new QgsPolygon3DSymbolHandler( polygonSymbol, layer->selectedFeatureIds() );
582 }
583} // namespace Qgs3DSymbolImpl
584
@ Line
Lines.
Definition qgis.h:381
@ Triangles
Triangle based rendering (default).
Definition qgis.h:4412
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
Definition qgis.h:4418
@ 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:159
static const QgsAbstractMaterial3DHandler * handlerForMaterialSettings(const QgsAbstractMaterialSettings *settings)
Returns the handler to use for a material settings.
Definition qgs3d.cpp:142
@ 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.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
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
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:35