Skip to content

Commit 875da3f

Browse files
committedMay 8, 2018
[FEATURE] Optionally add back faces of polygons in tessellator
Often the polygonZ/multipatch data do not have consistent ordering of vertices (e.g. all clock-wise or counter clock-wise). Disabling culling helps to avoid seemingly missing surfaces, but the shading is still not correct due to reversed normals. This new option to add back faces fixes the problem: for each triangle we create both front and back face with correct normals - at the expense of increased number of vertex data.
1 parent 00d7ab6 commit 875da3f

10 files changed

+150
-64
lines changed
 

‎src/3d/qgstessellatedpolygongeometry.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ QgsTessellatedPolygonGeometry::~QgsTessellatedPolygonGeometry()
6262

6363
void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
6464
{
65-
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals );
65+
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces );
6666
for ( int i = 0; i < polygons.count(); ++i )
6767
{
6868
QgsPolygon *polygon = polygons.at( i );

‎src/3d/qgstessellatedpolygongeometry.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
4646
//! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
4747
void setInvertNormals( bool invert ) { mInvertNormals = invert; }
4848

49+
//! Returns whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
50+
bool addBackFaces() const { return mAddBackFaces; }
51+
//! Sets whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
52+
void setAddBackFaces( bool add ) { mAddBackFaces = add; }
53+
4954
//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
5055
void setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );
5156

@@ -57,6 +62,7 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
5762

5863
bool mWithNormals = true;
5964
bool mInvertNormals = false;
65+
bool mAddBackFaces = false;
6066
};
6167

6268
#endif // QGSTESSELLATEDPOLYGONGEOMETRY_H

‎src/3d/qgstessellator.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@ static void make_quad( float x0, float y0, float z0, float x1, float y1, float z
6666
}
6767

6868

69-
QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals )
69+
QgsTessellator::QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals, bool addBackFaces )
7070
: mOriginX( originX )
7171
, mOriginY( originY )
7272
, mAddNormals( addNormals )
7373
, mInvertNormals( invertNormals )
74+
, mAddBackFaces( addBackFaces )
7475
{
7576
mStride = 3 * sizeof( float );
7677
if ( addNormals )
@@ -368,6 +369,18 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
368369
if ( mAddNormals )
369370
mData << pNormal.x() << pNormal.z() << - pNormal.y();
370371
}
372+
373+
if ( mAddBackFaces )
374+
{
375+
// the same triangle with reversed order of coordinates and inverted normal
376+
for ( int i = 2; i >= 0; i-- )
377+
{
378+
exterior->pointAt( i, pt, vt );
379+
mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY;
380+
if ( mAddNormals )
381+
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
382+
}
383+
}
371384
}
372385
else
373386
{
@@ -476,6 +489,24 @@ void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeigh
476489
if ( mAddNormals )
477490
mData << pNormal.x() << pNormal.z() << - pNormal.y();
478491
}
492+
493+
if ( mAddBackFaces )
494+
{
495+
// the same triangle with reversed order of coordinates and inverted normal
496+
for ( int j = 2; j >= 0; --j )
497+
{
498+
p2t::Point *p = t->GetPoint( j );
499+
QVector4D pt( p->x, p->y, z[p], 0 );
500+
if ( toOldBase )
501+
pt = *toOldBase * pt;
502+
const double fx = pt.x() - mOriginX + pt0.x();
503+
const double fy = pt.y() - mOriginY + pt0.y();
504+
const double fz = pt.z() + extrusionHeight + pt0.z();
505+
mData << fx << fz << -fy;
506+
if ( mAddNormals )
507+
mData << -pNormal.x() << -pNormal.z() << pNormal.y();
508+
}
509+
}
479510
}
480511
}
481512
catch ( ... )

‎src/3d/qgstessellator.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class _3D_EXPORT QgsTessellator
3939
{
4040
public:
4141
//! Creates tessellator with a specified origin point of the world (in map coordinates)
42-
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false );
42+
QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false );
4343

4444
//! Tessellates a triangle and adds its vertex entries to the output data array
4545
void addPolygon( const QgsPolygon &polygon, float extrusionHeight );
@@ -58,6 +58,7 @@ class _3D_EXPORT QgsTessellator
5858
double mOriginX, mOriginY;
5959
bool mAddNormals;
6060
bool mInvertNormals;
61+
bool mAddBackFaces;
6162
QVector<float> mData;
6263
int mStride;
6364
};

‎src/3d/symbols/qgspolygon3dsymbol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void QgsPolygon3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext
3333
elemDataProperties.setAttribute( QStringLiteral( "extrusion-height" ), mExtrusionHeight );
3434
elemDataProperties.setAttribute( QStringLiteral( "culling-mode" ), Qgs3DUtils::cullingModeToString( mCullingMode ) );
3535
elemDataProperties.setAttribute( QStringLiteral( "invert-normals" ), mInvertNormals ? "1" : "0" );
36+
elemDataProperties.setAttribute( QStringLiteral( "add-back-faces" ), mAddBackFaces ? "1" : "0" );
3637
elem.appendChild( elemDataProperties );
3738

3839
QDomElement elemMaterial = doc.createElement( QStringLiteral( "material" ) );
@@ -55,6 +56,7 @@ void QgsPolygon3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteCon
5556
mExtrusionHeight = elemDataProperties.attribute( QStringLiteral( "extrusion-height" ) ).toFloat();
5657
mCullingMode = Qgs3DUtils::cullingModeFromString( elemDataProperties.attribute( QStringLiteral( "culling-mode" ) ) );
5758
mInvertNormals = elemDataProperties.attribute( QStringLiteral( "invert-normals" ) ).toInt();
59+
mAddBackFaces = elemDataProperties.attribute( QStringLiteral( "add-back-faces" ) ).toInt();
5860

5961
QDomElement elemMaterial = elem.firstChildElement( QStringLiteral( "material" ) );
6062
mMaterial.readXml( elemMaterial );

‎src/3d/symbols/qgspolygon3dsymbol.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
7676
//! Sets whether the normals of triangles will be inverted (useful for fixing clockwise / counter-clockwise face vertex orders)
7777
void setInvertNormals( bool invert ) { mInvertNormals = invert; }
7878

79+
/**
80+
* Returns whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
81+
* \since QGIS 3.2
82+
*/
83+
bool addBackFaces() const { return mAddBackFaces; }
84+
85+
/**
86+
* Sets whether also triangles facing the other side will be created. Useful if input data have inconsistent order of vertices
87+
* \since QGIS 3.2
88+
*/
89+
void setAddBackFaces( bool add ) { mAddBackFaces = add; }
90+
7991
private:
8092
//! how to handle altitude of vector features
8193
AltitudeClamping mAltClamping = AltClampRelative;
@@ -87,6 +99,7 @@ class _3D_EXPORT QgsPolygon3DSymbol : public QgsAbstract3DSymbol
8799
QgsPhongMaterialSettings mMaterial; //!< Defines appearance of objects
88100
Qt3DRender::QCullFace::CullingMode mCullingMode = Qt3DRender::QCullFace::NoCulling; //!< Front/back culling mode
89101
bool mInvertNormals = false;
102+
bool mAddBackFaces = false;
90103
};
91104

92105

‎src/3d/symbols/qgspolygon3dsymbol_p.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
200200

201201
mGeometry = new QgsTessellatedPolygonGeometry;
202202
mGeometry->setInvertNormals( symbol.invertNormals() );
203+
mGeometry->setAddBackFaces( symbol.addBackFaces() );
203204
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );
204205

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

‎src/app/3d/qgspolygon3dsymbolwidget.cpp

Lines changed: 3 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( chkAddBackFaces, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
3536
connect( chkInvertNormals, &QCheckBox::clicked, this, &QgsPolygon3DSymbolWidget::changed );
3637
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPolygon3DSymbolWidget::changed );
3738
connect( btnHeightDD, &QgsPropertyOverrideButton::changed, this, &QgsPolygon3DSymbolWidget::changed );
@@ -71,6 +72,7 @@ void QgsPolygon3DSymbolWidget::setSymbol( const QgsPolygon3DSymbol &symbol, QgsV
7172
cboAltClamping->setCurrentIndex( ( int ) symbol.altitudeClamping() );
7273
cboAltBinding->setCurrentIndex( ( int ) symbol.altitudeBinding() );
7374
cboCullingMode->setCurrentIndex( _cullingModeToIndex( symbol.cullingMode() ) );
75+
chkAddBackFaces->setChecked( symbol.addBackFaces() );
7476
chkInvertNormals->setChecked( symbol.invertNormals() );
7577
widgetMaterial->setMaterial( symbol.material() );
7678

@@ -86,6 +88,7 @@ QgsPolygon3DSymbol QgsPolygon3DSymbolWidget::symbol() const
8688
sym.setAltitudeClamping( ( AltitudeClamping ) cboAltClamping->currentIndex() );
8789
sym.setAltitudeBinding( ( AltitudeBinding ) cboAltBinding->currentIndex() );
8890
sym.setCullingMode( _cullingModeFromIndex( cboCullingMode->currentIndex() ) );
91+
sym.setAddBackFaces( chkAddBackFaces->isChecked() );
8992
sym.setInvertNormals( chkInvertNormals->isChecked() );
9093
sym.setMaterial( widgetMaterial->material() );
9194

‎src/ui/3d/polygon3dsymbolwidget.ui

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,58 @@
1414
<string>Form</string>
1515
</property>
1616
<layout class="QGridLayout" name="gridLayout">
17-
<item row="4" column="0">
18-
<widget class="QLabel" name="label_5">
17+
<item row="6" column="0" colspan="2">
18+
<widget class="QCheckBox" name="chkInvertNormals">
1919
<property name="text">
20-
<string>Culling Mode</string>
20+
<string>Invert Normals (Experimental)</string>
2121
</property>
2222
</widget>
2323
</item>
24-
<item row="4" column="1">
25-
<widget class="QComboBox" name="cboCullingMode">
24+
<item row="0" column="2">
25+
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
26+
<property name="text">
27+
<string>...</string>
28+
</property>
29+
</widget>
30+
</item>
31+
<item row="3" column="0">
32+
<widget class="QLabel" name="label_4">
33+
<property name="text">
34+
<string>Altitude Binding</string>
35+
</property>
36+
</widget>
37+
</item>
38+
<item row="3" column="1">
39+
<widget class="QComboBox" name="cboAltBinding">
2640
<item>
2741
<property name="text">
28-
<string>No culling</string>
42+
<string>Vertex</string>
2943
</property>
3044
</item>
3145
<item>
3246
<property name="text">
33-
<string>Front</string>
47+
<string>Centroid</string>
3448
</property>
3549
</item>
50+
</widget>
51+
</item>
52+
<item row="2" column="1">
53+
<widget class="QComboBox" name="cboAltClamping">
3654
<item>
3755
<property name="text">
38-
<string>Back</string>
56+
<string>Absolute</string>
57+
</property>
58+
</item>
59+
<item>
60+
<property name="text">
61+
<string>Relative</string>
62+
</property>
63+
</item>
64+
<item>
65+
<property name="text">
66+
<string>Terrain</string>
3967
</property>
4068
</item>
41-
</widget>
42-
</item>
43-
<item row="0" column="2">
44-
<widget class="QgsPropertyOverrideButton" name="btnHeightDD">
45-
<property name="text">
46-
<string>...</string>
47-
</property>
4869
</widget>
4970
</item>
5071
<item row="0" column="0">
@@ -54,18 +75,8 @@
5475
</property>
5576
</widget>
5677
</item>
57-
<item row="2" column="0">
58-
<widget class="QLabel" name="label_3">
59-
<property name="text">
60-
<string>Altitude Clamping</string>
61-
</property>
62-
</widget>
63-
</item>
64-
<item row="0" column="1">
65-
<widget class="QgsDoubleSpinBox" name="spinHeight">
66-
<property name="minimum">
67-
<double>-99999.000000000000000</double>
68-
</property>
78+
<item row="1" column="1">
79+
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
6980
<property name="maximum">
7081
<double>99999.000000000000000</double>
7182
</property>
@@ -78,74 +89,70 @@
7889
</property>
7990
</widget>
8091
</item>
92+
<item row="2" column="0">
93+
<widget class="QLabel" name="label_3">
94+
<property name="text">
95+
<string>Altitude Clamping</string>
96+
</property>
97+
</widget>
98+
</item>
8199
<item row="1" column="2">
82100
<widget class="QgsPropertyOverrideButton" name="btnExtrusionDD">
83101
<property name="text">
84102
<string>...</string>
85103
</property>
86104
</widget>
87105
</item>
88-
<item row="2" column="1">
89-
<widget class="QComboBox" name="cboAltClamping">
106+
<item row="4" column="1">
107+
<widget class="QComboBox" name="cboCullingMode">
90108
<item>
91109
<property name="text">
92-
<string>Absolute</string>
110+
<string>No culling</string>
93111
</property>
94112
</item>
95113
<item>
96114
<property name="text">
97-
<string>Relative</string>
115+
<string>Front</string>
98116
</property>
99117
</item>
100118
<item>
101119
<property name="text">
102-
<string>Terrain</string>
120+
<string>Back</string>
103121
</property>
104122
</item>
105123
</widget>
106124
</item>
107-
<item row="3" column="0">
108-
<widget class="QLabel" name="label_4">
125+
<item row="4" column="0">
126+
<widget class="QLabel" name="label_5">
109127
<property name="text">
110-
<string>Altitude Binding</string>
128+
<string>Culling Mode</string>
111129
</property>
112130
</widget>
113131
</item>
114-
<item row="1" column="1">
115-
<widget class="QgsDoubleSpinBox" name="spinExtrusion">
116-
<property name="maximum">
117-
<double>99999.000000000000000</double>
132+
<item row="7" column="0" colspan="3">
133+
<widget class="Line" name="line">
134+
<property name="orientation">
135+
<enum>Qt::Horizontal</enum>
118136
</property>
119137
</widget>
120138
</item>
121-
<item row="3" column="1">
122-
<widget class="QComboBox" name="cboAltBinding">
123-
<item>
124-
<property name="text">
125-
<string>Vertex</string>
126-
</property>
127-
</item>
128-
<item>
129-
<property name="text">
130-
<string>Centroid</string>
131-
</property>
132-
</item>
133-
</widget>
134-
</item>
135-
<item row="7" column="0" colspan="3">
139+
<item row="8" column="0" colspan="3">
136140
<widget class="QgsPhongMaterialWidget" name="widgetMaterial" native="true"/>
137141
</item>
138-
<item row="6" column="0" colspan="3">
139-
<widget class="Line" name="line">
140-
<property name="orientation">
141-
<enum>Qt::Horizontal</enum>
142+
<item row="0" column="1">
143+
<widget class="QgsDoubleSpinBox" name="spinHeight">
144+
<property name="minimum">
145+
<double>-99999.000000000000000</double>
146+
</property>
147+
<property name="maximum">
148+
<double>99999.000000000000000</double>
142149
</property>
143150
</widget>
144151
</item>
145-
<item row="5" column="0" colspan="2">
146-
<widget class="QCheckBox" name="chkInvertNormals">
152+
<item row="5" column="0">
153+
<widget class="QCheckBox" name="chkAddBackFaces">
147154
<property name="text">
148-
<string>Invert Normals (Experimental)</string>
155+
<string>Add back faces</string>
149156
</property>
150157
</widget>
151158
</item>
@@ -176,6 +183,9 @@
176183
<tabstop>btnExtrusionDD</tabstop>
177184
<tabstop>cboAltClamping</tabstop>
178185
<tabstop>cboAltBinding</tabstop>
186+
<tabstop>cboCullingMode</tabstop>
187+
<tabstop>chkAddBackFaces</tabstop>
188+
<tabstop>chkInvertNormals</tabstop>
179189
</tabstops>
180190
<resources/>
181191
<connections/>

‎tests/src/3d/testqgstessellator.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class TestQgsTessellator : public QObject
129129

130130
void testBasic();
131131
void testWalls();
132+
void testBackEdges();
132133
void asMultiPolygon();
133134
void testBadCoordinates();
134135
void testIssue17745();
@@ -245,6 +246,24 @@ void TestQgsTessellator::testWalls()
245246
QVERIFY( checkTriangleOutput( tZ.data(), false, tc ) );
246247
}
247248

249+
void TestQgsTessellator::testBackEdges()
250+
{
251+
QgsPolygon polygon;
252+
polygon.fromWkt( "POLYGON((1 1, 2 1, 3 2, 1 2, 1 1))" );
253+
254+
QVector3D up( 0, 0, 1 ); // surface normal pointing straight up
255+
QVector3D dn( 0, 0, -1 ); // surface normal pointing straight down for back faces
256+
QList<TriangleCoords> tcNormals;
257+
tcNormals << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ), up, up, up );
258+
tcNormals << TriangleCoords( QVector3D( 3, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 1, 2, 0 ), dn, dn, dn );
259+
tcNormals << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ), up, up, up );
260+
tcNormals << TriangleCoords( QVector3D( 2, 1, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 1, 2, 0 ), dn, dn, dn );
261+
262+
QgsTessellator tN( 0, 0, true, false, true );
263+
tN.addPolygon( polygon, 0 );
264+
QVERIFY( checkTriangleOutput( tN.data(), true, tcNormals ) );
265+
}
266+
248267
void TestQgsTessellator::asMultiPolygon()
249268
{
250269
QgsPolygon polygon;

0 commit comments

Comments
 (0)
Please sign in to comment.