Skip to content

Commit 006352f

Browse files
committed
More tessellation fixes and more unit tests
1 parent 8412a87 commit 006352f

File tree

2 files changed

+113
-38
lines changed

2 files changed

+113
-38
lines changed

src/3d/qgstessellator.cpp

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -111,37 +111,60 @@ static void _makeWalls( const QgsCurve &ring, bool ccw, float extrusionHeight, Q
111111
}
112112
}
113113

114-
static QVector3D _calculateNormal( const QgsCurve *curve, bool &hasValidZ )
114+
static QVector3D _calculateNormal( const QgsCurve *curve, double originX, double originY )
115115
{
116-
// Calculate the polygon's normal vector, based on Newell's method
117-
// https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
118116
QgsVertexId::VertexType vt;
119117
QgsPoint pt1, pt2;
120-
QVector3D normal( 0, 0, 0 );
121118

122-
// assume that Z coordinates are not present
123-
hasValidZ = false;
119+
// if it is just plain 2D curve there is no need to calculate anything
120+
// because it will be a flat horizontally oriented patch
121+
if ( !QgsWkbTypes::hasZ( curve->wkbType() ) )
122+
return QVector3D( 0, 0, 1 );
123+
124+
// often we have 3D coordinates, but Z is the same for all vertices
125+
// so in order to save calculation and avoid possible issues with order of vertices
126+
// (the calculation below may decide that a polygon faces downwards)
127+
bool sameZ = true;
128+
curve->pointAt( 0, pt1, vt );
129+
for ( int i = 1; i < curve->numPoints(); i++ )
130+
{
131+
curve->pointAt( i, pt2, vt );
132+
if ( pt1.z() != pt2.z() )
133+
{
134+
sameZ = false;
135+
break;
136+
}
137+
}
138+
if ( sameZ )
139+
return QVector3D( 0, 0, 1 );
124140

141+
// Calculate the polygon's normal vector, based on Newell's method
142+
// https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
143+
//
144+
// Order of vertices is important here as it determines the front/back face of the polygon
145+
146+
double nx = 0, ny = 0, nz = 0;
125147
for ( int i = 0; i < curve->numPoints() - 1; i++ )
126148
{
127149
curve->pointAt( i, pt1, vt );
128150
curve->pointAt( i + 1, pt2, vt );
129151

152+
// shift points by the tessellator's origin - this does not affect normal calculation and it may save us from losing some precision
153+
pt1.setX( pt1.x() - originX );
154+
pt1.setY( pt1.y() - originY );
155+
pt2.setX( pt2.x() - originX );
156+
pt2.setY( pt2.y() - originY );
157+
130158
if ( std::isnan( pt1.z() ) || std::isnan( pt2.z() ) )
131159
continue;
132160

133-
hasValidZ = true;
134-
135-
normal.setX( normal.x() + ( pt1.y() - pt2.y() ) * ( pt1.z() + pt2.z() ) );
136-
normal.setY( normal.y() + ( pt1.z() - pt2.z() ) * ( pt1.x() + pt2.x() ) );
137-
normal.setZ( normal.z() + ( pt1.x() - pt2.x() ) * ( pt1.y() + pt2.y() ) );
161+
nx += ( pt1.y() - pt2.y() ) * ( pt1.z() + pt2.z() );
162+
ny += ( pt1.z() - pt2.z() ) * ( pt1.x() + pt2.x() );
163+
nz += ( pt1.x() - pt2.x() ) * ( pt1.y() + pt2.y() );
138164
}
139165

140-
if ( !hasValidZ )
141-
return QVector3D( 0, 0, 1 );
142-
166+
QVector3D normal( nx, ny, nz );
143167
normal.normalize();
144-
145168
return normal;
146169
}
147170

@@ -194,8 +217,7 @@ void QgsTessellator::addPolygon( const QgsPolygonV2 &polygon, float extrusionHei
194217
QgsVertexId::VertexType vt;
195218
QgsPoint pt;
196219

197-
bool hasValidZ;
198-
const QVector3D pNormal = _calculateNormal( exterior, hasValidZ );
220+
const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY );
199221
const int pCount = exterior->numPoints();
200222

201223
// Polygon is a triangle

tests/src/3d/testqgstessellator.cpp

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,16 @@ struct TriangleCoords
3838
//! Constructs from tessellator output. Note: tessellator outputs (X,-Z,Y) tuples for (X,Y,Z) input coords
3939
TriangleCoords( const float *data, bool withNormal )
4040
{
41-
pts[0] = QVector3D( data[0], -data[2], data[1] );
42-
pts[1] = QVector3D( data[3], -data[5], data[4] );
43-
pts[2] = QVector3D( data[6], -data[8], data[7] );
44-
if ( withNormal )
45-
{
46-
data += 9;
47-
normals[0] = QVector3D( data[0], -data[2], data[1] );
48-
normals[1] = QVector3D( data[3], -data[5], data[4] );
49-
normals[2] = QVector3D( data[6], -data[8], data[7] );
50-
}
51-
else
52-
{
53-
normals[0] = normals[1] = normals[2] = QVector3D();
54-
}
41+
#define FLOAT3_TO_VECTOR(x) QVector3D( data[0], -data[2], data[1] )
42+
43+
pts[0] = FLOAT3_TO_VECTOR( data ); data += 3;
44+
if ( withNormal ) { normals[0] = FLOAT3_TO_VECTOR( data ); data += 3; }
45+
46+
pts[1] = FLOAT3_TO_VECTOR( data ); data += 3;
47+
if ( withNormal ) { normals[1] = FLOAT3_TO_VECTOR( data ); data += 3; }
48+
49+
pts[2] = FLOAT3_TO_VECTOR( data ); data += 3;
50+
if ( withNormal ) { normals[2] = FLOAT3_TO_VECTOR( data ); data += 3; }
5551
}
5652

5753
//! Compares two triangles
@@ -62,10 +58,47 @@ struct TriangleCoords
6258
normals[0] == other.normals[0] && normals[1] == other.normals[1] && normals[2] == other.normals[2];
6359
}
6460

61+
bool operator!=( const TriangleCoords &other ) const
62+
{
63+
return !operator==( other );
64+
}
65+
66+
void dump() const
67+
{
68+
qDebug() << pts[0] << pts[1] << pts[2] << normals[0] << normals[1] << normals[2];
69+
}
70+
6571
QVector3D pts[3];
6672
QVector3D normals[3];
6773
};
6874

75+
76+
bool checkTriangleOutput( const QVector<float> &data, bool withNormals, const QList<TriangleCoords> &expected )
77+
{
78+
int valuesPerTriangle = withNormals ? 18 : 9;
79+
if ( data.count() != expected.count() * valuesPerTriangle )
80+
return false;
81+
82+
// TODO: allow arbitrary order of triangles in output
83+
const float *dataRaw = data.constData();
84+
for ( int i = 0; i < expected.count(); ++i )
85+
{
86+
const TriangleCoords &exp = expected.at( i );
87+
TriangleCoords out( dataRaw, withNormals );
88+
if ( exp != out )
89+
{
90+
qDebug() << "expected:";
91+
exp.dump();
92+
qDebug() << "got:";
93+
out.dump();
94+
return false;
95+
}
96+
dataRaw += withNormals ? 18 : 9;
97+
}
98+
return true;
99+
}
100+
101+
69102
/**
70103
* \ingroup UnitTests
71104
* This is a unit test for the node tool
@@ -103,17 +136,37 @@ void TestQgsTessellator::testBasic()
103136
QgsPolygonV2 polygon;
104137
polygon.fromWkt( "POLYGON((1 1, 2 1, 3 2, 1 2, 1 1))" );
105138

139+
QgsPolygonV2 polygonZ;
140+
polygonZ.fromWkt( "POLYGONZ((1 1 0, 2 1 0, 3 2 0, 1 2 0, 1 1 0))" );
141+
142+
QList<TriangleCoords> tc;
143+
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ) );
144+
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ) );
145+
146+
QVector3D up( 0, 0, 1 ); // surface normal pointing straight up
147+
QList<TriangleCoords> tcNormals;
148+
tcNormals << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ), up, up, up );
149+
tcNormals << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ), up, up, up );
150+
151+
// without normals
152+
106153
QgsTessellator t( 0, 0, false );
107154
t.addPolygon( polygon, 0 );
155+
QVERIFY( checkTriangleOutput( t.data(), false, tc ) );
108156

109-
TriangleCoords tcA( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ) );
110-
TriangleCoords tcB( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ) );
157+
QgsTessellator tZ( 0, 0, false );
158+
tZ.addPolygon( polygonZ, 0 );
159+
QVERIFY( checkTriangleOutput( tZ.data(), false, tc ) );
111160

112-
QVector<float> polygonData = t.data();
113-
QCOMPARE( polygonData.count(), 2 * 3 * 3 ); // two triangles (3 points with x/y/z coords)
114-
// TODO: allow arbitrary order of triangles in output
115-
QVERIFY( tcA == TriangleCoords( polygonData.constData(), false ) );
116-
QVERIFY( tcB == TriangleCoords( polygonData.constData() + 9, false ) );
161+
// with normals
162+
163+
QgsTessellator tN( 0, 0, true );
164+
tN.addPolygon( polygon, 0 );
165+
QVERIFY( checkTriangleOutput( tN.data(), true, tcNormals ) );
166+
167+
QgsTessellator tNZ( 0, 0, true );
168+
tNZ.addPolygon( polygonZ, 0 );
169+
QVERIFY( checkTriangleOutput( tNZ.data(), true, tcNormals ) );
117170
}
118171

119172

0 commit comments

Comments
 (0)