载入中...
搜索中...
未找到
FluidSurfaceBinding.cpp
浏览该文件的文档.
2
3#include <glm/gtx/norm.hpp>
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <unordered_map>
9
10namespace eve::fluids {
11namespace {
12
13struct EdgeKey {
14 uint32_t a = 0;
15 uint32_t b = 0;
16
17 bool operator==(const EdgeKey& rhs) const { return a == rhs.a && b == rhs.b; }
18};
19
20struct EdgeHash {
21 size_t operator()(const EdgeKey& edge) const {
22 const size_t first = std::hash<uint32_t>{}(edge.a);
23 const size_t second = std::hash<uint32_t>{}(edge.b);
24 return first ^ (second + size_t(0x9e3779b9u) + (first << 6u) + (first >> 2u));
25 }
26};
27
28struct EdgeOwner {
29 int triangle = -1;
31};
32
33EdgeKey edgeKey(uint32_t a, uint32_t b) { return {std::min(a, b), std::max(a, b)}; }
34
35glm::vec3 closestPointBarycentric(const glm::vec3& p, const glm::vec3& a, const glm::vec3& b,
36 const glm::vec3& c) {
37 const glm::vec3 ab = b - a;
38 const glm::vec3 ac = c - a;
39 const glm::vec3 ap = p - a;
40 const float d1 = glm::dot(ab, ap);
41 const float d2 = glm::dot(ac, ap);
42 if (d1 <= 0.f && d2 <= 0.f) return {1.f, 0.f, 0.f};
43
44 const glm::vec3 bp = p - b;
45 const float d3 = glm::dot(ab, bp);
46 const float d4 = glm::dot(ac, bp);
47 if (d3 >= 0.f && d4 <= d3) return {0.f, 1.f, 0.f};
48
49 const float vc = d1 * d4 - d3 * d2;
50 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
51 const float v = d1 / (d1 - d3);
52 return {1.f - v, v, 0.f};
53 }
54
55 const glm::vec3 cp = p - c;
56 const float d5 = glm::dot(ab, cp);
57 const float d6 = glm::dot(ac, cp);
58 if (d6 >= 0.f && d5 <= d6) return {0.f, 0.f, 1.f};
59
60 const float vb = d5 * d2 - d1 * d6;
61 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
62 const float w = d2 / (d2 - d6);
63 return {1.f - w, 0.f, w};
64 }
65
66 const float va = d3 * d6 - d5 * d4;
67 if (va <= 0.f && d4 - d3 >= 0.f && d5 - d6 >= 0.f) {
68 const float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
69 return {0.f, 1.f - w, w};
70 }
71
72 const float inv = 1.f / (va + vb + vc);
73 const float v = vb * inv;
74 const float w = vc * inv;
75 return {1.f - v - w, v, w};
76}
77
78} // namespace
79
80bool FluidSurfaceBinding::build(const std::vector<glm::vec3>& positions,
81 const std::vector<uint32_t>& indices,
82 const std::vector<glm::vec2>& uvs) {
83 restPositions_.clear();
84 currentPositions_.clear();
85 previousPositions_.clear();
86 indices_.clear();
87 uvs_.clear();
88 adjacency_.clear();
89 const auto fail = [&]() {
90 restPositions_.clear();
91 currentPositions_.clear();
92 previousPositions_.clear();
93 indices_.clear();
94 uvs_.clear();
95 adjacency_.clear();
96 return false;
97 };
98 if (positions.empty() || indices.empty() || indices.size() % 3u != 0u) return false;
99 if (!uvs.empty() && uvs.size() != positions.size()) return false;
100 for (uint32_t index : indices) if (index >= positions.size()) return false;
101
102 restPositions_ = positions;
103 currentPositions_ = positions;
104 previousPositions_ = positions;
105 indices_ = indices;
106 uvs_ = uvs;
107 adjacency_.assign(indices.size() / 3u, glm::ivec3(-1));
108
109 std::unordered_map<EdgeKey, std::vector<EdgeOwner>, EdgeHash> edges;
110 for (int tri = 0; tri < triangleCount(); ++tri) {
111 const uint32_t base = uint32_t(tri) * 3u;
112 const glm::vec3 ab = positions[indices_[base + 1u]] - positions[indices_[base]];
113 const glm::vec3 ac = positions[indices_[base + 2u]] - positions[indices_[base]];
114 if (glm::length2(glm::cross(ab, ac)) < 1e-16f) return fail();
115 for (int opposite = 0; opposite < 3; ++opposite) {
116 const uint32_t a = indices_[base + uint32_t((opposite + 1) % 3)];
117 const uint32_t b = indices_[base + uint32_t((opposite + 2) % 3)];
118 const EdgeKey key = edgeKey(a, b);
119 auto& owners = edges[key];
120 owners.push_back({tri, opposite});
121 if (owners.size() > 2u) return fail();
122 }
123 }
124 for (const auto& [key, owners] : edges) {
125 (void)key;
126 if (owners.size() != 2u) continue;
127 adjacency_[size_t(owners[0].triangle)][owners[0].oppositeVertex] = owners[1].triangle;
128 adjacency_[size_t(owners[1].triangle)][owners[1].oppositeVertex] = owners[0].triangle;
129 }
130 return true;
131}
132
133void FluidSurfaceBinding::setTransform(const glm::mat4& transform) {
134 if (!isValid()) return;
135 previousPositions_ = currentPositions_;
136 for (size_t i = 0; i < restPositions_.size(); ++i)
137 currentPositions_[i] = glm::vec3(transform * glm::vec4(restPositions_[i], 1.f));
138}
139
140bool FluidSurfaceBinding::setDeformedPositions(const std::vector<glm::vec3>& worldPositions) {
141 if (!isValid() || worldPositions.size() != currentPositions_.size()) return false;
142 previousPositions_ = currentPositions_;
143 currentPositions_ = worldPositions;
144 return true;
145}
146
147void FluidSurfaceBinding::commitPose() { previousPositions_ = currentPositions_; }
148
150 return !currentPositions_.empty() && currentPositions_.size() == previousPositions_.size() &&
151 indices_.size() >= 3u && indices_.size() % 3u == 0u;
152}
153
154int FluidSurfaceBinding::triangleCount() const { return int(indices_.size() / 3u); }
155
156std::vector<glm::uvec3> FluidSurfaceBinding::triangles() const {
157 std::vector<glm::uvec3> result;
158 result.reserve(size_t(triangleCount()));
159 for (size_t i = 0; i + 2u < indices_.size(); i += 3u)
160 result.emplace_back(indices_[i], indices_[i + 1u], indices_[i + 2u]);
161 return result;
162}
163
164glm::vec3 FluidSurfaceBinding::triangleNormal(uint32_t triangle) const {
165 const uint32_t base = triangle * 3u;
166 const glm::vec3& a = currentPositions_[indices_[base]];
167 const glm::vec3& b = currentPositions_[indices_[base + 1u]];
168 const glm::vec3& c = currentPositions_[indices_[base + 2u]];
169 const glm::vec3 n = glm::cross(b - a, c - a);
170 const float n2 = glm::length2(n);
171 return n2 > 1e-16f ? n / std::sqrt(n2) : glm::vec3(0.f, 1.f, 0.f);
172}
173
174glm::vec3 FluidSurfaceBinding::barycentric(uint32_t triangle, const glm::vec3& point) const {
175 const uint32_t base = triangle * 3u;
176 const glm::vec3& a = currentPositions_[indices_[base]];
177 const glm::vec3& b = currentPositions_[indices_[base + 1u]];
178 const glm::vec3& c = currentPositions_[indices_[base + 2u]];
179 const glm::vec3 v0 = b - a;
180 const glm::vec3 v1 = c - a;
181 const glm::vec3 v2 = point - a;
182 const float d00 = glm::dot(v0, v0);
183 const float d01 = glm::dot(v0, v1);
184 const float d11 = glm::dot(v1, v1);
185 const float d20 = glm::dot(v2, v0);
186 const float d21 = glm::dot(v2, v1);
187 const float denom = d00 * d11 - d01 * d01;
188 if (std::fabs(denom) < 1e-16f) return {1.f, 0.f, 0.f};
189 const float v = (d11 * d20 - d01 * d21) / denom;
190 const float w = (d00 * d21 - d01 * d20) / denom;
191 return {1.f - v - w, v, w};
192}
193
195 SurfaceSample sample;
196 if (!isValid() || location.triangle >= uint32_t(triangleCount())) return sample;
197 sample.location = location;
198 const glm::vec3 bary = location.barycentric;
199 const uint32_t base = location.triangle * 3u;
200 for (int i = 0; i < 3; ++i) {
201 const uint32_t vertex = indices_[base + uint32_t(i)];
202 sample.position += currentPositions_[vertex] * bary[i];
203 sample.previousPosition += previousPositions_[vertex] * bary[i];
204 if (!uvs_.empty()) sample.uv += uvs_[vertex] * bary[i];
205 }
206 sample.normal = triangleNormal(location.triangle);
207 const glm::vec3 edge = currentPositions_[indices_[base + 1u]] - currentPositions_[indices_[base]];
208 const glm::vec3 tangent = edge - sample.normal * glm::dot(edge, sample.normal);
209 const float t2 = glm::length2(tangent);
210 sample.tangent = t2 > 1e-16f ? tangent / std::sqrt(t2) : glm::vec3(1.f, 0.f, 0.f);
211 sample.bitangent = glm::normalize(glm::cross(sample.normal, sample.tangent));
212 if (dt > 1e-8f) sample.velocity = (sample.position - sample.previousPosition) / dt;
213 return sample;
214}
215
216bool FluidSurfaceBinding::project(const glm::vec3& worldPosition, float maxDistance,
217 SurfaceLocation& outLocation) const {
218 if (!isValid()) return false;
219 float best = std::numeric_limits<float>::max();
220 SurfaceLocation location;
221 for (int tri = 0; tri < triangleCount(); ++tri) {
222 const uint32_t base = uint32_t(tri) * 3u;
223 const glm::vec3 bary = closestPointBarycentric(worldPosition,
224 currentPositions_[indices_[base]], currentPositions_[indices_[base + 1u]],
225 currentPositions_[indices_[base + 2u]]);
226 const glm::vec3 point = currentPositions_[indices_[base]] * bary.x +
227 currentPositions_[indices_[base + 1u]] * bary.y +
228 currentPositions_[indices_[base + 2u]] * bary.z;
229 const float distance2 = glm::distance2(worldPosition, point);
230 if (distance2 < best) {
231 best = distance2;
232 location = {uint32_t(tri), bary};
233 }
234 }
235 if (maxDistance >= 0.f && best > maxDistance * maxDistance) return false;
236 outLocation = location;
237 return true;
238}
239
241 const glm::vec3& worldDisplacement,
242 int maxCrossings) const {
243 SurfaceWalkResult result;
244 if (!isValid() || start.triangle >= uint32_t(triangleCount()) || maxCrossings < 0) return result;
245 result.location = start;
246 result.valid = true;
247 glm::vec3 remaining = worldDisplacement;
248 constexpr float epsilon = 1e-5f;
249
250 for (int crossing = 0; crossing <= maxCrossings; ++crossing) {
251 const SurfaceSample sample = evaluate(result.location, 0.f);
252 remaining -= sample.normal * glm::dot(remaining, sample.normal);
253 if (glm::length2(remaining) < epsilon * epsilon) {
254 result.remainingDisplacement = glm::vec3(0.f);
255 return result;
256 }
257 const glm::vec3 targetBary = barycentric(result.location.triangle, sample.position + remaining);
258 int outside = -1;
259 float mostNegative = -epsilon;
260 for (int i = 0; i < 3; ++i) {
261 if (targetBary[i] < mostNegative) {
262 mostNegative = targetBary[i];
263 outside = i;
264 }
265 }
266 if (outside < 0) {
267 result.location.barycentric = glm::max(targetBary, glm::vec3(0.f));
268 result.location.barycentric /= result.location.barycentric.x + result.location.barycentric.y +
269 result.location.barycentric.z;
270 result.remainingDisplacement = glm::vec3(0.f);
271 return result;
272 }
273
274 const float from = result.location.barycentric[outside];
275 const float to = targetBary[outside];
276 const float t = std::clamp(from / (from - to), 0.f, 1.f);
277 glm::vec3 edgeBary = glm::mix(result.location.barycentric, targetBary, t);
278 edgeBary[outside] = 0.f;
279 edgeBary = glm::max(edgeBary, glm::vec3(0.f));
280 edgeBary /= edgeBary.x + edgeBary.y + edgeBary.z;
281 remaining *= 1.f - t;
282
283 const int next = adjacentTriangle(result.location.triangle, outside);
284 if (next < 0) {
285 result.location.barycentric = edgeBary;
286 result.remainingDisplacement = remaining;
287 result.reachedBoundary = true;
288 return result;
289 }
290
291 const SurfaceSample edgeSample = evaluate({result.location.triangle, edgeBary}, 0.f);
292 result.location.triangle = uint32_t(next);
293 result.location.barycentric = barycentric(uint32_t(next), edgeSample.position);
294 result.location.barycentric = glm::max(result.location.barycentric, glm::vec3(0.f));
295 result.location.barycentric /= result.location.barycentric.x + result.location.barycentric.y +
296 result.location.barycentric.z;
297 if (crossing == maxCrossings) {
298 result.remainingDisplacement = remaining;
299 return result;
300 }
301 }
302 return result;
303}
304
306 if (triangle >= adjacency_.size() || oppositeVertex < 0 || oppositeVertex > 2) return -1;
307 return adjacency_[triangle][oppositeVertex];
308}
309
310} // namespace eve::fluids
Duration start
float w
Definition AnimClip.cpp:738
std::string from
Vec3 tangent
Definition CaveMesh.cpp:80
glm::vec4 p[6]
int oppositeVertex
int triangle
std::uint32_t key
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::uint32_t ab
std::uint32_t ac
std::vector< std::uint32_t > indices
std::vector< float > positions
float v
std::int32_t second
std::int32_t c
std::int32_t first
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float t
const RoadEdge * edge
std::map< Cell, int > best
uint32_t index
std::vector< int > edges
glm::vec3 point
glm::vec4 bary
bool build(const std::vector< glm::vec3 > &positions, const std::vector< uint32_t > &indices, const std::vector< glm::vec2 > &uvs={})
Build topology and initialize the current and previous poses.
void setTransform(const glm::mat4 &transform)
Apply a new rigid pose and preserve the old pose for velocity queries.
SurfaceSample evaluate(const SurfaceLocation &location, float dt) const
Evaluate a surface address in the current and previous poses.
bool isValid() const
True when valid.
bool setDeformedPositions(const std::vector< glm::vec3 > &worldPositions)
Supply a new deformed world-space pose and preserve the previous pose.
int adjacentTriangle(uint32_t triangle, int oppositeVertex) const
Adjacent triangle.
int triangleCount() const
Triangle count.
SurfaceWalkResult walkAcrossSurface(const SurfaceLocation &start, const glm::vec3 &worldDisplacement, int maxCrossings=16) const
Move a location by a world-space displacement, crossing triangle edges.
std::vector< glm::uvec3 > triangles() const
Triangles.
void commitPose()
Make the current pose the previous pose, yielding zero surface velocity.
bool project(const glm::vec3 &worldPosition, float maxDistance, SurfaceLocation &outLocation) const
Find the closest material point on the current surface.
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
Stable material-space address of a point on a triangle surface.
Evaluated world-space frame and motion at a surface location.
Result of walking a material point across triangle adjacency.