15constexpr std::uint32_t kNoParent = 0xFFFFFFFFu;
18 float x = 0.f,
y = 0.f,
z = 0.f;
27 std::vector<std::uint32_t>
verts;
30bool hasVertex(
const Meshlet &
m, std::uint32_t
v) {
31 return std::find(
m.verts.begin(),
m.verts.end(),
v) !=
m.verts.end();
34bool canAdd(
const Meshlet &
m,
const std::uint32_t corner[3],
int maxVerts,
int maxTris) {
35 if (
static_cast<int>(
m.triangles.size() / 3) >= maxTris)
return false;
37 for (
int k = 0; k < 3; ++k)
38 if (!hasVertex(
m, corner[k])) ++newVerts;
39 return static_cast<int>(
m.verts.size()) + newVerts <= maxVerts;
42void addTriangle(Meshlet &
m,
const std::uint32_t corner[3]) {
43 for (
int k = 0; k < 3; ++k)
44 if (!hasVertex(
m, corner[k]))
m.verts.push_back(corner[k]);
45 for (
int k = 0; k < 3; ++k)
m.triangles.push_back(corner[k]);
48void meshletize(
const std::vector<std::uint32_t> &indices,
int vertexCount,
49 const VirtualGeometryBuilder::Options &opt, std::vector<Meshlet> &out) {
50 const int triCount =
static_cast<int>(indices.size() / 3);
51 if (triCount <= 0)
return;
53 std::vector<std::vector<int>> vtxTris(
static_cast<std::size_t
>(vertexCount));
54 for (
int t = 0; t < triCount; ++t)
55 for (
int k = 0; k < 3; ++k) vtxTris[indices[3 * t + k]].push_back(t);
57 std::vector<char> assigned(
static_cast<std::size_t
>(triCount), 0);
60 if (assigned[
seed])
continue;
63 std::queue<int> frontier;
65 const std::uint32_t seedCorner[3] = {indices[3 *
seed], indices[3 *
seed + 1],
66 indices[3 *
seed + 2]};
67 addTriangle(
m, seedCorner);
69 for (
int k = 0; k < 3; ++k)
70 for (
int nb : vtxTris[seedCorner[k]])
71 if (!assigned[nb]) frontier.push(nb);
73 while (!frontier.empty() &&
74 static_cast<int>(
m.triangles.size() / 3) < opt.maxTrianglesPerCluster) {
75 int t = frontier.front();
77 if (assigned[t])
continue;
78 const std::uint32_t corner[3] = {indices[3 * t], indices[3 * t + 1], indices[3 * t + 2]};
79 if (!canAdd(
m, corner, opt.maxVerticesPerCluster, opt.maxTrianglesPerCluster))
continue;
80 addTriangle(
m, corner);
82 for (
int k = 0; k < 3; ++k)
83 for (
int nb : vtxTris[corner[k]])
84 if (!assigned[nb]) frontier.push(nb);
86 out.push_back(std::move(
m));
90void computeSphere(
const std::vector<float> &positions,
const std::vector<std::uint32_t> &
verts,
91 float &
cx,
float &
cy,
float &cz,
float &r) {
93 cx =
cy = cz = r = 0.f;
96 double sx = 0, sy = 0, sz = 0;
97 for (std::uint32_t
v :
verts) {
98 sx += positions[3 *
v];
99 sy += positions[3 *
v + 1];
100 sz += positions[3 *
v + 2];
102 float n =
static_cast<float>(
verts.size());
103 cx =
static_cast<float>(sx) /
n;
104 cy =
static_cast<float>(sy) /
n;
105 cz =
static_cast<float>(sz) /
n;
107 for (std::uint32_t
v :
verts) {
108 float dx = positions[3 *
v] -
cx, dy = positions[3 *
v + 1] -
cy, dz = positions[3 *
v + 2] - cz;
109 r = std::max(r, std::sqrt(dx * dx + dy * dy + dz * dz));
118class QuadricSimplifier {
120 QuadricSimplifier(
const std::vector<float> &positions,
121 const std::vector<std::uint32_t> &inTris,
122 const std::vector<std::uint32_t> &
verts,
123 std::vector<std::uint32_t> &outTris,
float &outError)
124 : srcPos_(positions), outTris_(outTris), outError_(outError), verts_(
verts) {
127 initPos_ = positions;
130 void run(std::size_t targetTriangles) {
132 for (std::uint32_t
v : verts_) alive_[
v] = 1;
134 std::size_t aliveTri = countAliveTriangles();
135 double maxCost = 0.0;
136 const float kInf = std::numeric_limits<float>::infinity();
137 std::unordered_map<std::uint64_t, std::uint8_t> blocked;
139 while (aliveTri > targetTriangles) {
140 float bestCost = kInf;
141 std::uint32_t bestA = 0, bestB = 0;
142 float bestP[3] = {0, 0, 0};
143 for (
const auto &e : edges_) {
144 std::uint32_t
a = e.first,
b = e.second;
145 if (!alive_[
a] || !alive_[
b])
continue;
146 std::uint64_t key = (std::uint64_t(
a) << 32) |
b;
147 if (blocked.count(key))
continue;
150 if (
cost < bestCost) {
159 if (bestCost == kInf)
break;
160 if (!collapse(bestA, bestB, bestP, maxCost)) {
161 blocked[(std::uint64_t(bestA) << 32) | bestB] = 1;
164 aliveTri = countAliveTriangles();
168 for (std::size_t i = 0; i + 2 < tris_.size(); i += 3) {
169 std::uint32_t
a = tris_[i],
b = tris_[i + 1],
c = tris_[i + 2];
170 if (
a !=
b &&
b !=
c &&
a !=
c) {
171 outTris_.push_back(
a);
172 outTris_.push_back(
b);
173 outTris_.push_back(
c);
176 outError_ = std::sqrt(maxCost);
180 const std::vector<float> &srcPos_;
181 std::vector<float> pos_;
182 std::vector<std::uint32_t> verts_;
183 std::vector<std::uint32_t> tris_;
184 std::vector<std::uint32_t> &outTris_;
186 std::uint32_t maxV_ = 0;
187 std::vector<unsigned char> alive_;
188 std::vector<double> quadric_;
189 std::vector<std::vector<std::size_t>> triIncident_;
190 std::vector<std::pair<std::uint32_t, std::uint32_t>> edges_;
191 std::vector<float> initPos_;
194 double evalQuadricAtInit(std::uint32_t
v)
const {
195 const double *Q = &quadric_[
static_cast<std::size_t
>(
v) * 10];
196 double x = initPos_[3 *
v],
y = initPos_[3 *
v + 1],
z = initPos_[3 *
v + 2];
197 return Q[0] *
x *
x + 2 * Q[1] *
x *
y + 2 * Q[2] *
x *
z + 2 * Q[6] *
x +
198 Q[3] *
y *
y + 2 * Q[4] *
y *
z + 2 * Q[7] *
y +
199 Q[5] *
z *
z + 2 * Q[8] *
z + Q[9];
204 for (std::uint32_t
v : verts_) maxV_ = std::max(maxV_,
v);
205 alive_.assign(
static_cast<std::size_t
>(maxV_) + 1, 0);
206 triIncident_.assign(
static_cast<std::size_t
>(maxV_) + 1, {});
207 quadric_.assign(
static_cast<std::size_t
>(maxV_ + 1) * 10, 0.0);
208 std::unordered_map<std::uint64_t, std::uint8_t> seen;
210 for (std::size_t i = 0; i + 2 < tris_.size(); i += 3) {
211 std::uint32_t t[3] = {tris_[i], tris_[i + 1], tris_[i + 2]};
212 Vec3
a{pos_[3 * t[0]], pos_[3 * t[0] + 1], pos_[3 * t[0] + 2]};
213 Vec3
b{pos_[3 * t[1]], pos_[3 * t[1] + 1], pos_[3 * t[1] + 2]};
214 Vec3
c{pos_[3 * t[2]], pos_[3 * t[2] + 1], pos_[3 * t[2] + 2]};
215 Vec3
n{(
b.y -
a.y) * (
c.z -
a.z) - (
b.z -
a.z) * (
c.y -
a.y),
216 (
b.z -
a.z) * (
c.x -
a.x) - (
b.x -
a.x) * (
c.z -
a.z),
217 (
b.x -
a.x) * (
c.y -
a.y) - (
b.y -
a.y) * (
c.x -
a.x)};
218 float nl = std::sqrt(
n.x *
n.x +
n.y *
n.y +
n.z *
n.z);
219 if (nl < 1e-12f)
continue;
220 n.x /= nl;
n.y /= nl;
n.z /= nl;
221 float d = -(
n.x *
a.x +
n.y *
a.y +
n.z *
a.z);
222 double q[10] = {double(
n.x) *
n.x, double(
n.x) *
n.y, double(
n.x) *
n.z,
223 double(
n.y) *
n.y, double(
n.y) *
n.z, double(
n.z) *
n.z,
224 double(
n.x) *
d, double(
n.y) *
d, double(
n.z) *
d,
226 for (
int k = 0; k < 3; ++k) {
227 double *qv = &quadric_[
static_cast<std::size_t
>(t[k]) * 10];
228 for (
int j = 0; j < 10; ++j) qv[j] += q[j];
230 for (
int k = 0; k < 3; ++k) {
231 triIncident_[t[k]].push_back(i / 3);
232 std::uint32_t
u = t[k],
v = t[(k + 1) % 3];
233 if (
u ==
v)
continue;
234 std::uint64_t key = (std::uint64_t(std::min(
u,
v)) << 32) | std::max(
u,
v);
235 if (seen.try_emplace(key, 1).second) edges_.emplace_back(std::min(
u,
v), std::max(
u,
v));
240 void collapseCost(std::uint32_t
a, std::uint32_t
b,
float &
cost,
float p[3])
const {
241 const double *qa = &quadric_[
static_cast<std::size_t
>(
a) * 10];
242 const double *qb = &quadric_[
static_cast<std::size_t
>(
b) * 10];
244 for (
int j = 0; j < 10; ++j) Q[j] = qa[j] + qb[j];
245 double A[9] = {Q[0], Q[1], Q[2], Q[1], Q[3], Q[4], Q[2], Q[4], Q[5]};
246 double g[3] = {Q[6], Q[7], Q[8]};
247 double det = A[0] * (A[4] * A[8] - A[5] * A[7]) -
248 A[1] * (A[3] * A[8] - A[5] * A[6]) +
249 A[2] * (A[3] * A[7] - A[4] * A[6]);
251 if (std::fabs(det) > 1e-12) {
252 double inv = 1.0 / det;
253 double b1[3] = {-g[0], -g[1], -g[2]};
254 x[0] = inv * ((A[4] * A[8] - A[5] * A[7]) * b1[0] +
255 (A[2] * A[7] - A[1] * A[8]) * b1[1] +
256 (A[1] * A[5] - A[2] * A[4]) * b1[2]);
257 x[1] = inv * ((A[5] * A[6] - A[3] * A[8]) * b1[0] +
258 (A[0] * A[8] - A[2] * A[6]) * b1[1] +
259 (A[2] * A[3] - A[0] * A[5]) * b1[2]);
260 x[2] = inv * ((A[3] * A[7] - A[4] * A[6]) * b1[0] +
261 (A[1] * A[6] - A[0] * A[7]) * b1[1] +
262 (A[0] * A[4] - A[1] * A[3]) * b1[2]);
264 x[0] = 0.5 * (pos_[3 *
a] + pos_[3 *
b]);
265 x[1] = 0.5 * (pos_[3 *
a + 1] + pos_[3 *
b + 1]);
266 x[2] = 0.5 * (pos_[3 *
a + 2] + pos_[3 *
b + 2]);
268 double cv = Q[0] *
x[0] *
x[0] + 2 * Q[1] *
x[0] *
x[1] + 2 * Q[2] *
x[0] *
x[2] +
269 2 * Q[6] *
x[0] + Q[3] *
x[1] *
x[1] + 2 * Q[4] *
x[1] *
x[2] +
270 2 * Q[7] *
x[1] + Q[5] *
x[2] *
x[2] + 2 * Q[8] *
x[2] + Q[9];
271 cost =
static_cast<float>(std::max(0.0, cv));
272 p[0] =
static_cast<float>(
x[0]);
273 p[1] =
static_cast<float>(
x[1]);
274 p[2] =
static_cast<float>(
x[2]);
277 float signedArea(std::uint32_t
a, std::uint32_t
b, std::uint32_t
c)
const {
278 Vec3 pa{pos_[3 *
a], pos_[3 *
a + 1], pos_[3 *
a + 2]};
279 Vec3 pb{pos_[3 *
b], pos_[3 *
b + 1], pos_[3 *
b + 2]};
280 Vec3 pc{pos_[3 *
c], pos_[3 *
c + 1], pos_[3 *
c + 2]};
281 Vec3
n{(pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y),
282 (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z),
283 (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x)};
284 return n.x +
n.y +
n.z;
287 bool flips(std::size_t ti, std::uint32_t
a, std::uint32_t
b)
const {
288 std::uint32_t t0 = tris_[ti * 3 + 0], t1 = tris_[ti * 3 + 1], t2 = tris_[ti * 3 + 2];
289 const bool usesA = (t0 ==
a || t1 ==
a || t2 ==
a);
290 const bool usesB = (t0 ==
b || t1 ==
b || t2 ==
b);
293 if (usesA && usesB)
return false;
294 std::uint32_t s0 = (t0 ==
b) ?
a : t0, s1 = (t1 ==
b) ?
a : t1, s2 = (t2 ==
b) ?
a : t2;
295 if (s0 == s1 || s1 == s2 || s0 == s2)
return true;
296 float before = signedArea(t0, t1, t2);
297 float after = signedArea(s0, s1, s2);
298 if (before == 0.f)
return true;
299 return (after * before) < 0.f;
302 bool collapse(std::uint32_t
a, std::uint32_t
b,
const float p[3],
double &maxError) {
303 float ax = pos_[3 *
a], ay = pos_[3 *
a + 1], az = pos_[3 *
a + 2];
304 pos_[3 *
a] =
p[0]; pos_[3 *
a + 1] =
p[1]; pos_[3 *
a + 2] =
p[2];
306 for (std::size_t ti : triIncident_[
a]) if (flips(ti,
a,
b)) {
ok =
false;
break; }
308 for (std::size_t ti : triIncident_[
b]) if (flips(ti,
a,
b)) {
ok =
false;
break; }
314 double *qa = &quadric_[
static_cast<std::size_t
>(
a) * 10];
315 const double *qb = &quadric_[
static_cast<std::size_t
>(
b) * 10];
316 for (
int j = 0; j < 10; ++j) qa[j] += qb[j];
317 maxError = std::max(maxError, std::max(evalQuadricAtInit(
a), evalQuadricAtInit(
b)));
320 pos_[3 *
a] = ax; pos_[3 *
a + 1] = ay; pos_[3 *
a + 2] = az;
325 std::size_t countAliveTriangles()
const {
327 for (std::size_t i = 0; i + 2 < tris_.size(); i += 3) {
328 std::uint32_t
a = tris_[i],
b = tris_[i + 1],
c = tris_[i + 2];
329 if (
a !=
b &&
b !=
c &&
a !=
c) ++
n;
347 std::vector<std::uint32_t> indices(in.
indices,
351 std::vector<Meshlet> lod0;
353 if (lod0.empty())
return false;
355 std::vector<VgCluster> clusters;
356 clusters.reserve(lod0.size());
357 for (
auto &
m : lod0) {
360 c.triCount =
static_cast<std::uint32_t
>(
m.triangles.size() / 3);
363 c.parent = kNoParent;
364 c.vertCount =
static_cast<std::uint32_t
>(
m.verts.size());
367 clusters.push_back(
c);
371 std::vector<VgCluster> current = clusters;
372 std::size_t level = 0;
374 const std::size_t mergeF =
static_cast<std::size_t
>(opt.
mergeFactor);
375 while (current.size() > 1 && totalLevels < opt.
minLodLevels) {
377 std::vector<VgCluster> parents;
378 std::vector<std::vector<std::uint32_t>> simplifiedList;
380 for (std::size_t g = 0; g < current.size(); g += mergeF) {
381 std::vector<std::uint32_t> unionTris;
382 std::vector<std::uint32_t> unionVerts;
383 std::vector<unsigned char> seenV(
static_cast<std::size_t
>(out.
vertexCount), 0);
384 const std::size_t hi = std::min(current.size(), g + mergeF);
385 for (std::size_t k = g; k < hi; ++k) {
387 for (std::uint32_t i = 0; i < ch.
triCount * 3; ++i) {
389 unionTris.push_back(
v);
390 if (!seenV[
v]) { seenV[
v] = 1; unionVerts.push_back(
v); }
396 parent.parent = kNoParent;
397 parent.childCount =
static_cast<std::uint32_t
>(hi - g);
399 std::vector<std::uint32_t> simplified;
401 std::size_t target = std::max<std::size_t>(
402 1,
static_cast<std::size_t
>(unionTris.size() / 3 * opt.
lodTargetRatio));
403 QuadricSimplifier qs(out.
positions, unionTris, unionVerts, simplified,
error);
405 parent.triCount =
static_cast<std::uint32_t
>(simplified.size() / 3);
408 std::vector<unsigned char> used(
static_cast<std::size_t
>(out.
vertexCount), 0);
409 std::vector<std::uint32_t> pverts;
410 for (std::uint32_t
v : simplified)
411 if (!used[
v]) { used[
v] = 1; pverts.push_back(
v); }
413 parent.vertCount =
static_cast<std::uint32_t
>(pverts.size());
415 parents.push_back(
parent);
416 simplifiedList.push_back(std::move(simplified));
420 for (std::size_t i = 0; i < parents.size(); ++i) {
421 parents[i].triStart =
static_cast<std::uint32_t
>(out.
triangles.size() / 3);
423 simplifiedList[i].end());
427 const std::uint32_t baseId =
static_cast<std::uint32_t
>(clusters.size() - current.size());
428 for (std::size_t g = 0; g < current.size(); g += mergeF) {
429 std::size_t groupIndex = g / mergeF;
430 std::uint32_t pid =
static_cast<std::uint32_t
>(clusters.size() + groupIndex);
431 std::size_t hi = std::min(current.size(), g + mergeF);
432 for (std::size_t k = g; k < hi; ++k) {
433 current[k].parent = pid;
434 parents[groupIndex].children[k - g] = baseId +
static_cast<std::uint32_t
>(k);
437 clusters.insert(clusters.end(), parents.begin(), parents.end());