载入中...
搜索中...
未找到
GtsMeshSimplifier.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <array>
4#include <cmath>
5#include <limits>
6#include <map>
7#include <set>
8#include <vector>
9namespace eve::procgen {
10namespace {
11struct V {
12 double x, y, z, nx, ny, nz, u, v, r = 1, g = 1, b = 1, a = 1;
13 bool alive = true, locked = false, border = false, seam = false, foldover = false;
14 std::array<double, 10> q{};
15};
16struct T {
17 std::uint32_t a, b, c, aa, ab, ac;
18 int group;
19 bool alive = true;
20};
21using Edge = std::pair<std::uint32_t, std::uint32_t>;
22Edge edge(std::uint32_t a, std::uint32_t b) { return {std::min(a, b), std::max(a, b)}; }
23double qeval(const std::array<double, 10>& q, double x, double y, double z) {
24 return q[0] * x * x + 2 * q[1] * x * y + 2 * q[2] * x * z + q[3] * y * y + 2 * q[4] * y * z + q[5] * z * z +
25 2 * q[6] * x + 2 * q[7] * y + 2 * q[8] * z + q[9];
26}
27bool valid(const MeshBuild& m) {
28 return m.getVertexCount() >= 3 && m.getIndexCount() >= 3 && m.getIndexCount() % 3 == 0 &&
29 m.positions().size() == size_t(m.getVertexCount()) * 3 &&
30 m.normals().size() == size_t(m.getVertexCount()) * 3 && m.uvs().size() == size_t(m.getVertexCount()) * 2 &&
31 (!m.hasVertexColors() || m.colors().size() == size_t(m.getVertexCount()) * 4);
32}
33} // namespace
36 if (!valid(source) || !std::isfinite(quality) || quality < 0 || quality > 1 || o.maxIterationCount <= 0 ||
37 !std::isfinite(o.vertexLinkDistance) || o.vertexLinkDistance < 0 || !std::isfinite(o.aggressiveness) ||
38 o.aggressiveness <= 0)
40 "invalid GTS mesh simplification input or options",
41 "procgen.gtsMeshSimplify"));
42 std::vector<V> vs;
43 vs.reserve(source.getVertexCount());
44 for (int i = 0; i < source.getVertexCount(); ++i) {
45 V v{source.getPositionX(i), source.getPositionY(i), source.getPositionZ(i), source.getNormalX(i),
46 source.getNormalY(i), source.getNormalZ(i), source.getUvU(i), source.getUvV(i),
47 source.getColor(i, 0), source.getColor(i, 1), source.getColor(i, 2), source.getColor(i, 3)};
48 const double* p = &v.x;
49 for (int k = 0; k < 12; ++k)
50 if (!std::isfinite(p[k]))
52 DiagnosticCode::InvalidArgument, "mesh attributes must be finite", "procgen.gtsMeshSimplify"));
53 vs.push_back(v);
54 }
55 std::vector<T> ts;
56 for (int i = 0; i < source.getIndexCount(); i += 3) {
57 int a = source.getIndex(i), b = source.getIndex(i + 1), c = source.getIndex(i + 2);
58 if (a < 0 || b < 0 || c < 0 || a >= int(vs.size()) || b >= int(vs.size()) || c >= int(vs.size()) || a == b ||
59 b == c || a == c)
61 "mesh triangles must be valid and non-degenerate",
62 "procgen.gtsMeshSimplify"));
63 ts.push_back({uint32_t(a), uint32_t(b), uint32_t(c), uint32_t(a), uint32_t(b), uint32_t(c),
64 source.getTriangleGroup(i / 3)});
65 }
66 std::map<Edge, int> counts;
67 for (auto& t : ts) {
68 auto& a = vs[t.a];
69 auto& b = vs[t.b];
70 auto& c = vs[t.c];
71 double ux = b.x - a.x, uy = b.y - a.y, uz = b.z - a.z, vx = c.x - a.x, vy = c.y - a.y, vz = c.z - a.z;
72 double nx = uy * vz - uz * vy, ny = uz * vx - ux * vz, nz = ux * vy - uy * vx,
73 l = sqrt(nx * nx + ny * ny + nz * nz);
74 if (l < 1e-12)
76 DiagnosticCode::InvalidArgument, "mesh contains a zero-area triangle", "procgen.gtsMeshSimplify"));
77 nx /= l;
78 ny /= l;
79 nz /= l;
80 double d = -(nx * a.x + ny * a.y + nz * a.z);
81 std::array<double, 10> q{nx * nx, nx * ny, nx * nz, ny * ny, ny * nz, nz * nz, nx * d, ny * d, nz * d, d * d};
82 for (auto id : {t.a, t.b, t.c})
83 for (int k = 0; k < 10; ++k) vs[id].q[k] += q[k];
84 ++counts[edge(t.a, t.b)];
85 ++counts[edge(t.b, t.c)];
86 ++counts[edge(t.c, t.a)];
87 }
88 for (auto& [e, n] : counts)
89 if (n == 1) {
90 vs[e.first].border = true;
91 vs[e.second].border = true;
92 }
93 if (o.enableSmartLink) {
94 double distance2 = o.vertexLinkDistance * o.vertexLinkDistance;
95 for (size_t i = 0; i < vs.size(); ++i)
96 if (vs[i].border)
97 for (size_t j = i + 1; j < vs.size(); ++j)
98 if (vs[j].border) {
99 double dx = vs[i].x - vs[j].x, dy = vs[i].y - vs[j].y, dz = vs[i].z - vs[j].z;
100 if (dx * dx + dy * dy + dz * dz <= distance2) {
101 bool sameUv = std::abs(vs[i].u - vs[j].u) <= 1e-12 && std::abs(vs[i].v - vs[j].v) <= 1e-12;
102 vs[i].border = vs[j].border = false;
103 if (sameUv)
104 vs[i].foldover = vs[j].foldover = true;
105 else
106 vs[i].seam = vs[j].seam = true;
107 for (auto& t : ts) {
108 if (t.a == j) t.a = uint32_t(i);
109 if (t.b == j) t.b = uint32_t(i);
110 if (t.c == j) t.c = uint32_t(i);
111 }
112 }
113 }
114 }
115 for (auto& v : vs)
116 v.locked = (o.preserveBorderEdges && v.border) || (o.preserveUvSeamEdges && v.seam) ||
117 (o.preserveUvFoldoverEdges && v.foldover);
118 int alive = int(ts.size()), target = int(std::lround(ts.size() * quality));
119 for (int iteration = 0; iteration < o.maxIterationCount && alive > target; ++iteration) {
120 std::set<Edge> es;
121 for (auto& t : ts)
122 if (t.alive) {
123 es.insert(edge(t.a, t.b));
124 es.insert(edge(t.b, t.c));
125 es.insert(edge(t.c, t.a));
126 }
127 double best = std::numeric_limits<double>::infinity();
128 Edge chosen{};
129 for (auto e : es) {
130 auto& a = vs[e.first];
131 auto& b = vs[e.second];
132 if (!a.alive || !b.alive || (a.locked && b.locked) || a.border != b.border || a.seam != b.seam ||
133 a.foldover != b.foldover)
134 continue;
135 std::array<double, 10> q;
136 for (int k = 0; k < 10; ++k) q[k] = a.q[k] + b.q[k];
137 double x = a.locked ? a.x
138 : b.locked ? b.x
139 : (a.x + b.x) * .5,
140 y = a.locked ? a.y
141 : b.locked ? b.y
142 : (a.y + b.y) * .5,
143 z = a.locked ? a.z
144 : b.locked ? b.z
145 : (a.z + b.z) * .5,
146 cost = qeval(q, x, y, z);
147 if (o.preserveSurfaceCurvature) cost *= 2. - std::clamp(a.nx * b.nx + a.ny * b.ny + a.nz * b.nz, -1., 1.);
148 if (cost < best) {
149 best = cost;
150 chosen = e;
151 }
152 }
153 const double threshold = 1e-9 * std::pow(iteration + 3., o.aggressiveness);
154 if (!std::isfinite(best)) break;
155 if (best > threshold) continue;
156 int removedByCollapse = 0;
157 for (const auto& t : ts)
158 if (t.alive) {
159 auto ids = std::array<uint32_t, 3>{t.a, t.b, t.c};
160 for (auto& n : ids)
161 if (n == chosen.second) n = chosen.first;
162 if (ids[0] == ids[1] || ids[1] == ids[2] || ids[0] == ids[2]) ++removedByCollapse;
163 }
164 if (removedByCollapse >= alive) {
165 vs[chosen.first].locked = true;
166 continue;
167 }
168 auto& a = vs[chosen.first];
169 auto& b = vs[chosen.second];
170 V candidate = a;
171 candidate.x = a.locked ? a.x : b.locked ? b.x : (a.x + b.x) * .5;
172 candidate.y = a.locked ? a.y : b.locked ? b.y : (a.y + b.y) * .5;
173 candidate.z = a.locked ? a.z : b.locked ? b.z : (a.z + b.z) * .5;
174 candidate.nx = (a.nx + b.nx) * .5;
175 candidate.ny = (a.ny + b.ny) * .5;
176 candidate.nz = (a.nz + b.nz) * .5;
177 candidate.u = a.locked ? a.u : b.locked ? b.u : (a.u + b.u) * .5;
178 candidate.v = a.locked ? a.v : b.locked ? b.v : (a.v + b.v) * .5;
179 candidate.locked = a.locked || b.locked;
180 for (int k = 0; k < 10; ++k) candidate.q[k] = a.q[k] + b.q[k];
181 bool flip = false;
182 for (auto& t : ts)
183 if (t.alive && (t.a == chosen.second || t.b == chosen.second || t.c == chosen.second ||
184 t.a == chosen.first || t.b == chosen.first || t.c == chosen.first)) {
185 auto old0 = vs[t.a], old1 = vs[t.b], old2 = vs[t.c];
186 auto ids = std::array<uint32_t, 3>{t.a, t.b, t.c};
187 for (auto& n : ids)
188 if (n == chosen.second) n = chosen.first;
189 if (ids[0] == ids[1] || ids[1] == ids[2] || ids[0] == ids[2]) continue;
190 auto p = vs[ids[0]], q = vs[ids[1]], r = vs[ids[2]];
191 if (ids[0] == chosen.first) p = candidate;
192 if (ids[1] == chosen.first) q = candidate;
193 if (ids[2] == chosen.first) r = candidate;
194 double oux = old1.x - old0.x, ouy = old1.y - old0.y, ouz = old1.z - old0.z, ovx = old2.x - old0.x,
195 ovy = old2.y - old0.y, ovz = old2.z - old0.z;
196 double onx = ouy * ovz - ouz * ovy, ony = ouz * ovx - oux * ovz, onz = oux * ovy - ouy * ovx;
197 double ux = q.x - p.x, uy = q.y - p.y, uz = q.z - p.z, vx = r.x - p.x, vy = r.y - p.y, vz = r.z - p.z,
198 nx = uy * vz - uz * vy, ny = uz * vx - ux * vz, nz = ux * vy - uy * vx;
199 double area = nx * nx + ny * ny + nz * nz;
200 if (area < 1e-16 || onx * nx + ony * ny + onz * nz <= 0) {
201 flip = true;
202 break;
203 }
204 }
205 if (flip) {
206 a.locked = true;
207 continue;
208 }
209 a = candidate;
210 b.alive = false;
211 for (auto& t : ts)
212 if (t.alive) {
213 if (t.a == chosen.second) t.a = chosen.first;
214 if (t.b == chosen.second) t.b = chosen.first;
215 if (t.c == chosen.second) t.c = chosen.first;
216 if (t.a == t.b || t.b == t.c || t.a == t.c) {
217 t.alive = false;
218 --alive;
219 }
220 }
221 }
222 MeshBuild next;
223 std::vector<float> colors;
224 std::map<std::pair<uint32_t, uint32_t>, int> map;
225 for (auto& t : ts)
226 if (t.alive) {
227 if (t.group >= 0)
228 next.setActiveGroup(source.getGroupName(t.group));
229 else
230 next.setActiveGroup("");
231 uint32_t ids[3]{t.a, t.b, t.c}, attrs[3]{t.aa, t.ab, t.ac}, out[3];
232 for (int k = 0; k < 3; ++k) {
233 auto key = std::make_pair(ids[k], attrs[k]);
234 auto found = map.find(key);
235 if (found == map.end()) {
236 auto& p = vs[ids[k]];
237 auto& a = vs[attrs[k]];
238 double nl = sqrt(a.nx * a.nx + a.ny * a.ny + a.nz * a.nz);
239 double nx = nl > 1e-12 ? a.nx / nl : a.nx, ny = nl > 1e-12 ? a.ny / nl : a.ny,
240 nz = nl > 1e-12 ? a.nz / nl : a.nz;
241 int index = next.getVertexCount();
242 next.addVertex(float(p.x), float(p.y), float(p.z), float(nx), float(ny), float(nz), float(a.u),
243 float(a.v));
244 if (source.hasVertexColors())
245 colors.insert(colors.end(), {float(a.r), float(a.g), float(a.b), float(a.a)});
246 found = map.emplace(key, index).first;
247 }
248 out[k] = uint32_t(found->second);
249 }
250 next.addTriangle(out[0], out[1], out[2]);
251 }
252 if (!colors.empty()) next.setVertexColors(std::move(colors)).ignore("validated simplified color stream");
253 if (next.empty())
255 DiagnosticCode::InvalidArgument, "simplification removed the complete mesh", "procgen.gtsMeshSimplify"));
256 output = std::move(next);
258}
259} // namespace eve::procgen
LogicalId target
bool locked
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
building::EdgeCurveGroup group
float nx
float nz
float ny
glm::vec4 p[6]
eve::resource::CostSpec cost
tensor::Graph g
Definition GpuGraph.cpp:7
std::uint32_t alive
std::uint32_t key
glm::uvec4 ids
float u
Definition Grass.cpp:233
float area
Definition Grass.cpp:62
glm::vec3 n
Definition Grass.cpp:63
bool foldover
std::array< double, 10 > q
std::uint32_t ab
double r
bool seam
bool border
std::uint32_t ac
std::uint32_t aa
float v
std::int32_t c
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float d
float t
std::vector< float > colors
const RoadEdge * edge
bool found
float dz
float dy
float dx
std::map< Cell, int > best
uint32_t index
const UnitySourceAsset & source
float m[16]
float vz
float vy
float vx
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
Result< int > simplifyGtsMesh(MeshBuild &output, const MeshBuild &source, float quality, const GtsMeshSimplificationOptions &o)
Simplify a mesh with deterministic quadric edge collapse.
GTS mesh simplification controls supported by MeshBuild streams.
glm::uvec4 counts