Skip to content

Commit f1962d1

Browse files
committed
fix mesh chunking making holes on flat meshes
1 parent 532f9ae commit f1962d1

File tree

2 files changed

+135
-2
lines changed

2 files changed

+135
-2
lines changed

sources/libcore/mesh/meshAlgorithms.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,14 +1597,16 @@ namespace cage
15971597

15981598
Aabb clippingBox(const Aabb &box, uint32 axis, Real position, bool second)
15991599
{
1600+
CAGE_ASSERT(position == saturate(position));
16001601
const Vec3 c = box.center();
1601-
const Vec3 hs = box.size() * 0.6; // slightly larger box to avoid clipping due to floating point imprecisions
1602+
const Vec3 hs = box.size() + 1; // slightly larger box to avoid clipping due to floating point imprecisions
16021603
Aabb r = Aabb(c - hs, c + hs);
1603-
const Real s = interpolate(r.a[axis], r.b[axis], position);
1604+
const Real s = interpolate(box.a[axis], box.b[axis], position);
16041605
if (second)
16051606
r.a[axis] = s;
16061607
else
16071608
r.b[axis] = s;
1609+
CAGE_ASSERT(r.valid());
16081610
return r;
16091611
}
16101612

sources/test-core/mesh.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <algorithm>
2+
#include <array>
23
#include <vector>
34

45
#include "main.h"
@@ -190,6 +191,64 @@ namespace
190191
return msh;
191192
}
192193

194+
Holder<Mesh> makeLargeGrid()
195+
{
196+
std::vector<Vec3> p;
197+
p.reserve(1000);
198+
for (uint32 y = 0; y < 101; y++)
199+
for (uint32 x = 0; x < 101; x++)
200+
p.push_back(Vec3(Real(x) - 50, Real(y) - 50, 0));
201+
std::vector<uint32> is;
202+
is.reserve(1000);
203+
for (uint32 y = 0; y < 100; y++)
204+
{
205+
for (uint32 x = 0; x < 100; x++)
206+
{
207+
const uint32 a = y * 101 + x;
208+
static constexpr uint32 o[6] = { 0, 1, 101, 1, 102, 101 };
209+
for (uint32 b : o)
210+
is.push_back(a + b);
211+
}
212+
}
213+
Holder<Mesh> msh = newMesh();
214+
msh->positions(p);
215+
msh->indices(is);
216+
return msh;
217+
}
218+
219+
Holder<Mesh> makeCube()
220+
{
221+
static constexpr Vec3 verts[] = {
222+
Vec3(-1, -1, -1), // 0
223+
Vec3(1, -1, -1), // 1
224+
Vec3(1, 1, -1), // 2
225+
Vec3(-1, 1, -1), // 3
226+
Vec3(-1, -1, 1), // 4
227+
Vec3(1, -1, 1), // 5
228+
Vec3(1, 1, 1), // 6
229+
Vec3(-1, 1, 1) // 7
230+
};
231+
232+
static constexpr uint32 faces[] = { // Back face
233+
0, 1, 2, 2, 3, 0,
234+
// Front face
235+
4, 5, 6, 6, 7, 4,
236+
// Left face
237+
0, 3, 7, 7, 4, 0,
238+
// Right face
239+
1, 5, 6, 6, 2, 1,
240+
// Bottom face
241+
0, 1, 5, 5, 4, 0,
242+
// Top face
243+
3, 2, 6, 6, 7, 3
244+
};
245+
246+
Holder<Mesh> msh = newMesh();
247+
msh->positions(verts);
248+
msh->indices(faces);
249+
return msh;
250+
}
251+
193252
void testMeshBasics()
194253
{
195254
const Holder<const Mesh> msh = makeSphere();
@@ -995,6 +1054,76 @@ namespace
9951054
CAGE_TEST(msh->indicesCount() == cnt);
9961055
meshSimplify(+msh, {}); // make sure that the resulting mesh has usable topology
9971056
}
1057+
1058+
void testMeshClipping()
1059+
{
1060+
CAGE_TESTCASE("mesh clipping");
1061+
1062+
for (Real offset : { Real(-2), Real(-1.5), Real(0), Real(0.5), Real(1.33) })
1063+
{
1064+
Holder<Mesh> grid = makeGrid();
1065+
meshApplyTransform(+grid, Transform(Vec3(), Quat(), 0.5));
1066+
Holder<Mesh> box = makeCube();
1067+
meshApplyTransform(+box, Transform(Vec3(offset, 0, 0), Quat(), 1));
1068+
Aabb aabb = box->boundingBox();
1069+
meshClip(+grid, aabb);
1070+
1071+
{ // merge the box with the grid for better visual check
1072+
const auto inds = box->indices();
1073+
const auto poss = box->positions();
1074+
const uint32 tris = numeric_cast<uint32>(inds.size() / 3);
1075+
for (uint32 tri = 0; tri < tris; tri++)
1076+
{
1077+
const uint32 *ids = inds.data() + tri * 3;
1078+
const Vec3 &a = poss[ids[0]];
1079+
const Vec3 &b = poss[ids[1]];
1080+
const Vec3 &c = poss[ids[2]];
1081+
const Triangle t = Triangle(a, b, c);
1082+
grid->addTriangle(t);
1083+
}
1084+
}
1085+
grid->exportFile(Stringizer() + "meshes/algorithms/testClipping/" + offset + ".obj");
1086+
}
1087+
}
1088+
1089+
Real meshSurfaceArea(const Mesh *mesh)
1090+
{
1091+
const auto inds = mesh->indices();
1092+
const auto poss = mesh->positions();
1093+
const uint32 cnt = numeric_cast<uint32>(inds.size() / 3);
1094+
Real result = 0;
1095+
for (uint32 ti = 0; ti < cnt; ti++)
1096+
{
1097+
const Triangle t = Triangle(poss[inds[ti * 3 + 0]], poss[inds[ti * 3 + 1]], poss[inds[ti * 3 + 2]]);
1098+
result += t.area();
1099+
}
1100+
return result;
1101+
}
1102+
1103+
void testMeshChunking()
1104+
{
1105+
CAGE_TESTCASE("mesh chunking");
1106+
1107+
Holder<Mesh> grid = makeLargeGrid();
1108+
const Aabb initialBox = grid->boundingBox();
1109+
approxEqual(initialBox, Aabb(Vec3(-50, -50, 0), Vec3(50, 50, 0)));
1110+
const Real initialArea = meshSurfaceArea(+grid);
1111+
CAGE_TEST(abs(initialArea - 10'000) < 1);
1112+
MeshChunkingConfig cfg;
1113+
cfg.maxSurfaceArea = 10'000 / randomRange(10, 30);
1114+
auto r = meshChunking(+grid, cfg);
1115+
Real sum = 0;
1116+
Aabb box;
1117+
uint32 i = 0;
1118+
for (const auto &it : r)
1119+
{
1120+
sum += meshSurfaceArea(+it);
1121+
box += it->boundingBox();
1122+
it->exportFile(Stringizer() + "meshes/algorithms/testChunking/" + (i++) + ".obj");
1123+
}
1124+
CAGE_TEST(abs(sum - initialArea) < 1);
1125+
approxEqual(box, initialBox);
1126+
}
9981127
}
9991128

10001129
void testMesh()
@@ -1007,4 +1136,6 @@ void testMesh()
10071136
testMeshRetexture();
10081137
testMeshLines();
10091138
testMeshConsistentWinding();
1139+
testMeshClipping();
1140+
testMeshChunking();
10101141
}

0 commit comments

Comments
 (0)