载入中...
搜索中...
未找到
MeshContactBlend.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <limits>
7#include <string>
8#include <utility>
9#include <vector>
10
11namespace eve::procgen {
12namespace {
13
14constexpr float kEpsilon = 1e-8f;
15
16struct Vec3 {
17 float x = 0.f, y = 0.f, z = 0.f;
18};
19
20Vec3 operator+(Vec3 a, Vec3 b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
21Vec3 operator-(Vec3 a, Vec3 b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
22Vec3 operator*(Vec3 a, float s) { return {a.x * s, a.y * s, a.z * s}; }
23float dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
24Vec3 cross(Vec3 a, Vec3 b) {
25 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
26}
27float length2(Vec3 v) { return dot(v, v); }
28Vec3 normalized(Vec3 v) {
29 const float len = std::sqrt(length2(v));
30 return len > kEpsilon ? v * (1.f / len) : Vec3{0.f, 1.f, 0.f};
31}
32float saturate(float value) { return std::clamp(value, 0.f, 1.f); }
33
34float evaluateFalloff(std::string_view kind, float t) {
35 t = saturate(t);
36 if (kind == "linear") return t;
37 if (kind == "sharp") return t * t;
38 if (kind == "sphere") return std::sqrt(std::max(0.f, 2.f * t - t * t));
39 return t * t * (3.f - 2.f * t);
40}
41
42struct ClosestHit {
43 float distance = std::numeric_limits<float>::infinity();
44 Vec3 point{};
45 Vec3 normal{0.f, 1.f, 0.f};
46};
47
48ClosestHit closestOnTriangle(Vec3 point, Vec3 a, Vec3 b, Vec3 c) {
49 const Vec3 ab = b - a, ac = c - a, ap = point - a;
50 const float d1 = dot(ab, ap), d2 = dot(ac, ap);
51 const Vec3 faceNormal = normalized(cross(ab, ac));
52 if (d1 <= 0.f && d2 <= 0.f) return {std::sqrt(length2(point - a)), a, faceNormal};
53 const Vec3 bp = point - b;
54 const float d3 = dot(ab, bp), d4 = dot(ac, bp);
55 if (d3 >= 0.f && d4 <= d3) return {std::sqrt(length2(point - b)), b, faceNormal};
56 const float vc = d1 * d4 - d3 * d2;
57 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
58 const float v = d1 / (d1 - d3);
59 const Vec3 p = a + ab * v;
60 return {std::sqrt(length2(point - p)), p, faceNormal};
61 }
62 const Vec3 cp = point - c;
63 const float d5 = dot(ab, cp), d6 = dot(ac, cp);
64 if (d6 >= 0.f && d5 <= d6) return {std::sqrt(length2(point - c)), c, faceNormal};
65 const float vb = d5 * d2 - d1 * d6;
66 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
67 const float w = d2 / (d2 - d6);
68 const Vec3 p = a + ac * w;
69 return {std::sqrt(length2(point - p)), p, faceNormal};
70 }
71 const float va = d3 * d6 - d5 * d4;
72 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
73 const float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
74 const Vec3 p = b + (c - b) * w;
75 return {std::sqrt(length2(point - p)), p, faceNormal};
76 }
77 const float denom = 1.f / (va + vb + vc);
78 const float v = vb * denom;
79 const float w = vc * denom;
80 const Vec3 p = a + ab * v + ac * w;
81 return {std::sqrt(length2(point - p)), p, faceNormal};
82}
83
84struct Aabb {
85 Vec3 min{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
86 std::numeric_limits<float>::max()};
87 Vec3 max{-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(),
88 -std::numeric_limits<float>::max()};
89 void expand(Vec3 p) {
90 min.x = std::min(min.x, p.x);
91 min.y = std::min(min.y, p.y);
92 min.z = std::min(min.z, p.z);
93 max.x = std::max(max.x, p.x);
94 max.y = std::max(max.y, p.y);
95 max.z = std::max(max.z, p.z);
96 }
97 void expand(const Aabb& other) {
98 expand(other.min);
99 expand(other.max);
100 }
101 [[nodiscard]] float distance2(Vec3 p) const {
102 const float dx = p.x < min.x ? min.x - p.x : (p.x > max.x ? p.x - max.x : 0.f);
103 const float dy = p.y < min.y ? min.y - p.y : (p.y > max.y ? p.y - max.y : 0.f);
104 const float dz = p.z < min.z ? min.z - p.z : (p.z > max.z ? p.z - max.z : 0.f);
105 return dx * dx + dy * dy + dz * dz;
106 }
107};
108
109struct TriangleRef {
110 std::uint32_t i0 = 0, i1 = 0, i2 = 0;
111 Vec3 centroid{};
112 Aabb bounds{};
113};
114
115struct BvhNode {
116 Aabb bounds{};
117 std::int32_t left = -1;
118 std::int32_t right = -1;
119 std::int32_t begin = 0;
120 std::int32_t count = 0;
121};
122
123struct SurfaceBvh {
124 const MeshBuild* mesh = nullptr;
125 std::vector<TriangleRef> triangles;
126 std::vector<std::int32_t> order;
127 std::vector<BvhNode> nodes;
128
129 void build(const MeshBuild& source, const std::vector<std::int32_t>& vertexSourceIds, std::int32_t sourceId) {
130 mesh = &source;
131 triangles.clear();
132 order.clear();
133 nodes.clear();
134 for (int t = 0; t + 2 < source.getIndexCount(); t += 3) {
135 const auto i0 = static_cast<std::uint32_t>(source.getIndex(t));
136 const auto i1 = static_cast<std::uint32_t>(source.getIndex(t + 1));
137 const auto i2 = static_cast<std::uint32_t>(source.getIndex(t + 2));
138 if (static_cast<std::size_t>(i0) >= vertexSourceIds.size()) continue;
139 if (vertexSourceIds[i0] != sourceId) continue;
140 TriangleRef tri;
141 tri.i0 = i0;
142 tri.i1 = i1;
143 tri.i2 = i2;
144 const Vec3 a{source.getPositionX(static_cast<int>(i0)), source.getPositionY(static_cast<int>(i0)),
145 source.getPositionZ(static_cast<int>(i0))};
146 const Vec3 b{source.getPositionX(static_cast<int>(i1)), source.getPositionY(static_cast<int>(i1)),
147 source.getPositionZ(static_cast<int>(i1))};
148 const Vec3 c{source.getPositionX(static_cast<int>(i2)), source.getPositionY(static_cast<int>(i2)),
149 source.getPositionZ(static_cast<int>(i2))};
150 tri.centroid = (a + b + c) * (1.f / 3.f);
151 tri.bounds.expand(a);
152 tri.bounds.expand(b);
153 tri.bounds.expand(c);
154 triangles.push_back(tri);
155 }
156 if (triangles.empty()) return;
157 order.resize(triangles.size());
158 for (std::size_t i = 0; i < order.size(); ++i) order[i] = static_cast<std::int32_t>(i);
159 nodes.reserve(triangles.size() * 2u);
160 buildNode(0, static_cast<std::int32_t>(order.size()), 0);
161 }
162
163 std::int32_t buildNode(std::int32_t begin, std::int32_t end, int depth) {
164 BvhNode node;
165 node.begin = begin;
166 node.count = end - begin;
167 for (std::int32_t i = begin; i < end; ++i)
168 node.bounds.expand(triangles[static_cast<std::size_t>(order[static_cast<std::size_t>(i)])].bounds);
169 const auto index = static_cast<std::int32_t>(nodes.size());
170 nodes.push_back(node);
171 if (node.count <= 8) return index;
172 const int axis = depth % 3;
173 const auto mid = begin + node.count / 2;
174 std::nth_element(order.begin() + begin, order.begin() + mid, order.begin() + end,
175 [&](std::int32_t a, std::int32_t b) {
176 const auto& ca = triangles[static_cast<std::size_t>(a)].centroid;
177 const auto& cb = triangles[static_cast<std::size_t>(b)].centroid;
178 if (axis == 0) return ca.x < cb.x || (ca.x == cb.x && a < b);
179 if (axis == 1) return ca.y < cb.y || (ca.y == cb.y && a < b);
180 return ca.z < cb.z || (ca.z == cb.z && a < b);
181 });
182 nodes[static_cast<std::size_t>(index)].left = buildNode(begin, mid, depth + 1);
183 nodes[static_cast<std::size_t>(index)].right = buildNode(mid, end, depth + 1);
184 nodes[static_cast<std::size_t>(index)].count = 0;
185 return index;
186 }
187
188 void queryNode(std::int32_t nodeIndex, Vec3 point, float maxDistance, ClosestHit& best) const {
189 if (nodeIndex < 0 || mesh == nullptr || nodes.empty()) return;
190 const auto& node = nodes[static_cast<std::size_t>(nodeIndex)];
191 if (node.bounds.distance2(point) > maxDistance * maxDistance) return;
192 if (node.count > 0) {
193 for (std::int32_t i = 0; i < node.count; ++i) {
194 const auto& tri =
195 triangles[static_cast<std::size_t>(order[static_cast<std::size_t>(node.begin + i)])];
196 const Vec3 a{mesh->getPositionX(static_cast<int>(tri.i0)), mesh->getPositionY(static_cast<int>(tri.i0)),
197 mesh->getPositionZ(static_cast<int>(tri.i0))};
198 const Vec3 b{mesh->getPositionX(static_cast<int>(tri.i1)), mesh->getPositionY(static_cast<int>(tri.i1)),
199 mesh->getPositionZ(static_cast<int>(tri.i1))};
200 const Vec3 c{mesh->getPositionX(static_cast<int>(tri.i2)), mesh->getPositionY(static_cast<int>(tri.i2)),
201 mesh->getPositionZ(static_cast<int>(tri.i2))};
202 auto hit = closestOnTriangle(point, a, b, c);
203 if (hit.distance < best.distance && hit.distance <= maxDistance) best = hit;
204 }
205 return;
206 }
207 queryNode(node.left, point, maxDistance, best);
208 queryNode(node.right, point, maxDistance, best);
209 }
210
211 [[nodiscard]] ClosestHit closest(Vec3 point, float maxDistance) const {
212 ClosestHit best;
213 if (!nodes.empty()) queryNode(0, point, maxDistance, best);
214 return best;
215 }
216};
217
218Result<void> validateParams(const MeshContactBlendParams& params) {
219 if (!std::isfinite(params.edgeRadius) || params.edgeRadius < 0.f || !std::isfinite(params.materialRadius) ||
220 params.materialRadius < 0.f || !std::isfinite(params.strength) || params.strength < 0.f ||
221 !std::isfinite(params.normalsBlend) || params.normalsBlend < 0.f || !std::isfinite(params.materialBlend) ||
222 params.materialBlend < 0.f || !std::isfinite(params.maxQueryDistance) || params.maxQueryDistance < 0.f ||
223 !std::isfinite(params.surfaceOffset))
225 "contact blend parameters must be finite and non-negative",
226 "params", {}, "procgen.mesh.blend"));
227 if (params.falloff != "smooth" && params.falloff != "linear" && params.falloff != "sharp" &&
228 params.falloff != "sphere")
230 "contact blend falloff must be smooth|linear|sharp|sphere",
231 "falloff", {}, "procgen.mesh.blend"));
232 return Result<void>::success();
233}
234
235void applyHitToVertex(MeshBuild& output, std::vector<float>& colors, int vertex, const ClosestHit& best,
236 float maxQuery, const MeshContactBlendParams& params) {
237 auto& positions = output.positions();
238 auto& normals = output.normals();
239 const std::size_t base = static_cast<std::size_t>(vertex) * 3u;
240 const Vec3 origin{positions[base], positions[base + 1u], positions[base + 2u]};
241 if (!std::isfinite(best.distance) || best.distance > maxQuery) {
242 colors[static_cast<std::size_t>(vertex) * 4u + 3u] = 0.f;
243 return;
244 }
245 float edgeWeight = 0.f;
246 if (params.edgeRadius > 0.f && params.strength > 0.f) {
247 const float t = 1.f - saturate(best.distance / params.edgeRadius);
248 edgeWeight = saturate(evaluateFalloff(params.falloff, t) * params.strength);
249 }
250 float materialWeight = 0.f;
251 if (params.materialRadius > 0.f && params.materialBlend > 0.f) {
252 const float t = 1.f - saturate(best.distance / params.materialRadius);
253 materialWeight = saturate(evaluateFalloff(params.falloff, t) * params.materialBlend);
254 }
255 if (params.softSnapPositions && edgeWeight > 0.f) {
256 const Vec3 target = best.point + best.normal * params.surfaceOffset;
257 const Vec3 blended = origin + (target - origin) * edgeWeight;
258 positions[base] = blended.x;
259 positions[base + 1] = blended.y;
260 positions[base + 2] = blended.z;
261 // Soft-snap changes geometry; hit-normal lerp fights continuous shading.
262 // Callers rebuild triangle normals after the full vertex pass.
263 } else if (params.normalsBlend > 0.f && edgeWeight > 0.f) {
264 const Vec3 n0{normals[base], normals[base + 1u], normals[base + 2u]};
265 const float w = saturate(edgeWeight * params.normalsBlend);
266 const Vec3 n1 = normalized(n0 * (1.f - w) + best.normal * w);
267 normals[base] = n1.x;
268 normals[base + 1] = n1.y;
269 normals[base + 2] = n1.z;
270 }
271 colors[static_cast<std::size_t>(vertex) * 4u + 3u] = materialWeight;
272}
273
274void recalculateNormalsFromGeometry(MeshBuild& mesh) {
275 auto& normals = mesh.normals();
276 std::fill(normals.begin(), normals.end(), 0.f);
277 const auto& positions = mesh.positions();
278 const auto& indices = mesh.indices();
279 for (std::size_t i = 0; i + 2 < indices.size(); i += 3) {
280 const std::size_t a = static_cast<std::size_t>(indices[i]) * 3u;
281 const std::size_t b = static_cast<std::size_t>(indices[i + 1u]) * 3u;
282 const std::size_t c = static_cast<std::size_t>(indices[i + 2u]) * 3u;
283 const Vec3 ab{positions[b] - positions[a], positions[b + 1u] - positions[a + 1u],
284 positions[b + 2u] - positions[a + 2u]};
285 const Vec3 ac{positions[c] - positions[a], positions[c + 1u] - positions[a + 1u],
286 positions[c + 2u] - positions[a + 2u]};
287 const Vec3 face = cross(ab, ac);
288 for (const auto vertex : {a, b, c}) {
289 normals[vertex] += face.x;
290 normals[vertex + 1u] += face.y;
291 normals[vertex + 2u] += face.z;
292 }
293 }
294 for (std::size_t i = 0; i + 2 < normals.size(); i += 3) {
295 const Vec3 n = normalized({normals[i], normals[i + 1u], normals[i + 2u]});
296 normals[i] = n.x;
297 normals[i + 1u] = n.y;
298 normals[i + 2u] = n.z;
299 }
300}
301
302void smoothNormalsNeighborhood(MeshBuild& mesh, int iterations) {
303 // Soft-snap necks keep a geometric pinch; average 1-ring normals so lighting
304 // reads continuous without requiring a remesh/fillet.
305 const int count = mesh.getVertexCount();
306 if (count <= 0 || iterations <= 0) return;
307 std::vector<std::vector<std::uint32_t>> neighbors(static_cast<std::size_t>(count));
308 const auto& indices = mesh.indices();
309 for (std::size_t i = 0; i + 2 < indices.size(); i += 3) {
310 const std::uint32_t a = indices[i], b = indices[i + 1u], c = indices[i + 2u];
311 neighbors[a].push_back(b);
312 neighbors[a].push_back(c);
313 neighbors[b].push_back(a);
314 neighbors[b].push_back(c);
315 neighbors[c].push_back(a);
316 neighbors[c].push_back(b);
317 }
318 auto& normals = mesh.normals();
319 std::vector<float> next = normals;
320 for (int iteration = 0; iteration < iterations; ++iteration) {
321 const auto current = normals;
322 for (int vertex = 0; vertex < count; ++vertex) {
323 const auto& adjacent = neighbors[static_cast<std::size_t>(vertex)];
324 Vec3 sum{current[static_cast<std::size_t>(vertex) * 3u],
325 current[static_cast<std::size_t>(vertex) * 3u + 1u],
326 current[static_cast<std::size_t>(vertex) * 3u + 2u]};
327 for (const auto neighbor : adjacent) {
328 sum.x += current[static_cast<std::size_t>(neighbor) * 3u];
329 sum.y += current[static_cast<std::size_t>(neighbor) * 3u + 1u];
330 sum.z += current[static_cast<std::size_t>(neighbor) * 3u + 2u];
331 }
332 const Vec3 n = normalized(sum);
333 const std::size_t base = static_cast<std::size_t>(vertex) * 3u;
334 next[base] = n.x;
335 next[base + 1u] = n.y;
336 next[base + 2u] = n.z;
337 }
338 normals.swap(next);
339 }
340}
341
342void finalizeSoftSnapNormals(MeshBuild& mesh) {
343 recalculateNormalsFromGeometry(mesh);
344 smoothNormalsNeighborhood(mesh, 12);
345 mesh.setMeta("contactBlend.normals", "recalculated+smoothed");
346}
347
348} // namespace
349
351 if (mesh.getVertexCount() <= 0) return;
352 finalizeSoftSnapNormals(mesh);
353}
354
355namespace {
356
357Result<MeshBuild> blendMultiImpl(const MeshBuild& mesh, const std::vector<std::int32_t>& vertexSourceIds,
358 const MeshContactBlendParams& params) {
359 const int vertexCount = mesh.getVertexCount();
360 if (vertexCount < 0 || static_cast<std::size_t>(vertexCount) != vertexSourceIds.size())
362 DiagnosticCode::InvalidArgument, "contact blend vertexSourceIds size must match vertex count",
363 "vertexSourceIds", {}, "procgen.mesh.blend"));
364 auto validated = validateParams(params);
365 if (!validated.ok()) return Result<MeshBuild>::failure(validated.status());
366
367 float maxQuery = params.maxQueryDistance;
368 if (maxQuery <= 0.f) maxQuery = std::max(params.edgeRadius, params.materialRadius);
369 if (maxQuery <= 0.f) {
370 MeshBuild copy = mesh;
371 copy.setMeta("contactBlend", "identity");
372 return Result<MeshBuild>::success(std::move(copy));
373 }
374
375 std::vector<std::int32_t> uniqueSources = vertexSourceIds;
376 std::sort(uniqueSources.begin(), uniqueSources.end());
377 uniqueSources.erase(std::unique(uniqueSources.begin(), uniqueSources.end()), uniqueSources.end());
378 if (uniqueSources.size() < 2)
380 DiagnosticCode::InvalidArgument, "contact blend requires at least two distinct source ids",
381 "vertexSourceIds", {}, "procgen.mesh.blend"));
382
383 std::vector<SurfaceBvh> bvhs(uniqueSources.size());
384 for (std::size_t i = 0; i < uniqueSources.size(); ++i)
385 bvhs[i].build(mesh, vertexSourceIds, uniqueSources[i]);
386
387 MeshBuild output = mesh;
388 std::vector<float> colors =
389 mesh.hasVertexColors() ? mesh.colors()
390 : std::vector<float>(static_cast<std::size_t>(vertexCount) * 4u, 1.f);
391 auto& positions = output.positions();
392 for (int vertex = 0; vertex < vertexCount; ++vertex) {
393 const auto sourceId = vertexSourceIds[static_cast<std::size_t>(vertex)];
394 const std::size_t base = static_cast<std::size_t>(vertex) * 3u;
395 const Vec3 origin{positions[base], positions[base + 1u], positions[base + 2u]};
396 ClosestHit best;
397 for (std::size_t i = 0; i < uniqueSources.size(); ++i) {
398 if (uniqueSources[i] == sourceId) continue;
399 auto hit = bvhs[i].closest(origin, maxQuery);
400 if (hit.distance < best.distance) best = hit;
401 }
402 applyHitToVertex(output, colors, vertex, best, maxQuery, params);
403 }
404
405 auto setColors = output.setVertexColors(std::move(colors));
406 if (!setColors.ok()) return Result<MeshBuild>::failure(setColors.status());
407 if (params.softSnapPositions) finalizeSoftSnapNormals(output);
408 output.setMeta("contactBlend", "applied");
409 output.setMeta("contactBlend.falloff", std::string(params.falloff));
410 return Result<MeshBuild>::success(std::move(output));
411}
412
413Result<MeshBuild> blendAgainstImpl(const MeshBuild& movable, const MeshBuild& surface,
414 const MeshContactBlendParams& params) {
415 if (movable.empty())
417 "contact blend movable mesh is empty", "movable", {},
418 "procgen.mesh.blend"));
419 if (surface.empty())
421 "contact blend surface mesh is empty", "surface", {},
422 "procgen.mesh.blend"));
423 auto validated = validateParams(params);
424 if (!validated.ok()) return Result<MeshBuild>::failure(validated.status());
425
426 float maxQuery = params.maxQueryDistance;
427 if (maxQuery <= 0.f) maxQuery = std::max(params.edgeRadius, params.materialRadius);
428 if (maxQuery <= 0.f) {
429 MeshBuild copy = movable;
430 copy.setMeta("contactBlend", "identity");
431 return Result<MeshBuild>::success(std::move(copy));
432 }
433
434 // Surface-only BVH: every surface vertex tagged source 1; query ignores movable topology.
435 std::vector<std::int32_t> surfaceIds(static_cast<std::size_t>(surface.getVertexCount()), 1);
436 SurfaceBvh bvh;
437 bvh.build(surface, surfaceIds, 1);
438 if (bvh.triangles.empty())
440 "contact blend surface has no triangles", "surface", {},
441 "procgen.mesh.blend"));
442
443 MeshBuild output = movable;
444 const int vertexCount = output.getVertexCount();
445 std::vector<float> colors =
446 movable.hasVertexColors() ? movable.colors()
447 : std::vector<float>(static_cast<std::size_t>(vertexCount) * 4u, 1.f);
448 auto& positions = output.positions();
449 for (int vertex = 0; vertex < vertexCount; ++vertex) {
450 const std::size_t base = static_cast<std::size_t>(vertex) * 3u;
451 const Vec3 origin{positions[base], positions[base + 1u], positions[base + 2u]};
452 applyHitToVertex(output, colors, vertex, bvh.closest(origin, maxQuery), maxQuery, params);
453 }
454 auto setColors = output.setVertexColors(std::move(colors));
455 if (!setColors.ok()) return Result<MeshBuild>::failure(setColors.status());
456 if (params.softSnapPositions) finalizeSoftSnapNormals(output);
457 output.setMeta("contactBlend", "againstSurface");
458 output.setMeta("contactBlend.falloff", std::string(params.falloff));
459 return Result<MeshBuild>::success(std::move(output));
460}
461
462} // namespace
463
464Result<MeshBuild> meshContactBlendResult(const MeshBuild& mesh, const std::vector<std::int32_t>& vertexSourceIds,
466 return blendMultiImpl(mesh, vertexSourceIds, params);
467}
468
471 return blendAgainstImpl(movable, surface, params);
472}
473
474} // namespace eve::procgen
LogicalId target
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int mid
Definition AnimSmr.cpp:120
std::string output
const std::string & s
glm::vec4 p[6]
std::uint64_t sourceId
std::uint32_t vertexCount
uint32_t i1
Definition Grass.cpp:61
uint32_t i2
Definition Grass.cpp:61
uint32_t i0
Definition Grass.cpp:61
glm::vec3 n
Definition Grass.cpp:63
std::uint32_t ab
std::uint32_t ac
std::vector< std::uint32_t > indices
std::vector< float > normals
std::vector< float > positions
float v
HexVec3 left
HexVec3 right
std::int32_t c
TokenKind kind
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
Vec3 centroid
std::vector< std::int32_t > order
std::vector< BvhNode > nodes
std::vector< TriangleRef > triangles
Texture * normal
bool hit
float begin
float t
Mesh * mesh
std::vector< float > colors
V3 origin
Definition RoadBake.cpp:138
const RoadNode * node
double current
float dz
float dy
float dx
std::uint32_t count
std::map< Cell, int > best
int iterations
Definition TreeMesh.cpp:311
uint32_t index
const UnitySourceAsset & source
std::uint32_t depth
glm::vec3 point
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
std::vector< ParamSpec > params
float length2(float x, float z)
Length 2.
Definition AnimMath.h:150
Vec3 operator-(Vec3 lhs, Vec3 rhs)
Operator -.
Vec3 operator+(Vec3 lhs, Vec3 rhs)
Operator +.
Vec3 operator*(Vec3 value, float scale)
Operator *.
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
Result< MeshBuild > meshContactBlendResult(const MeshBuild &mesh, const std::vector< std::int32_t > &vertexSourceIds, const MeshContactBlendParams &params)
Fuse edge normals / optional positions and material blend weights across source slices.
Result< MeshBuild > meshContactBlendAgainstSurfaceResult(const MeshBuild &movable, const MeshBuild &surface, const MeshContactBlendParams &params)
Deform movable toward surface only (dynamic adhere / one-way fusion).
void rebuildSoftSnapContactNormals(MeshBuild &mesh)
Rebuild triangle normals then 1-ring-average them (soft-snap / post-weld lighting).
int axis(int64_t a, size_t rank)
Axis.
void faceNormal(FaceDir d, float &nx, float &ny, float &nz)
Outward unit normal for the face.
Definition FaceDir.h:88
Parameters for contact-band edge and material fusion.
glm::vec4 bounds