Skip to content

Commit cc96e9e

Browse files
NEDJIMAbelgacemnirvnrouaultnyalldawson
authoredJul 9, 2020
[Feature] texturing support for vector layer (#36981)
* [Feature] texturing support for vector layer * Fixed tesselation test not passing issue * Fixed diffuse_texture_path typo * Refactoring according to code review * [Bugfix] the texture coordinates are not setup properly for certain walls * Added seperation between roofs and walls * [Feature] texture coordinates rotation * took back the seperation of texture rotation between walls and roofs because the user will be specifying them with different symbols anyway * Fixed docs test not passing issue * Fixed compilation problem * Fixed compilation problem * Executed sipify_all trying to fix test not passing issue * [ui] Harmonize play button across the board * [ui] Better network logger icon * [ui] Harmonize loop widget of 3D map view * [ui] Add a record icon and use it in the network logger panel * [ui] Move icon-less network logger toolbar actions under settings menu * [ui] Reflect playback stay in the 3D map view's play button * Fix azure * Use prefix increment operator to fix cppcheck warnings related to postfixOperator * [afs] Read field aliases from layer definition * Fix ui build warning * [FEATURE][processing] Add modeler algorithm to set a project expression variable Allows a model to set Project-level expression variables during execution. Especially useful with the new Export Print Layout algorithms to allow models which dynamically set variables used in a layout prior to export. * Add test * Refactoring according to code review Co-authored-by: nirvn <nirvn.asia@gmail.com> Co-authored-by: Even Rouault <even.rouault@spatialys.com> Co-authored-by: Nyall Dawson <nyall.dawson@gmail.com>
1 parent 1219f55 commit cc96e9e

21 files changed

+681
-173
lines changed
 

‎python/3d/auto_generated/qgsphongmaterialsettings.sip.in

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ Returns specular color component
4545
float shininess() const;
4646
%Docstring
4747
Returns shininess of the surface
48+
%End
49+
bool isUsingDiffuseTexture() const;
50+
%Docstring
51+
Returns whether the diffuse texture is used
52+
%End
53+
QString texturePath() const;
54+
%Docstring
55+
Returns the diffuse texture path
56+
%End
57+
58+
float textureScale() const;
59+
%Docstring
60+
Returns the texture scale
61+
The texture scale changes the size of the displayed texture in the 3D scene
62+
If the texture scale is less than 1 the texture will be stretched
63+
%End
64+
65+
float textureRotation() const;
66+
%Docstring
67+
Returns the texture's rotation in degrees
4868
%End
4969

5070
void setAmbient( const QColor &ambient );
@@ -62,6 +82,26 @@ Sets specular color component
6282
void setShininess( float shininess );
6383
%Docstring
6484
Sets shininess of the surface
85+
%End
86+
void useTexture( bool used );
87+
%Docstring
88+
Sets whether the diffuse texture will be used
89+
%End
90+
void setTexturePath( QString texturePath );
91+
%Docstring
92+
Sets the path of the texture
93+
%End
94+
95+
void setTextureScale( float scale );
96+
%Docstring
97+
Sets the texture scale
98+
The texture scale changes the size of the displayed texture in the 3D scene
99+
If the texture scale is less than 1 the texture will be stretched
100+
%End
101+
102+
void setTextureRotation( float rotation );
103+
%Docstring
104+
Sets the texture rotation in degrees
65105
%End
66106

67107
void readXml( const QDomElement &elem );

‎python/3d/auto_generated/symbols/qgspolygon3dsymbol.sip.in

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,20 @@ Returns edge lines color
158158
Sets edge lines color
159159

160160
.. versionadded:: 3.8
161+
%End
162+
163+
void setRenderedFacade( int side );
164+
%Docstring
165+
Sets which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
166+
167+
.. versionadded:: 3.16
168+
%End
169+
170+
int renderedFacade() const;
171+
%Docstring
172+
Returns which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
173+
174+
.. versionadded:: 3.16
161175
%End
162176

163177
};

‎python/core/auto_generated/qgstessellator.sip.in

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ Optionally provides extrusion by adding triangles that serve as walls when extru
2727
#include "qgstessellator.h"
2828
%End
2929
public:
30-
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
30+
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
31+
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
3132
%Docstring
3233
Creates tessellator with a specified origin point of the world (in map coordinates)
3334
%End
3435

35-
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false );
36+
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
37+
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
3638
%Docstring
3739
Creates tessellator with a specified ``bounds`` of input geometry coordinates.
3840
This constructor allows the tessellator to map input coordinates to a desirable range for numerical

‎src/3d/qgsphongmaterialsettings.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ void QgsPhongMaterialSettings::readXml( const QDomElement &elem )
2424
mDiffuse = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "diffuse" ) ) );
2525
mSpecular = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "specular" ) ) );
2626
mShininess = elem.attribute( QStringLiteral( "shininess" ) ).toFloat();
27+
mIsUsingDiffuseTexture = elem.attribute( QStringLiteral( "is_using_diffuse_texture" ), QStringLiteral( "0" ) ).toInt();
28+
mTexturePath = elem.attribute( QStringLiteral( "diffuse_texture_path" ), QString() );
29+
mTextureScale = elem.attribute( QStringLiteral( "texture_scale" ), QString( "1.0" ) ).toFloat();
30+
mTextureRotation = elem.attribute( QStringLiteral( "texture-rotation" ), QString( "0.0" ) ).toFloat();
2731
}
2832

2933
void QgsPhongMaterialSettings::writeXml( QDomElement &elem ) const
@@ -32,4 +36,8 @@ void QgsPhongMaterialSettings::writeXml( QDomElement &elem ) const
3236
elem.setAttribute( QStringLiteral( "diffuse" ), QgsSymbolLayerUtils::encodeColor( mDiffuse ) );
3337
elem.setAttribute( QStringLiteral( "specular" ), QgsSymbolLayerUtils::encodeColor( mSpecular ) );
3438
elem.setAttribute( QStringLiteral( "shininess" ), mShininess );
39+
elem.setAttribute( QStringLiteral( "is_using_diffuse_texture" ), mIsUsingDiffuseTexture );
40+
elem.setAttribute( QStringLiteral( "diffuse_texture_path" ), mTexturePath );
41+
elem.setAttribute( QStringLiteral( "texture_scale" ), mTextureScale );
42+
elem.setAttribute( QStringLiteral( "texture-rotation" ), mTextureRotation );
3543
}

‎src/3d/qgsphongmaterialsettings.h

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class _3D_EXPORT QgsPhongMaterialSettings
3939
: mAmbient( QColor::fromRgbF( 0.1f, 0.1f, 0.1f, 1.0f ) )
4040
, mDiffuse( QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) )
4141
, mSpecular( QColor::fromRgbF( 1.0f, 1.0f, 1.0f, 1.0f ) )
42+
, mIsUsingDiffuseTexture( false )
43+
, mTexturePath( QString() )
44+
, mTextureScale( 1.0f )
45+
, mTextureRotation( 0.0f )
4246
{
4347
}
4448

@@ -50,6 +54,20 @@ class _3D_EXPORT QgsPhongMaterialSettings
5054
QColor specular() const { return mSpecular; }
5155
//! Returns shininess of the surface
5256
float shininess() const { return mShininess; }
57+
//! Returns whether the diffuse texture is used
58+
bool isUsingDiffuseTexture() const { return mIsUsingDiffuseTexture; }
59+
//! Returns the diffuse texture path
60+
QString texturePath() const { return mTexturePath; }
61+
62+
/**
63+
* Returns the texture scale
64+
* The texture scale changes the size of the displayed texture in the 3D scene
65+
* If the texture scale is less than 1 the texture will be stretched
66+
*/
67+
float textureScale() const { return mTextureScale; }
68+
69+
//! Returns the texture's rotation in degrees
70+
float textureRotation() const { return mTextureRotation; }
5371

5472
//! Sets ambient color component
5573
void setAmbient( const QColor &ambient ) { mAmbient = ambient; }
@@ -59,6 +77,20 @@ class _3D_EXPORT QgsPhongMaterialSettings
5977
void setSpecular( const QColor &specular ) { mSpecular = specular; }
6078
//! Sets shininess of the surface
6179
void setShininess( float shininess ) { mShininess = shininess; }
80+
//! Sets whether the diffuse texture will be used
81+
void useTexture( bool used ) { mIsUsingDiffuseTexture = used; }
82+
//! Sets the path of the texture
83+
void setTexturePath( QString texturePath ) { mTexturePath = texturePath; }
84+
85+
/**
86+
* Sets the texture scale
87+
* The texture scale changes the size of the displayed texture in the 3D scene
88+
* If the texture scale is less than 1 the texture will be stretched
89+
*/
90+
void setTextureScale( float scale ) { mTextureScale = scale; }
91+
92+
//! Sets the texture rotation in degrees
93+
void setTextureRotation( float rotation ) { mTextureRotation = rotation; }
6294

6395
//! Reads settings from a DOM element
6496
void readXml( const QDomElement &elem );
@@ -70,14 +102,22 @@ class _3D_EXPORT QgsPhongMaterialSettings
70102
return mAmbient == other.mAmbient &&
71103
mDiffuse == other.mDiffuse &&
72104
mSpecular == other.mSpecular &&
73-
mShininess == other.mShininess;
105+
mShininess == other.mShininess &&
106+
mIsUsingDiffuseTexture == other.mIsUsingDiffuseTexture &&
107+
mTexturePath == other.mTexturePath &&
108+
mTextureScale == other.mTextureScale &&
109+
mTextureRotation == other.mTextureRotation;
74110
}
75111

76112
private:
77113
QColor mAmbient;
78114
QColor mDiffuse;
79115
QColor mSpecular;
80116
float mShininess = 0.0f;
117+
bool mIsUsingDiffuseTexture;
118+
QString mTexturePath;
119+
float mTextureScale;
120+
float mTextureRotation;
81121
};
82122

83123

‎src/3d/qgstessellatedpolygongeometry.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@
2525
#include "qgspolygon.h"
2626

2727

28-
QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( QNode *parent )
29-
: Qt3DRender::QGeometry( parent )
28+
QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( bool _withNormals, bool _invertNormals, bool _addBackFaces, bool _addTextureCoords, QNode *parent )
29+
: Qt3DRender::QGeometry( parent ),
30+
mWithNormals( _withNormals ),
31+
mInvertNormals( _invertNormals ),
32+
mAddBackFaces( _addBackFaces ),
33+
mAddTextureCoords( _addTextureCoords )
3034
{
3135
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
3236
mVertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this );
3337
#else
3438
mVertexBuffer = new Qt3DRender::QBuffer( this );
3539
#endif
3640

37-
QgsTessellator tmpTess( 0, 0, mWithNormals );
41+
QgsTessellator tmpTess( 0, 0, mWithNormals, false, false, false, mAddTextureCoords );
3842
const int stride = tmpTess.stride();
3943

4044
mPositionAttribute = new Qt3DRender::QAttribute( this );
@@ -58,6 +62,18 @@ QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( QNode *parent )
5862
mNormalAttribute->setByteOffset( 3 * sizeof( float ) );
5963
addAttribute( mNormalAttribute );
6064
}
65+
if ( mAddTextureCoords )
66+
{
67+
mTextureCoordsAttribute = new Qt3DRender::QAttribute( this );
68+
mTextureCoordsAttribute->setName( Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName() );
69+
mTextureCoordsAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
70+
mTextureCoordsAttribute->setVertexSize( 2 );
71+
mTextureCoordsAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
72+
mTextureCoordsAttribute->setBuffer( mVertexBuffer );
73+
mTextureCoordsAttribute->setByteStride( stride );
74+
mTextureCoordsAttribute->setByteOffset( mWithNormals ? 6 * sizeof( float ) : 3 * sizeof( float ) );
75+
addAttribute( mTextureCoordsAttribute );
76+
}
6177
}
6278

6379
void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
@@ -66,7 +82,7 @@ void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &poly
6682
mTriangleIndexStartingIndices.reserve( polygons.count() );
6783
mTriangleIndexFids.reserve( polygons.count() );
6884

69-
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces );
85+
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces, false, mAddTextureCoords );
7086
for ( int i = 0; i < polygons.count(); ++i )
7187
{
7288
Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 );
@@ -88,6 +104,8 @@ void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &poly
88104
mPositionAttribute->setCount( nVerts );
89105
if ( mNormalAttribute )
90106
mNormalAttribute->setCount( nVerts );
107+
if ( mAddTextureCoords )
108+
mTextureCoordsAttribute->setCount( nVerts );
91109
}
92110

93111
void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData, int vertexCount, const QVector<QgsFeatureId> &triangleIndexFids, const QVector<uint> &triangleIndexStartingIndices )
@@ -99,6 +117,8 @@ void QgsTessellatedPolygonGeometry::setData( const QByteArray &vertexBufferData,
99117
mPositionAttribute->setCount( vertexCount );
100118
if ( mNormalAttribute )
101119
mNormalAttribute->setCount( vertexCount );
120+
if ( mTextureCoordsAttribute )
121+
mTextureCoordsAttribute->setCount( vertexCount );
102122
}
103123

104124

‎src/3d/qgstessellatedpolygongeometry.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
4444
Q_OBJECT
4545
public:
4646
//! Constructor
47-
QgsTessellatedPolygonGeometry( QNode *parent = nullptr );
47+
QgsTessellatedPolygonGeometry( bool _withNormals = true, bool invertNormals = false, bool addBackFaces = false, bool addTextureCoords = false, QNode *parent = nullptr );
4848

4949
//! Returns whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
5050
bool invertNormals() const { return mInvertNormals; }
@@ -63,6 +63,12 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
6363
*/
6464
void setAddBackFaces( bool add ) { mAddBackFaces = add; }
6565

66+
/**
67+
* Sets whether the texture coordinates will be generated
68+
* \since QGIS 3.16
69+
*/
70+
void setAddTextureCoords( bool add ) { mAddTextureCoords = add; }
71+
6672
//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
6773
void setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );
6874

@@ -83,6 +89,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
8389

8490
Qt3DRender::QAttribute *mPositionAttribute = nullptr;
8591
Qt3DRender::QAttribute *mNormalAttribute = nullptr;
92+
Qt3DRender::QAttribute *mTextureCoordsAttribute = nullptr;
8693
Qt3DRender::QBuffer *mVertexBuffer = nullptr;
8794

8895
QVector<QgsFeatureId> mTriangleIndexFids;
@@ -91,6 +98,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
9198
bool mWithNormals = true;
9299
bool mInvertNormals = false;
93100
bool mAddBackFaces = false;
101+
bool mAddTextureCoords = false;
94102
};
95103

96104
#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H

‎src/3d/symbols/qgsline3dsymbol_p.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ void QgsBufferedLine3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, cons
182182
QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
183183
int nVerts = data.count() / out.tessellator->stride();
184184

185-
QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry;
185+
QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( false, false, false, false );
186186
geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
187187

188188
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;

‎src/3d/symbols/qgsmesh3dsymbol_p.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,7 @@ Qt3DRender::QGeometryRenderer *QgsMesh3DSymbolEntityNode::renderer( const Qgs3DM
137137
// Polygons from mesh are already triangles, but
138138
// call QgsTessellatedPolygonGeometry to
139139
// use symbol settings for back faces, normals, etc
140-
mGeometry = new QgsTessellatedPolygonGeometry;
141-
mGeometry->setInvertNormals( false );
142-
mGeometry->setAddBackFaces( symbol.addBackFaces() );
140+
mGeometry = new QgsTessellatedPolygonGeometry( true, false, symbol.addBackFaces(), symbol.material().isUsingDiffuseTexture() );
143141
QList<float> extrusionHeightPerPolygon;
144142
mGeometry->setPolygons( polygons, fids, origin, 0.0, extrusionHeightPerPolygon );
145143

‎src/3d/symbols/qgspolygon3dsymbol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
3737
elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) );
3838
elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
3939
elemDataProperties.setAttribute( QStringLiteral( "add-back-faces" ), mAddBackFaces ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
40+
elemDataProperties.setAttribute( QStringLiteral( "rendered-facade" ), mRenderedFacade );
4041
elem.appendChild( elemDataProperties );
4142

4243
QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
@@ -66,6 +67,7 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon
6667
mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) );
6768
mInvertNormals = elemDataProperties.attribute( QStringLiteral( "invert-normals" ) ).toInt();
6869
mAddBackFaces = elemDataProperties.attribute( QStringLiteral( "add-back-faces" ) ).toInt();
70+
mRenderedFacade = elemDataProperties.attribute( QStringLiteral( "rendered-facade" ), "3" ).toInt();
6971

7072
QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
7173
mMaterial.readXml( elemMaterial );

‎src/3d/symbols/qgspolygon3dsymbol.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
128128
*/
129129
void setEdgeColor( const QColor &color ) { mEdgeColor = color; }
130130

131+
/**
132+
* Sets which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
133+
* \since QGIS 3.16
134+
*/
135+
void setRenderedFacade( int side ) { mRenderedFacade = side; }
136+
137+
/**
138+
* Returns which facade of the buildings is rendered (0 for None, 1 for Walls, 2 for Roofs, 3 for WallsAndRoofs)
139+
* \since QGIS 3.16
140+
*/
141+
int renderedFacade() const { return mRenderedFacade; }
142+
131143
private:
132144
//! how to handle altitude of vector features
133145
Qgs3DTypes::AltitudeClamping mAltClamping = Qgs3DTypes::AltClampRelative;
@@ -140,6 +152,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
140152
Qgs3DTypes::CullingMode mCullingMode = Qgs3DTypes::NoCulling; //!< Front/back culling mode
141153
bool mInvertNormals = false;
142154
bool mAddBackFaces = false;
155+
int mRenderedFacade = 3;
143156

144157
bool mEdgesEnabled = false; //!< Whether to highlight edges
145158
float mEdgeWidth = 1.f; //!< Width of edges in pixels

‎src/3d/symbols/qgspolygon3dsymbol_p.cpp

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
#include "qgstessellator.h"
2323

2424
#include <Qt3DCore/QTransform>
25+
#include <Qt3DRender/QMaterial>
2526
#include <Qt3DExtras/QPhongMaterial>
27+
28+
#include <Qt3DExtras/QDiffuseMapMaterial>
29+
#include <Qt3DRender/QAbstractTextureImage>
30+
#include <Qt3DRender/QTexture>
31+
2632
#include <Qt3DRender/QEffect>
2733
#include <Qt3DRender/QTechnique>
2834
#include <Qt3DRender/QCullFace>
@@ -60,7 +66,7 @@ class QgsPolygon3DSymbolHandler : public QgsFeature3DHandler
6066

6167
void processPolygon( QgsPolygon *polyClone, QgsFeatureId fid, float height, float extrusionHeight, const Qgs3DRenderContext &context, PolygonData &out );
6268
void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PolygonData &out, bool selected );
63-
Qt3DExtras::QPhongMaterial *material( const QgsPolygon3DSymbol &symbol ) const;
69+
Qt3DRender::QMaterial *material( const QgsPolygon3DSymbol &symbol, bool isSelected, const Qgs3DRenderContext &context ) const;
6470

6571
// input specific for this class
6672
const QgsPolygon3DSymbol &mSymbol;
@@ -80,8 +86,8 @@ bool QgsPolygon3DSymbolHandler::prepare( const Qgs3DRenderContext &context, QSet
8086
outEdges.withAdjacency = true;
8187
outEdges.init( mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), &context.map() );
8288

83-
outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces() ) );
84-
outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces() ) );
89+
outNormal.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces(), false, mSymbol.material().isUsingDiffuseTexture(), mSymbol.renderedFacade(), mSymbol.material().textureRotation() ) );
90+
outSelected.tessellator.reset( new QgsTessellator( context.map().origin().x(), context.map().origin().y(), true, mSymbol.invertNormals(), mSymbol.addBackFaces(), false, mSymbol.material().isUsingDiffuseTexture(), mSymbol.renderedFacade(), mSymbol.material().textureRotation() ) );
8591

8692
QSet<QString> attrs = mSymbol.dataDefinedProperties().referencedFields( context.expressionContext() );
8793
attributeNames.unite( attrs );
@@ -219,21 +225,13 @@ void QgsPolygon3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs
219225
if ( out.tessellator->dataVerticesCount() == 0 )
220226
return; // nothing to show - no need to create the entity
221227

222-
Qt3DExtras::QPhongMaterial *mat = material( mSymbol );
223-
if ( selected )
224-
{
225-
// update the material with selection colors
226-
mat->setDiffuse( context.map().selectionColor() );
227-
mat->setAmbient( context.map().selectionColor().darker() );
228-
}
228+
Qt3DRender::QMaterial *mat = material( mSymbol, selected, context );
229229

230230
// extract vertex buffer data from tessellator
231231
QByteArray data( ( const char * )out.tessellator->data().constData(), out.tessellator->data().count() * sizeof( float ) );
232232
int nVerts = data.count() / out.tessellator->stride();
233233

234-
QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry;
235-
geometry->setInvertNormals( mSymbol.invertNormals() );
236-
geometry->setAddBackFaces( mSymbol.addBackFaces() );
234+
QgsTessellatedPolygonGeometry *geometry = new QgsTessellatedPolygonGeometry( true, mSymbol.invertNormals(), mSymbol.addBackFaces(), mSymbol.material().isUsingDiffuseTexture() );
237235
geometry->setData( data, nVerts, out.triangleIndexFids, out.triangleIndexStartingIndices );
238236

239237
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
@@ -264,28 +262,76 @@ static Qt3DRender::QCullFace::CullingMode _qt3DcullingMode( Qgs3DTypes::CullingM
264262
return Qt3DRender::QCullFace::NoCulling;
265263
}
266264

267-
Qt3DExtras::QPhongMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol &symbol ) const
265+
// front/back side culling
266+
static void applyCullingMode( Qgs3DTypes::CullingMode cullingMode, Qt3DRender::QMaterial *material )
268267
{
269-
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
270-
271-
// front/back side culling
272268
auto techniques = material->effect()->techniques();
273269
for ( auto tit = techniques.constBegin(); tit != techniques.constEnd(); ++tit )
274270
{
275271
auto renderPasses = ( *tit )->renderPasses();
276272
for ( auto rpit = renderPasses.begin(); rpit != renderPasses.end(); ++rpit )
277273
{
278274
Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace;
279-
cullFace->setMode( _qt3DcullingMode( symbol.cullingMode() ) );
275+
cullFace->setMode( _qt3DcullingMode( cullingMode ) );
280276
( *rpit )->addRenderState( cullFace );
281277
}
282278
}
279+
}
280+
281+
Qt3DRender::QMaterial *QgsPolygon3DSymbolHandler::material( const QgsPolygon3DSymbol &symbol, bool isSelected, const Qgs3DRenderContext &context ) const
282+
{
283+
Qt3DRender::QMaterial *retMaterial = nullptr;
284+
if ( symbol.material().isUsingDiffuseTexture() )
285+
{
286+
QString textureFilePath = symbol.material().texturePath();
287+
Qt3DExtras::QDiffuseMapMaterial *material = new Qt3DExtras::QDiffuseMapMaterial;
288+
289+
applyCullingMode( symbol.cullingMode(), material );
290+
291+
Qt3DRender::QTextureImage *textureImage = new Qt3DRender::QTextureImage;
292+
textureImage->setSource( QUrl::fromLocalFile( textureFilePath ) );
293+
material->diffuse()->addTextureImage( textureImage );
294+
295+
material->diffuse()->wrapMode()->setX( Qt3DRender::QTextureWrapMode::Repeat );
296+
material->diffuse()->wrapMode()->setY( Qt3DRender::QTextureWrapMode::Repeat );
297+
material->diffuse()->wrapMode()->setZ( Qt3DRender::QTextureWrapMode::Repeat );
298+
material->setSpecular( symbol.material().specular() );
299+
material->setShininess( symbol.material().shininess() );
300+
material->setTextureScale( symbol.material().textureScale() );
301+
302+
if ( isSelected )
303+
{
304+
// update the material with selection colors
305+
// TODO : dampen the color of diffuse texture
306+
// mat->setDiffuse( context.map().selectionColor() );
307+
material->setAmbient( context.map().selectionColor().darker() );
308+
}
309+
310+
retMaterial = material;
311+
312+
}
313+
else
314+
{
315+
316+
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
317+
318+
applyCullingMode( symbol.cullingMode(), material );
319+
320+
material->setAmbient( symbol.material().ambient() );
321+
material->setDiffuse( symbol.material().diffuse() );
322+
material->setSpecular( symbol.material().specular() );
323+
material->setShininess( symbol.material().shininess() );
324+
325+
if ( isSelected )
326+
{
327+
// update the material with selection colors
328+
material->setDiffuse( context.map().selectionColor() );
329+
material->setAmbient( context.map().selectionColor().darker() );
330+
}
331+
retMaterial = material;
332+
}
283333

284-
material->setAmbient( symbol.material().ambient() );
285-
material->setDiffuse( symbol.material().diffuse() );
286-
material->setSpecular( symbol.material().specular() );
287-
material->setShininess( symbol.material().shininess() );
288-
return material;
334+
return retMaterial;
289335
}
290336

291337

‎src/app/3d/qgs3dmaptoolmeasureline.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ void Qgs3DMapToolMeasureLine::activate()
8888
// Initialize the line layer
8989
QString mapCRS = mCanvas->map()->crs().authid();
9090
mMeasurementLayer = new QgsVectorLayer( QStringLiteral( "LineStringZ?crs=" ) + mapCRS, QStringLiteral( "Measurement" ), QStringLiteral( "memory" ) );
91+
QgsProject::instance()->addMapLayer( mMeasurementLayer );
9192

9293
// Add feature to layer
9394
mMeasurementLayer->startEditing();
@@ -206,6 +207,7 @@ void Qgs3DMapToolMeasureLine::updateMeasurementLayer()
206207
QgsGeometryMap geometryMap;
207208
geometryMap.insert( 1, lineGeometry );
208209
mMeasurementLayer->dataProvider()->changeGeometryValues( geometryMap );
210+
mMeasurementLayer->reload();
209211
mCanvas->map()->setRenderers( QList<QgsAbstract3DRenderer *>() << mMeasurementLayer->renderer3D()->clone() );
210212
}
211213

‎src/app/3d/qgsphongmaterialwidget.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ QgsPhongMaterialWidget::QgsPhongMaterialWidget( QWidget *parent )
2525

2626
setMaterial( QgsPhongMaterialSettings() );
2727

28+
textureFile->setFilter( "Images (*.png *.xpm *.jpg *.jpeg *.bmp)" );
29+
// btnDiffuse->setV
30+
2831
connect( btnDiffuse, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
2932
connect( btnAmbient, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
3033
connect( btnSpecular, &QgsColorButton::colorChanged, this, &QgsPhongMaterialWidget::changed );
3134
connect( spinShininess, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
35+
connect( useDiffuseCheckBox, &QCheckBox::stateChanged, this, &QgsPhongMaterialWidget::changed );
36+
connect( textureFile, &QgsFileWidget::fileChanged, this, &QgsPhongMaterialWidget::changed );
37+
connect( textureScaleSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
38+
connect( textureRotationSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
39+
this->activateTexturingUI( false );
3240
}
3341

3442
void QgsPhongMaterialWidget::setDiffuseVisible( bool visible )
@@ -48,6 +56,10 @@ void QgsPhongMaterialWidget::setMaterial( const QgsPhongMaterialSettings &materi
4856
btnAmbient->setColor( material.ambient() );
4957
btnSpecular->setColor( material.specular() );
5058
spinShininess->setValue( material.shininess() );
59+
useDiffuseCheckBox->setCheckState( material.isUsingDiffuseTexture() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked );
60+
textureFile->setFilePath( material.texturePath() );
61+
textureScaleSpinBox->setValue( material.textureScale() );
62+
textureRotationSpinBox->setValue( material.textureRotation() );
5163
}
5264

5365
QgsPhongMaterialSettings QgsPhongMaterialWidget::material() const
@@ -57,5 +69,19 @@ QgsPhongMaterialSettings QgsPhongMaterialWidget::material() const
5769
m.setAmbient( btnAmbient->color() );
5870
m.setSpecular( btnSpecular->color() );
5971
m.setShininess( spinShininess->value() );
72+
m.useTexture( useDiffuseCheckBox->checkState() == Qt::CheckState::Checked );
73+
m.setTexturePath( textureFile->filePath() );
74+
m.setTextureScale( textureScaleSpinBox->value() );
75+
m.setTextureRotation( textureRotationSpinBox->value() );
6076
return m;
6177
}
78+
79+
void QgsPhongMaterialWidget::activateTexturingUI( bool activated )
80+
{
81+
lblTextureScale->setVisible( activated );
82+
lblTextureRotation->setVisible( activated );
83+
textureScaleSpinBox->setVisible( activated );
84+
textureRotationSpinBox->setVisible( activated );
85+
useDiffuseCheckBox->setVisible( activated );
86+
textureFile->setVisible( activated );
87+
}

‎src/app/3d/qgsphongmaterialwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class QgsPhongMaterialWidget : public QWidget, private Ui::PhongMaterialWidget
3636
void setMaterial( const QgsPhongMaterialSettings &material );
3737
QgsPhongMaterialSettings material() const;
3838

39+
//! activates the texturing UI (to make sure texturing UI isn't visible when the user doesn't need it like in the 3D configuration window)
40+
void activateTexturingUI( bool activated );
3941
signals:
4042
void changed();
4143

‎src/app/3d/qgspolygon3dsymbolwidget.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
3232
connect( cboAltClamping, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
3333
connect( cboAltBinding, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
3434
connect( cboCullingMode, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
35+
connect( cboRenderedFacade, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPolygon3DSymbolWidget::changed );
3536
connect( chkAddBackFaces, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
3637
connect( chkInvertNormals, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
3738
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed );
@@ -40,6 +41,8 @@ QgsPolygon3DSymbolWidget::QgsPolygon3DSymbolWidget( QWidget *parent )
4041
connect( groupEdges, &QGroupBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
4142
connect( btnEdgeColor, &QgsColorButton::colorChanged, this, &QgsPolygon3DSymbolWidget::changed );
4243
connect( spinEdgeWidth, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPolygon3DSymbolWidget::changed );
44+
45+
widgetMaterial->activateTexturingUI( true );
4346
}
4447

4548
void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsVectorLayer *layer )
@@ -49,6 +52,8 @@ void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsV
4952
cboAltClamping->setCurrentIndex( static_cast<int>( symbol.altitudeClamping() ) );
5053
cboAltBinding->setCurrentIndex( static_cast<int>( symbol.altitudeBinding() ) );
5154
cboCullingMode->setCurrentIndex( static_cast<int>( symbol.cullingMode() ) );
55+
cboRenderedFacade->setCurrentIndex( symbol.renderedFacade() );
56+
5257
chkAddBackFaces->setChecked( symbol.addBackFaces() );
5358
chkInvertNormals->setChecked( symbol.invertNormals() );
5459
widgetMaterial->setMaterial( symbol.material() );
@@ -69,6 +74,7 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
6974
sym.setAltitudeClamping( static_cast<Qgs3DTypes::AltitudeClamping>( cboAltClamping->currentIndex() ) );
7075
sym.setAltitudeBinding( static_cast<Qgs3DTypes::AltitudeBinding>( cboAltBinding->currentIndex() ) );
7176
sym.setCullingMode( static_cast<Qgs3DTypes::CullingMode>( cboCullingMode->currentIndex() ) );
77+
sym.setRenderedFacade( cboRenderedFacade->currentIndex() );
7278
sym.setAddBackFaces( chkAddBackFaces->isChecked() );
7379
sym.setInvertNormals( chkInvertNormals->isChecked() );
7480
sym.setMaterial( widgetMaterial->material() );

‎src/core/qgstessellator.cpp

Lines changed: 222 additions & 48 deletions
Large diffs are not rendered by default.

‎src/core/qgstessellator.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class CORE_EXPORT QgsTessellator
4141
{
4242
public:
4343
//! Creates tessellator with a specified origin point of the world (in map coordinates)
44-
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
44+
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
45+
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
4546

4647
/**
4748
* Creates tessellator with a specified \a bounds of input geometry coordinates.
@@ -52,7 +53,8 @@ class CORE_EXPORT QgsTessellator
5253
*
5354
* \since QGIS 3.10
5455
*/
55-
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false );
56+
QgsTessellator( const QgsRectangle &bounds, bool addNormals, bool invertNormals = false, bool addBackFaces = false, bool noZ = false,
57+
bool addTextureCoords = false, int facade = 3, float textureRotation = 0.0f );
5658

5759
//! Tessellates a triangle and adds its vertex entries to the output data array
5860
void addPolygon( const QgsPolygon &polygon, float extrusionHeight );
@@ -95,9 +97,12 @@ class CORE_EXPORT QgsTessellator
9597
bool mAddNormals = false;
9698
bool mInvertNormals = false;
9799
bool mAddBackFaces = false;
100+
bool mAddTextureCoords = false;
98101
QVector<float> mData;
99102
int mStride;
100103
bool mNoZ = false;
104+
int mTessellatedFacade = 3;
105+
float mTextureRotation = 0.0f;
101106

102107
float mZMin = std::numeric_limits<float>::max();
103108
float mZMax = std::numeric_limits<float>::min();

‎src/ui/3d/phongmaterialwidget.ui

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>338</width>
10-
<height>138</height>
9+
<width>394</width>
10+
<height>248</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string notr="true">Form</string>
1515
</property>
16-
<layout class="QFormLayout" name="formLayout">
16+
<layout class="QGridLayout" name="gridLayout">
1717
<item row="0" column="0">
1818
<widget class="QLabel" name="lblDiffuse">
1919
<property name="text">
@@ -97,6 +97,59 @@
9797
</property>
9898
</widget>
9999
</item>
100+
<item row="4" column="0">
101+
<widget class="QCheckBox" name="useDiffuseCheckBox">
102+
<property name="text">
103+
<string>Use diffuse texture</string>
104+
</property>
105+
<property name="checked">
106+
<bool>false</bool>
107+
</property>
108+
</widget>
109+
</item>
110+
<item row="4" column="1">
111+
<widget class="QgsFileWidget" name="textureFile" native="true"/>
112+
</item>
113+
<item row="5" column="0">
114+
<widget class="QLabel" name="lblTextureScale">
115+
<property name="text">
116+
<string>Texture scale</string>
117+
</property>
118+
</widget>
119+
</item>
120+
<item row="5" column="1">
121+
<widget class="QDoubleSpinBox" name="textureScaleSpinBox">
122+
<property name="minimum">
123+
<double>0.010000000000000</double>
124+
</property>
125+
<property name="maximum">
126+
<double>100.000000000000000</double>
127+
</property>
128+
<property name="singleStep">
129+
<double>1.000000000000000</double>
130+
</property>
131+
<property name="value">
132+
<double>1.000000000000000</double>
133+
</property>
134+
</widget>
135+
</item>
136+
<item row="6" column="0">
137+
<widget class="QLabel" name="lblTextureRotation">
138+
<property name="text">
139+
<string>Texture rotation</string>
140+
</property>
141+
</widget>
142+
</item>
143+
<item row="6" column="1">
144+
<widget class="QDoubleSpinBox" name="textureRotationSpinBox">
145+
<property name="maximum">
146+
<double>360.000000000000000</double>
147+
</property>
148+
<property name="singleStep">
149+
<double>5.000000000000000</double>
150+
</property>
151+
</widget>
152+
</item>
100153
</layout>
101154
</widget>
102155
<customwidgets>
@@ -111,6 +164,12 @@
111164
<header>qgscolorbutton.h</header>
112165
<container>1</container>
113166
</customwidget>
167+
<customwidget>
168+
<class>QgsFileWidget</class>
169+
<extends>QWidget</extends>
170+
<header>qgsfilewidget.h</header>
171+
<container>1</container>
172+
</customwidget>
114173
</customwidgets>
115174
<resources/>
116175
<connections/>

‎src/ui/3d/polygon3dsymbolwidget.ui

Lines changed: 127 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -33,66 +33,29 @@
3333
</property>
3434
</widget>
3535
</item>
36-
<item row="6" column="0" colspan="2">
37-
<widget class="QCheckBox" name="chkInvertNormals">
38-
<property name="text">
39-
<string>Invert normals (experimental)</string>
40-
</property>
41-
</widget>
42-
</item>
43-
<item row="4" column="1">
44-
<widget class="QComboBox" name="cboCullingMode">
45-
<item>
46-
<property name="text">
47-
<string>No culling</string>
48-
</property>
49-
</item>
50-
<item>
51-
<property name="text">
52-
<string>Front</string>
53-
</property>
54-
</item>
55-
<item>
56-
<property name="text">
57-
<string>Back</string>
58-
</property>
59-
</item>
60-
</widget>
61-
</item>
6236
<item row="1" column="0">
6337
<widget class="QLabel" name="label_2">
6438
<property name="text">
6539
<string>Extrusion</string>
6640
</property>
6741
</widget>
6842
</item>
69-
<item row="1" column="1">
70-
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
71-
<property name="maximum">
72-
<double>99999.000000000000000</double>
73-
</property>
74-
</widget>
75-
</item>
76-
<item row="10" column="0" colspan="3">
77-
<spacer name="verticalSpacer">
78-
<property name="orientation">
79-
<enum>Qt::Vertical</enum>
80-
</property>
81-
<property name="sizeHint" stdset="0">
82-
<size>
83-
<width>20</width>
84-
<height>40</height>
85-
</size>
86-
</property>
87-
</spacer>
88-
</item>
89-
<item row="7" column="0" colspan="3">
43+
<item row="8" column="0" colspan="3">
9044
<widget class="QgsCollapsibleGroupBox" name="groupShading">
9145
<property name="title">
9246
<string>Shading</string>
9347
</property>
9448
<layout class="QGridLayout" name="gridLayout_3">
95-
<property name="margin">
49+
<property name="leftMargin">
50+
<number>0</number>
51+
</property>
52+
<property name="topMargin">
53+
<number>0</number>
54+
</property>
55+
<property name="rightMargin">
56+
<number>0</number>
57+
</property>
58+
<property name="bottomMargin">
9659
<number>0</number>
9760
</property>
9861
<item row="0" column="0">
@@ -101,17 +64,17 @@
10164
</layout>
10265
</widget>
10366
</item>
104-
<item row="4" column="0">
105-
<widget class="QLabel" name="label_5">
67+
<item row="1" column="2">
68+
<widget class="QgsPropertyOverrideButton" name="btnExtrusionDD">
10669
<property name="text">
107-
<string>Culling mode</string>
70+
<string></string>
10871
</property>
10972
</widget>
11073
</item>
111-
<item row="1" column="2">
112-
<widget class="QgsPropertyOverrideButton" name="btnExtrusionDD">
74+
<item row="7" column="0" colspan="2">
75+
<widget class="QCheckBox" name="chkInvertNormals">
11376
<property name="text">
114-
<string></string>
77+
<string>Invert normals (experimental)</string>
11578
</property>
11679
</widget>
11780
</item>
@@ -134,7 +97,68 @@
13497
</item>
13598
</widget>
13699
</item>
137-
<item row="8" column="0" colspan="3">
100+
<item row="1" column="1">
101+
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
102+
<property name="maximum">
103+
<double>99999.000000000000000</double>
104+
</property>
105+
</widget>
106+
</item>
107+
<item row="3" column="0">
108+
<widget class="QLabel" name="label_4">
109+
<property name="text">
110+
<string>Altitude binding</string>
111+
</property>
112+
</widget>
113+
</item>
114+
<item row="4" column="1">
115+
<widget class="QComboBox" name="cboCullingMode">
116+
<item>
117+
<property name="text">
118+
<string>No culling</string>
119+
</property>
120+
</item>
121+
<item>
122+
<property name="text">
123+
<string>Front</string>
124+
</property>
125+
</item>
126+
<item>
127+
<property name="text">
128+
<string>Back</string>
129+
</property>
130+
</item>
131+
</widget>
132+
</item>
133+
<item row="3" column="1">
134+
<widget class="QComboBox" name="cboAltBinding">
135+
<item>
136+
<property name="text">
137+
<string>Vertex</string>
138+
</property>
139+
</item>
140+
<item>
141+
<property name="text">
142+
<string>Centroid</string>
143+
</property>
144+
</item>
145+
</widget>
146+
</item>
147+
<item row="0" column="2">
148+
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
149+
<property name="text">
150+
<string>…</string>
151+
</property>
152+
</widget>
153+
</item>
154+
<item row="2" column="0">
155+
<widget class="QLabel" name="label_3">
156+
<property name="text">
157+
<string>Altitude clamping</string>
158+
</property>
159+
</widget>
160+
</item>
161+
<item row="9" column="0" colspan="3">
138162
<widget class="QGroupBox" name="groupEdges">
139163
<property name="title">
140164
<string>Edges</string>
@@ -195,53 +219,72 @@
195219
</layout>
196220
</widget>
197221
</item>
198-
<item row="5" column="0">
222+
<item row="4" column="0">
223+
<widget class="QLabel" name="label_5">
224+
<property name="text">
225+
<string>Culling mode</string>
226+
</property>
227+
</widget>
228+
</item>
229+
<item row="6" column="0">
199230
<widget class="QCheckBox" name="chkAddBackFaces">
200231
<property name="text">
201232
<string>Add back faces</string>
202233
</property>
203234
</widget>
204235
</item>
205-
<item row="0" column="2">
206-
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
236+
<item row="0" column="0">
237+
<widget class="QLabel" name="label">
207238
<property name="text">
208-
<string></string>
239+
<string>Height</string>
209240
</property>
210241
</widget>
211242
</item>
212-
<item row="3" column="1">
213-
<widget class="QComboBox" name="cboAltBinding">
243+
<item row="11" column="0" colspan="3">
244+
<spacer name="verticalSpacer">
245+
<property name="orientation">
246+
<enum>Qt::Vertical</enum>
247+
</property>
248+
<property name="sizeHint" stdset="0">
249+
<size>
250+
<width>20</width>
251+
<height>40</height>
252+
</size>
253+
</property>
254+
</spacer>
255+
</item>
256+
<item row="5" column="0">
257+
<widget class="QLabel" name="label_8">
258+
<property name="text">
259+
<string>Rendered facade</string>
260+
</property>
261+
</widget>
262+
</item>
263+
<item row="5" column="1">
264+
<widget class="QComboBox" name="cboRenderedFacade">
265+
<property name="currentIndex">
266+
<number>3</number>
267+
</property>
214268
<item>
215269
<property name="text">
216-
<string>Vertex</string>
270+
<string>No facades</string>
217271
</property>
218272
</item>
219273
<item>
220274
<property name="text">
221-
<string>Centroid</string>
275+
<string>Walls</string>
276+
</property>
277+
</item>
278+
<item>
279+
<property name="text">
280+
<string>Roofs</string>
281+
</property>
282+
</item>
283+
<item>
284+
<property name="text">
285+
<string>Walls and roofs</string>
222286
</property>
223287
</item>
224-
</widget>
225-
</item>
226-
<item row="3" column="0">
227-
<widget class="QLabel" name="label_4">
228-
<property name="text">
229-
<string>Altitude binding</string>
230-
</property>
231-
</widget>
232-
</item>
233-
<item row="0" column="0">
234-
<widget class="QLabel" name="label">
235-
<property name="text">
236-
<string>Height</string>
237-
</property>
238-
</widget>
239-
</item>
240-
<item row="2" column="0">
241-
<widget class="QLabel" name="label_3">
242-
<property name="text">
243-
<string>Altitude clamping</string>
244-
</property>
245288
</widget>
246289
</item>
247290
</layout>

‎tests/src/3d/testqgstessellator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ void TestQgsTessellator::testCrash2DTriangle()
406406
// test tessellation of a 2D triangle - https://github.com/qgis/QGIS/issues/36024
407407
QgsPolygon polygon;
408408
polygon.fromWkt( "Polygon((0 0, 42 0, 42 42, 0 0))" );
409-
QgsTessellator t( 0, 0, true );
409+
QgsTessellator t( 0, 0, true, false, false, true );
410410
t.addPolygon( polygon, 0 ); // must not crash - that's all we test here
411411
}
412412

0 commit comments

Comments
 (0)
Please sign in to comment.