Skip to content

Commit 0a8ea96

Browse files
authored
Merge pull request #883 from zeux/sphere7
clusterizer: Improve precision of bounding spheres
2 parents b2b3f59 + 0ec5678 commit 0a8ea96

File tree

2 files changed

+45
-25
lines changed

2 files changed

+45
-25
lines changed

demo/tests.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,18 +1146,12 @@ static void sphereBounds()
11461146
1, 0, 1, 3, // clang-format
11471147
};
11481148

1149-
// without the radius, the center is somewhere inside the tetrahedron
1150-
// note that we currently compute a somewhat suboptimal sphere here due to the tetrahedron being perfecly axis aligned
1149+
// without the radius, the center is inside the tetrahedron
11511150
meshopt_Bounds bounds = meshopt_computeSphereBounds(vbr, 4, sizeof(float) * 4, NULL, 0);
1152-
assert(bounds.radius < 0.97f);
1153-
1154-
// forcing a better initial guess for the center by using different radii produces a close to optimal sphere
1155-
float eps[4] = {1e-3f, 2e-3f, 3e-3f, 4e-3f};
1156-
meshopt_Bounds boundse = meshopt_computeSphereBounds(vbr, 4, sizeof(float) * 4, eps, sizeof(float));
1157-
assert(fabsf(boundse.center[0] - 0.5f) < 1e-2f);
1158-
assert(fabsf(boundse.center[1] - 0.5f) < 1e-2f);
1159-
assert(fabsf(boundse.center[2] - 0.5f) < 1e-2f);
1160-
assert(boundse.radius < 0.87f);
1151+
assert(fabsf(bounds.center[0] - 0.5f) < 1e-2f);
1152+
assert(fabsf(bounds.center[1] - 0.5f) < 1e-2f);
1153+
assert(fabsf(bounds.center[2] - 0.5f) < 1e-2f);
1154+
assert(bounds.radius < 0.87f);
11611155

11621156
// when using the radius, the last sphere envelops the entire set
11631157
meshopt_Bounds boundsr = meshopt_computeSphereBounds(vbr, 4, sizeof(float) * 4, vbr + 3, sizeof(float) * 4);

src/clusterizer.cpp

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// Graham Wihlidal. Optimizing the Graphics Pipeline with Compute. 2016
1111
// Matthaeus Chajdas. GeometryFX 1.2 - Cluster Culling. 2016
1212
// Jack Ritter. An Efficient Bounding Sphere. 1990
13+
// Thomas Larsson. Fast and Tight Fitting Bounding Spheres. 2008
1314
namespace meshopt
1415
{
1516

@@ -147,37 +148,62 @@ static void buildTriangleAdjacencySparse(TriangleAdjacency2& adjacency, const un
147148
}
148149
}
149150

150-
static void computeBoundingSphere(float result[4], const float* points, size_t count, size_t points_stride, const float* radii, size_t radii_stride)
151+
static void computeBoundingSphere(float result[4], const float* points, size_t count, size_t points_stride, const float* radii, size_t radii_stride, size_t axis_count)
151152
{
153+
static const float kAxes[7][3] = {
154+
// X, Y, Z
155+
{1, 0, 0},
156+
{0, 1, 0},
157+
{0, 0, 1},
158+
159+
// XYZ, -XYZ, X-YZ, XY-Z; normalized to unit length
160+
{0.57735026f, 0.57735026f, 0.57735026f},
161+
{-0.57735026f, 0.57735026f, 0.57735026f},
162+
{0.57735026f, -0.57735026f, 0.57735026f},
163+
{0.57735026f, 0.57735026f, -0.57735026f},
164+
};
165+
152166
assert(count > 0);
167+
assert(axis_count <= sizeof(kAxes) / sizeof(kAxes[0]));
153168

154169
size_t points_stride_float = points_stride / sizeof(float);
155170
size_t radii_stride_float = radii_stride / sizeof(float);
156171

157-
// find extremum points along all 3 axes; for each axis we get a pair of points with min/max coordinates
158-
size_t pmin[3] = {0, 0, 0};
159-
size_t pmax[3] = {0, 0, 0};
172+
// find extremum points along all axes; for each axis we get a pair of points with min/max coordinates
173+
size_t pmin[7], pmax[7];
174+
float tmin[7], tmax[7];
175+
176+
for (size_t axis = 0; axis < axis_count; ++axis)
177+
{
178+
pmin[axis] = pmax[axis] = 0;
179+
tmin[axis] = FLT_MAX;
180+
tmax[axis] = -FLT_MAX;
181+
}
160182

161183
for (size_t i = 0; i < count; ++i)
162184
{
163185
const float* p = points + i * points_stride_float;
164186
float r = radii[i * radii_stride_float];
165187

166-
for (int axis = 0; axis < 3; ++axis)
188+
for (size_t axis = 0; axis < axis_count; ++axis)
167189
{
168-
float bmin = points[pmin[axis] * points_stride_float + axis] - radii[pmin[axis] * radii_stride_float];
169-
float bmax = points[pmax[axis] * points_stride_float + axis] + radii[pmax[axis] * radii_stride_float];
190+
const float* ax = kAxes[axis];
191+
192+
float tp = ax[0] * p[0] + ax[1] * p[1] + ax[2] * p[2];
193+
float tpmin = tp - r, tpmax = tp + r;
170194

171-
pmin[axis] = (p[axis] - r < bmin) ? i : pmin[axis];
172-
pmax[axis] = (p[axis] + r > bmax) ? i : pmax[axis];
195+
pmin[axis] = (tpmin < tmin[axis]) ? i : pmin[axis];
196+
pmax[axis] = (tpmax > tmax[axis]) ? i : pmax[axis];
197+
tmin[axis] = (tpmin < tmin[axis]) ? tpmin : tmin[axis];
198+
tmax[axis] = (tpmax > tmax[axis]) ? tpmax : tmax[axis];
173199
}
174200
}
175201

176202
// find the pair of points with largest distance
177-
int paxis = 0;
203+
size_t paxis = 0;
178204
float paxisdr = 0;
179205

180-
for (int axis = 0; axis < 3; ++axis)
206+
for (size_t axis = 0; axis < axis_count; ++axis)
181207
{
182208
const float* p1 = points + pmin[axis] * points_stride_float;
183209
const float* p2 = points + pmax[axis] * points_stride_float;
@@ -1435,13 +1461,13 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t
14351461

14361462
// compute cluster bounding sphere; we'll use the center to determine normal cone apex as well
14371463
float psphere[4] = {};
1438-
computeBoundingSphere(psphere, corners[0][0], triangles * 3, sizeof(float) * 3, &rzero, 0);
1464+
computeBoundingSphere(psphere, corners[0][0], triangles * 3, sizeof(float) * 3, &rzero, 0, 7);
14391465

14401466
float center[3] = {psphere[0], psphere[1], psphere[2]};
14411467

14421468
// treating triangle normals as points, find the bounding sphere - the sphere center determines the optimal cone axis
14431469
float nsphere[4] = {};
1444-
computeBoundingSphere(nsphere, normals[0], triangles, sizeof(float) * 3, &rzero, 0);
1470+
computeBoundingSphere(nsphere, normals[0], triangles, sizeof(float) * 3, &rzero, 0, 3);
14451471

14461472
float axis[3] = {nsphere[0], nsphere[1], nsphere[2]};
14471473
float axislength = sqrtf(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
@@ -1568,7 +1594,7 @@ meshopt_Bounds meshopt_computeSphereBounds(const float* positions, size_t count,
15681594
const float rzero = 0.f;
15691595

15701596
float psphere[4] = {};
1571-
computeBoundingSphere(psphere, positions, count, positions_stride, radii ? radii : &rzero, radii ? radii_stride : 0);
1597+
computeBoundingSphere(psphere, positions, count, positions_stride, radii ? radii : &rzero, radii ? radii_stride : 0, 7);
15721598

15731599
bounds.center[0] = psphere[0];
15741600
bounds.center[1] = psphere[1];

0 commit comments

Comments
 (0)