载入中...
搜索中...
未找到
Builder.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <limits>
7#include <queue>
8#include <unordered_map>
9#include <utility>
10#include <vector>
11
13namespace {
14
15constexpr std::uint32_t kNoParent = 0xFFFFFFFFu;
16
17struct Vec3 {
18 float x = 0.f, y = 0.f, z = 0.f;
19};
20
21// ---------------------------------------------------------------------------
22// LOD0 meshletization: greedily grow connected clusters from seed triangles
23// via a BFS over vertex adjacency, capped by per-cluster vertex/triangle counts.
24// ---------------------------------------------------------------------------
25struct Meshlet {
26 std::vector<std::uint32_t> triangles; // global corner indices, count % 3 == 0
27 std::vector<std::uint32_t> verts; // unique global vertex indices
28};
29
30bool hasVertex(const Meshlet &m, std::uint32_t v) {
31 return std::find(m.verts.begin(), m.verts.end(), v) != m.verts.end();
32}
33
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;
36 int newVerts = 0;
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;
40}
41
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]);
46}
47
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;
52
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);
56
57 std::vector<char> assigned(static_cast<std::size_t>(triCount), 0);
58
59 for (int seed = 0; seed < triCount; ++seed) {
60 if (assigned[seed]) continue;
61
62 Meshlet m;
63 std::queue<int> frontier;
64 frontier.push(seed);
65 const std::uint32_t seedCorner[3] = {indices[3 * seed], indices[3 * seed + 1],
66 indices[3 * seed + 2]};
67 addTriangle(m, seedCorner);
68 assigned[seed] = 1;
69 for (int k = 0; k < 3; ++k)
70 for (int nb : vtxTris[seedCorner[k]])
71 if (!assigned[nb]) frontier.push(nb);
72
73 while (!frontier.empty() &&
74 static_cast<int>(m.triangles.size() / 3) < opt.maxTrianglesPerCluster) {
75 int t = frontier.front();
76 frontier.pop();
77 if (assigned[t]) continue; // already added to this cluster (frontier dupes)
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);
81 assigned[t] = 1;
82 for (int k = 0; k < 3; ++k)
83 for (int nb : vtxTris[corner[k]])
84 if (!assigned[nb]) frontier.push(nb);
85 }
86 out.push_back(std::move(m));
87 }
88}
89
90void computeSphere(const std::vector<float> &positions, const std::vector<std::uint32_t> &verts,
91 float &cx, float &cy, float &cz, float &r) {
92 if (verts.empty()) {
93 cx = cy = cz = r = 0.f;
94 return;
95 }
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];
101 }
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;
106 r = 0.f;
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));
110 }
111}
112
113// ---------------------------------------------------------------------------
114// QEM (Quadric Error Metric) simplifier. Works on a triangle list in GLOBAL
115// vertex indices; collapses edges into an optimal point, avoiding flips.
116// Reports the max geometric error radius (sqrt of max quadric cost).
117// ---------------------------------------------------------------------------
118class QuadricSimplifier {
119public:
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) {
125 tris_ = inTris;
126 pos_ = positions; // mutable working copy for flip tests
127 initPos_ = positions; // original positions for the error metric
128 }
129
130 void run(std::size_t targetTriangles) {
131 build();
132 for (std::uint32_t v : verts_) alive_[v] = 1;
133
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; // rejected edges
138
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;
148 float cost, p[3];
149 collapseCost(a, b, cost, p);
150 if (cost < bestCost) {
151 bestCost = cost;
152 bestA = a;
153 bestB = b;
154 bestP[0] = p[0];
155 bestP[1] = p[1];
156 bestP[2] = p[2];
157 }
158 }
159 if (bestCost == kInf) break;
160 if (!collapse(bestA, bestB, bestP, maxCost)) {
161 blocked[(std::uint64_t(bestA) << 32) | bestB] = 1;
162 continue; // skip this edge, try the next-best
163 }
164 aliveTri = countAliveTriangles();
165 }
166
167 outTris_.clear();
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);
174 }
175 }
176 outError_ = std::sqrt(maxCost);
177 }
178
179private:
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_;
185 float &outError_;
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_;
192
193 // Squared quadric error of vertex v evaluated at its ORIGINAL position.
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];
200 }
201
202 void build() {
203 maxV_ = 0;
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;
209
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,
225 double(d) * 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];
229 }
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));
236 }
237 }
238 }
239
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];
243 double Q[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]);
250 double x[3];
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]);
263 } else {
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]);
267 }
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]);
275 }
276
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;
285 }
286
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);
291 // Triangles on the collapsed edge become degenerate and are removed;
292 // they must not block the collapse.
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;
300 }
301
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];
305 bool ok = true;
306 for (std::size_t ti : triIncident_[a]) if (flips(ti, a, b)) { ok = false; break; }
307 if (ok)
308 for (std::size_t ti : triIncident_[b]) if (flips(ti, a, b)) { ok = false; break; }
309 if (ok) {
310 for (auto &idx : tris_) if (idx == b) idx = a;
311 alive_[b] = 0;
312 // Merge b's quadric into a, then measure cumulative geometric error
313 // (max quadric error over a and b at their original positions).
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)));
318 } else {
319 // Restore original position on rejection.
320 pos_[3 * a] = ax; pos_[3 * a + 1] = ay; pos_[3 * a + 2] = az;
321 }
322 return ok;
323 }
324
325 std::size_t countAliveTriangles() const {
326 std::size_t n = 0;
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;
330 }
331 return n;
332 }
333};
334
335} // namespace
336
339 if (in.vertexCount <= 0 || !in.positions || !in.indices || in.indexCount < 3) return false;
340
341 out = VirtualGeometryAsset();
342 out.vertexCount = in.vertexCount;
343 out.positions.assign(in.positions, in.positions + 3 * static_cast<std::size_t>(in.vertexCount));
344 if (in.normals)
345 out.normals.assign(in.normals, in.normals + 3 * static_cast<std::size_t>(in.vertexCount));
346
347 std::vector<std::uint32_t> indices(in.indices,
348 in.indices + static_cast<std::size_t>(in.indexCount));
349
350 // ---- Level 0: meshletize ----
351 std::vector<Meshlet> lod0;
352 meshletize(indices, in.vertexCount, opt, lod0);
353 if (lod0.empty()) return false;
354
355 std::vector<VgCluster> clusters;
356 clusters.reserve(lod0.size());
357 for (auto &m : lod0) {
358 VgCluster c;
359 c.triStart = static_cast<std::uint32_t>(out.triangles.size() / 3); // triangle units
360 c.triCount = static_cast<std::uint32_t>(m.triangles.size() / 3);
361 c.lodLevel = 0;
362 c.errorR = 0.f;
363 c.parent = kNoParent;
364 c.vertCount = static_cast<std::uint32_t>(m.verts.size());
365 computeSphere(out.positions, m.verts, c.cx, c.cy, c.cz, c.r);
366 out.triangles.insert(out.triangles.end(), m.triangles.begin(), m.triangles.end());
367 clusters.push_back(c);
368 }
369
370 // ---- Higher LOD levels: merge + QEM simplify (cluster DAG) ----
371 std::vector<VgCluster> current = clusters;
372 std::size_t level = 0;
373 int totalLevels = 1;
374 const std::size_t mergeF = static_cast<std::size_t>(opt.mergeFactor);
375 while (current.size() > 1 && totalLevels < opt.minLodLevels) {
376 ++level;
377 std::vector<VgCluster> parents;
378 std::vector<std::vector<std::uint32_t>> simplifiedList;
379
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) {
386 const VgCluster &ch = current[k];
387 for (std::uint32_t i = 0; i < ch.triCount * 3; ++i) {
388 std::uint32_t v = out.triangles[static_cast<std::size_t>(ch.triStart) * 3 + i];
389 unionTris.push_back(v);
390 if (!seenV[v]) { seenV[v] = 1; unionVerts.push_back(v); }
391 }
392 }
393
395 parent.lodLevel = static_cast<std::uint32_t>(level);
396 parent.parent = kNoParent;
397 parent.childCount = static_cast<std::uint32_t>(hi - g);
398
399 std::vector<std::uint32_t> simplified;
400 float error = 0.f;
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);
404 qs.run(target);
405 parent.triCount = static_cast<std::uint32_t>(simplified.size() / 3);
406 parent.errorR = error;
407
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); }
412 computeSphere(out.positions, pverts, parent.cx, parent.cy, parent.cz, parent.r);
413 parent.vertCount = static_cast<std::uint32_t>(pverts.size());
414
415 parents.push_back(parent);
416 simplifiedList.push_back(std::move(simplified));
417 }
418
419 // Append simplified triangles; assign real triStart (triangle units).
420 for (std::size_t i = 0; i < parents.size(); ++i) {
421 parents[i].triStart = static_cast<std::uint32_t>(out.triangles.size() / 3);
422 out.triangles.insert(out.triangles.end(), simplifiedList[i].begin(),
423 simplifiedList[i].end());
424 }
425 // Wire child <-> parent. `current` are the last current.size() clusters,
426 // so global id of current[k] = baseId + k (parents not yet inserted).
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);
435 }
436 }
437 clusters.insert(clusters.end(), parents.begin(), parents.end());
438 current = parents;
439 ++totalLevels;
440 }
441
442 out.clusters = std::move(clusters);
443 return !out.clusters.empty();
444}
445
446} // namespace eve::virtualgeometry
std::vector< std::uint32_t > triangles
Definition Builder.cpp:26
std::vector< std::uint32_t > verts
Definition Builder.cpp:27
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
uint32_t seed
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
std::string error
uint32_t a
uint32_t b
uint32_t c
std::vector< float > cost
int idx
glm::vec4 p[6]
int d
int v
int parent
Definition TreeMesh.cpp:175
float m[16]
bool build(const MeshInput &in, const Options &opt, VirtualGeometryAsset &out)
Returns false on empty/invalid input.
Definition Builder.cpp:337
CPU-side processed cluster (before packing into VgGpuCluster).
Fully processed virtual-geometry asset (CPU representation). Produced by Builder from a Mesh; uploade...
std::vector< std::uint32_t > triangles
Flat global triangle stream; each entry is an index into positions.