载入中...
搜索中...
未找到
VolumeFluidSimplexQuery.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <glm/gtc/quaternion.hpp>
6
8namespace {
9bool finite(glm::vec3 v) { return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z); }
10bool finite(glm::vec4 v) { return finite(glm::vec3(v)) && std::isfinite(v.w); }
11bool inRange(float v, float lo, float hi) { return std::isfinite(v) && v >= lo && v <= hi; }
12bool validFilter(unsigned filter) { return (filter & 0xffffu) != 0 && (filter >> 16u) != 0; }
13bool filtersMatch(unsigned a, unsigned b) {
14 return ((a & 0xffffu) & (b >> 16u)) != 0 && ((b & 0xffffu) & (a >> 16u)) != 0;
15}
16struct SimplexPoint {
17 glm::vec3 point{0.f};
18 glm::vec4 bary{1.f, 0.f, 0.f, 0.f};
19};
20
21SimplexPoint closestEdge(glm::vec3 a, glm::vec3 b, glm::vec3 p) {
22 const auto edge = b - a;
23 const float denominator = glm::dot(edge, edge);
24 const float t = denominator > 1e-12f ? std::clamp(glm::dot(p - a, edge) / denominator, 0.f, 1.f) : 0.f;
25 return {a + edge * t, {1.f - t, t, 0.f, 0.f}};
26}
27SimplexPoint closestTriangle(glm::vec3 a, glm::vec3 b, glm::vec3 c, glm::vec3 p) {
28 const auto ab = b - a, ac = c - a, ap = p - a;
29 const float d1 = glm::dot(ab, ap), d2 = glm::dot(ac, ap);
30 if (d1 <= 0.f && d2 <= 0.f) return {a, {1, 0, 0, 0}};
31 const auto bp = p - b;
32 const float d3 = glm::dot(ab, bp), d4 = glm::dot(ac, bp);
33 if (d3 >= 0.f && d4 <= d3) return {b, {0, 1, 0, 0}};
34 const float vc = d1 * d4 - d3 * d2;
35 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
36 const float v = d1 / (d1 - d3);
37 return {a + ab * v, {1 - v, v, 0, 0}};
38 }
39 const auto cp = p - c;
40 const float d5 = glm::dot(ab, cp), d6 = glm::dot(ac, cp);
41 if (d6 >= 0.f && d5 <= d6) return {c, {0, 0, 1, 0}};
42 const float vb = d5 * d2 - d1 * d6;
43 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
44 const float w = d2 / (d2 - d6);
45 return {a + ac * w, {1 - w, 0, w, 0}};
46 }
47 const float va = d3 * d6 - d5 * d4;
48 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
49 const float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
50 return {b + (c - b) * w, {0, 1 - w, w, 0}};
51 }
52 const float sum = va + vb + vc;
53 if (std::abs(sum) <= 1e-12f) {
54 auto first = closestEdge(a, b, p), second = closestEdge(b, c, p), third = closestEdge(c, a, p);
55 const float da = glm::dot(first.point - p, first.point - p), db = glm::dot(second.point - p, second.point - p),
56 dc = glm::dot(third.point - p, third.point - p);
57 if (da <= db && da <= dc) return first;
58 if (db <= dc) return {second.point, {0, second.bary.x, second.bary.y, 0}};
59 return {third.point, {third.bary.y, 0, third.bary.x, 0}};
60 }
61 const float inverse = 1.f / sum, v = vb * inverse, w = vc * inverse;
62 return {a + ab * v + ac * w, {1.f - v - w, v, w, 0}};
63}
64SimplexPoint closestSimplex(const VolumeFluidSimplex& simplex, std::span<const VolumeFluidParticle> particles,
65 glm::vec3 point) {
66 const auto a = particles[simplex.particleIndices[0]].position;
67 if (simplex.size == 1) return {a, {1, 0, 0, 0}};
68 const auto b = particles[simplex.particleIndices[1]].position;
69 if (simplex.size == 2) return closestEdge(a, b, point);
70 return closestTriangle(a, b, particles[simplex.particleIndices[2]].position, point);
71}
72float radiusAlong(const VolumeFluidSimplex& simplex, std::span<const VolumeFluidParticle> particles, glm::vec4 bary,
73 glm::vec3 normal) {
74 float radius = 0.f;
75 for (unsigned i = 0; i < simplex.size; ++i) {
76 const auto& particle = particles[simplex.particleIndices[i]];
77 const auto q =
78 glm::quat(particle.orientation.w, particle.orientation.x, particle.orientation.y, particle.orientation.z);
79 radius += bary[i] / glm::length((glm::conjugate(q) * normal) / particle.radii);
80 }
81 return radius;
82}
83glm::vec3 boxPoint(const VolumeFluidQueryShape& query, glm::vec3 world, glm::vec3& normal) {
84 const auto q = glm::normalize(glm::quat(query.rotation.w, query.rotation.x, query.rotation.y, query.rotation.z));
85 const auto half = query.size * .5f + glm::vec3(query.contactOffset),
86 local = glm::conjugate(q) * (world - query.center);
87 auto projected = glm::clamp(local, -half, half);
88 const auto outside = glm::abs(local) - half;
89 if (glm::all(glm::lessThanEqual(outside, glm::vec3(0.f)))) {
90 const auto gap = half - glm::abs(local);
91 int axis = 0;
92 if (gap.y < gap.x) axis = 1;
93 if (gap.z < gap[axis]) axis = 2;
94 projected[axis] = local[axis] >= 0.f ? half[axis] : -half[axis];
95 }
96 const auto point = query.center + q * projected, delta = world - point;
97 const float length = glm::length(delta);
98 normal = length > 1e-7f ? delta / length : q * glm::vec3(0, 1, 0);
99 return point;
100}
101VolumeFluidSimplex implicitPoint(unsigned index) { return {{index, 0, 0}, 1}; }
102bool selected(const VolumeFluidSimplex& simplex, std::span<const VolumeFluidParticle> particles,
103 const VolumeFluidQueryShape& query) {
104 for (unsigned i = 0; i < simplex.size; ++i) {
105 const auto& p = particles[simplex.particleIndices[i]];
106 if ((query.phaseMask & (1u << unsigned(p.material.phase))) != 0 &&
107 filtersMatch(query.collisionFilter, p.collisionFilter))
108 return true;
109 }
110 return false;
111}
112VolumeFluidSimplexHit evaluate(const VolumeFluidSimplex& simplex, unsigned simplexIndex, unsigned queryIndex,
113 std::span<const VolumeFluidParticle> particles, const VolumeFluidQueryShape& query) {
114 SimplexPoint convex;
115 glm::vec3 queryPoint, normal;
117 convex = closestSimplex(simplex, particles, query.center);
118 const auto delta = convex.point - query.center;
119 const float length = glm::length(delta);
120 normal = length > 1e-7f ? delta / length : glm::vec3(0, 1, 0);
121 queryPoint = query.center + normal * (query.size.x + query.contactOffset);
122 } else if (query.type == VolumeFluidQueryType::Box) {
123 convex.bary = glm::vec4(1.f / float(simplex.size));
124 convex.point = glm::vec3(0.f);
125 for (unsigned i = 0; i < simplex.size; ++i)
126 convex.point += particles[simplex.particleIndices[i]].position * convex.bary[i];
127 for (int iteration = 0; iteration < 8; ++iteration) {
128 queryPoint = boxPoint(query, convex.point, normal);
129 convex = closestSimplex(simplex, particles, queryPoint);
130 }
131 queryPoint = boxPoint(query, convex.point, normal);
132 } else {
133 const auto segment = query.size - query.center;
134 const float length = glm::length(segment);
135 const auto direction = segment / length;
136 float along = .5f * length;
137 queryPoint = query.center + direction * along;
138 for (int iteration = 0; iteration < 8; ++iteration) {
139 convex = closestSimplex(simplex, particles, queryPoint);
140 along = std::clamp(glm::dot(convex.point - query.center, direction), 0.f, length);
141 queryPoint = query.center + direction * along;
142 }
143 const auto delta = convex.point - queryPoint;
144 const float separation = glm::length(delta);
145 normal = separation > 1e-7f ? delta / separation : -direction;
146 queryPoint += normal * query.contactOffset;
147 }
148 const float centerDistance = glm::dot(convex.point - queryPoint, normal);
149 return {convex.bary, queryPoint, normal, centerDistance - radiusAlong(simplex, particles, convex.bary, normal),
150 simplexIndex, queryIndex};
151}
152} // namespace
153
154Result<std::vector<VolumeFluidSimplexHit>> querySimplexes(std::span<const VolumeFluidParticle> particles,
155 std::span<const VolumeFluidSimplex> simplexes,
156 std::span<const VolumeFluidQueryShape> queries,
157 unsigned maxHitsPerQuery) {
159 const auto fail = [](const char* message) {
160 return Output::failure(
161 Diagnostic::error(DiagnosticCode::InvalidArgument, message, "fluids.volume.querySimplexes"));
162 };
163 const size_t simplexCount = simplexes.empty() ? particles.size() : simplexes.size();
164 if (queries.size() > 256 || maxHitsPerQuery == 0 || maxHitsPerQuery > 4096)
165 return fail("Invalid query count or hit limit");
166 if (simplexCount > 65536 || (!queries.empty() && simplexCount > 4000000u / queries.size()))
167 return fail("Simplex query exceeds candidate budget");
168 for (const auto& query : queries) {
169 if (unsigned(query.type) > unsigned(VolumeFluidQueryType::Ray) || !finite(query.center) ||
170 !finite(query.size) || !inRange(query.contactOffset, 0.f, 10000.f) ||
171 !inRange(query.maxDistance, 0.f, 10000.f) || query.phaseMask == 0 || (query.phaseMask & ~0x0fu) != 0 ||
172 !validFilter(query.collisionFilter))
173 return fail("Invalid simplex query");
174 if (query.type == VolumeFluidQueryType::Sphere &&
175 (!inRange(query.size.x, 0.f, 10000.f) || query.size.y != 0 || query.size.z != 0))
176 return fail("Invalid sphere query");
177 if (query.type == VolumeFluidQueryType::Box &&
178 (glm::any(glm::lessThan(query.size, glm::vec3(0))) ||
179 glm::any(glm::greaterThan(query.size, glm::vec3(20000))) || !finite(query.rotation) ||
180 std::abs(glm::dot(query.rotation, query.rotation) - 1.f) > .001f))
181 return fail("Invalid box query");
182 if (query.type == VolumeFluidQueryType::Ray && glm::length(query.size - query.center) <= 1e-7f)
183 return fail("Invalid ray query");
184 }
185 struct Farther {
186 bool operator()(const VolumeFluidSimplexHit& a, const VolumeFluidSimplexHit& b) const {
187 return a.distance != b.distance ? a.distance < b.distance : a.simplexIndex < b.simplexIndex;
188 }
189 };
190 std::vector<VolumeFluidSimplexHit> output;
191 output.reserve(std::min<size_t>(queries.size() * size_t(maxHitsPerQuery), 4000000));
192 std::vector<VolumeFluidSimplexHit> heap;
193 heap.reserve(maxHitsPerQuery);
194 const Farther farther;
195 for (size_t qi = 0; qi < queries.size(); ++qi) {
196 heap.clear();
197 for (size_t si = 0; si < simplexCount; ++si) {
198 const auto implicit = implicitPoint(unsigned(si));
199 const auto& simplex = simplexes.empty() ? implicit : simplexes[si];
200 if (!selected(simplex, particles, queries[qi])) continue;
201 auto hit = evaluate(simplex, unsigned(si), unsigned(qi), particles, queries[qi]);
202 if (hit.distance > queries[qi].maxDistance) continue;
203 if (heap.size() < maxHitsPerQuery) {
204 heap.push_back(hit);
205 std::push_heap(heap.begin(), heap.end(), farther);
206 } else if (hit.distance < heap.front().distance ||
207 (hit.distance == heap.front().distance && si < heap.front().simplexIndex)) {
208 std::pop_heap(heap.begin(), heap.end(), farther);
209 heap.back() = hit;
210 std::push_heap(heap.begin(), heap.end(), farther);
211 }
212 }
213 std::sort_heap(heap.begin(), heap.end(), farther);
214 output.insert(output.end(), heap.begin(), heap.end());
215 }
216 return Output::success(std::move(output));
217}
218} // namespace eve::fluids::detail
float w
Definition AnimClip.cpp:738
std::string output
float gap
Vec3 projected
Definition CaveMesh.cpp:122
float length
Definition CaveMesh.cpp:94
glm::vec4 p[6]
std::string message
std::array< double, 10 > q
std::uint32_t ab
std::uint32_t ac
float v
std::int32_t second
std::int32_t c
std::int32_t first
std::string local
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Texture * normal
bool finite
World3D * world
float radius
bool hit
float t
RoadLaneDirection direction
const RoadEdge * edge
std::string filter
std::uint16_t third
float separation
Definition TreeMesh.cpp:159
uint32_t index
glm::vec3 point
glm::vec4 bary
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
I * query()
Queries .
Definition Capability.h:88
Result< std::vector< VolumeFluidSimplexHit > > querySimplexes(std::span< const VolumeFluidParticle > particles, std::span< const VolumeFluidSimplex > simplexes, std::span< const VolumeFluidQueryShape > queries, unsigned maxHitsPerQuery)
Queries simplexes.
int axis(int64_t a, size_t rank)
Axis.
std::vector< OnnxNamedTensor > evaluate(const ModelData &, std::span< const OnnxNamedTensor >, const std::vector< std::string > &, OnnxCompute *, OnnxRunOptions options)
Evaluate.
Owning Fluid3D-compatible nearest result for one simplex and query pair.