1use std::collections::HashMap;
19
20#[inline]
25fn add3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
26 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
27}
28
29#[inline]
30fn sub3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
31 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
32}
33
34#[inline]
35fn scale3(a: [f64; 3], s: f64) -> [f64; 3] {
36 [a[0] * s, a[1] * s, a[2] * s]
37}
38
39#[inline]
40fn dot3(a: [f64; 3], b: [f64; 3]) -> f64 {
41 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
42}
43
44#[inline]
45fn cross3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
46 [
47 a[1] * b[2] - a[2] * b[1],
48 a[2] * b[0] - a[0] * b[2],
49 a[0] * b[1] - a[1] * b[0],
50 ]
51}
52
53#[inline]
54fn len3(a: [f64; 3]) -> f64 {
55 dot3(a, a).sqrt()
56}
57
58#[inline]
59fn normalize3(a: [f64; 3]) -> [f64; 3] {
60 let l = len3(a);
61 if l < 1e-14 {
62 [0.0, 0.0, 1.0]
63 } else {
64 scale3(a, 1.0 / l)
65 }
66}
67
68pub fn vertex_triangle_closest(
76 p: [f64; 3],
77 a: [f64; 3],
78 b: [f64; 3],
79 c: [f64; 3],
80) -> (f64, [f64; 3]) {
81 let ab = sub3(b, a);
82 let ac = sub3(c, a);
83 let ap = sub3(p, a);
84
85 let d1 = dot3(ab, ap);
86 let d2 = dot3(ac, ap);
87 if d1 <= 0.0 && d2 <= 0.0 {
88 let diff = sub3(p, a);
89 return (dot3(diff, diff), [1.0, 0.0, 0.0]);
90 }
91
92 let bp = sub3(p, b);
93 let d3 = dot3(ab, bp);
94 let d4 = dot3(ac, bp);
95 if d3 >= 0.0 && d4 <= d3 {
96 let diff = sub3(p, b);
97 return (dot3(diff, diff), [0.0, 1.0, 0.0]);
98 }
99
100 let vc = d1 * d4 - d3 * d2;
101 if vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0 {
102 let v = d1 / (d1 - d3);
103 let closest = add3(a, scale3(ab, v));
104 let diff = sub3(p, closest);
105 return (dot3(diff, diff), [1.0 - v, v, 0.0]);
106 }
107
108 let cp = sub3(p, c);
109 let d5 = dot3(ab, cp);
110 let d6 = dot3(ac, cp);
111 if d6 >= 0.0 && d5 <= d6 {
112 let diff = sub3(p, c);
113 return (dot3(diff, diff), [0.0, 0.0, 1.0]);
114 }
115
116 let vb = d5 * d2 - d1 * d6;
117 if vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0 {
118 let w = d2 / (d2 - d6);
119 let closest = add3(a, scale3(ac, w));
120 let diff = sub3(p, closest);
121 return (dot3(diff, diff), [1.0 - w, 0.0, w]);
122 }
123
124 let va = d3 * d6 - d5 * d4;
125 if va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0 {
126 let w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
127 let bc = sub3(c, b);
128 let closest = add3(b, scale3(bc, w));
129 let diff = sub3(p, closest);
130 return (dot3(diff, diff), [0.0, 1.0 - w, w]);
131 }
132
133 let denom = 1.0 / (va + vb + vc);
134 let v = vb * denom;
135 let w = vc * denom;
136 let closest = add3(add3(a, scale3(ab, v)), scale3(ac, w));
137 let diff = sub3(p, closest);
138 (dot3(diff, diff), [1.0 - v - w, v, w])
139}
140
141#[derive(Debug, Clone)]
143pub struct VertexTriangleContact {
144 pub vertex_index: usize,
146 pub triangle_index: usize,
148 pub normal: [f64; 3],
150 pub depth: f64,
152 pub bary: [f64; 3],
154}
155
156pub fn detect_vertex_triangle_contacts(
161 vertices: &[[f64; 3]],
162 triangles: &[[usize; 3]],
163 tri_positions: &[[f64; 3]],
164 thickness: f64,
165) -> Vec<VertexTriangleContact> {
166 let threshold_sq = thickness * thickness;
167 let mut contacts = Vec::new();
168
169 for (vi, &vp) in vertices.iter().enumerate() {
170 for (ti, &tri) in triangles.iter().enumerate() {
171 let a = tri_positions[tri[0]];
172 let b = tri_positions[tri[1]];
173 let c = tri_positions[tri[2]];
174
175 let (dist_sq, bary) = vertex_triangle_closest(vp, a, b, c);
176 if dist_sq < threshold_sq {
177 let closest = add3(
178 add3(scale3(a, bary[0]), scale3(b, bary[1])),
179 scale3(c, bary[2]),
180 );
181 let to_vert = sub3(vp, closest);
182 let dist = dist_sq.sqrt();
183 let normal = if dist > 1e-14 {
184 scale3(to_vert, 1.0 / dist)
185 } else {
186 let ab = sub3(b, a);
187 let ac = sub3(c, a);
188 normalize3(cross3(ab, ac))
189 };
190 contacts.push(VertexTriangleContact {
191 vertex_index: vi,
192 triangle_index: ti,
193 normal,
194 depth: thickness - dist,
195 bary,
196 });
197 }
198 }
199 }
200 contacts
201}
202
203pub struct ClothSpatialHash {
209 pub cell_size: f64,
211 cells: HashMap<(i64, i64, i64), Vec<usize>>,
213}
214
215impl ClothSpatialHash {
216 pub fn new(cell_size: f64) -> Self {
218 ClothSpatialHash {
219 cell_size,
220 cells: HashMap::new(),
221 }
222 }
223
224 fn cell_of(&self, p: [f64; 3]) -> (i64, i64, i64) {
225 (
226 (p[0] / self.cell_size).floor() as i64,
227 (p[1] / self.cell_size).floor() as i64,
228 (p[2] / self.cell_size).floor() as i64,
229 )
230 }
231
232 pub fn build(&mut self, positions: &[[f64; 3]]) {
234 self.cells.clear();
235 for (i, &p) in positions.iter().enumerate() {
236 let key = self.cell_of(p);
237 self.cells.entry(key).or_default().push(i);
238 }
239 }
240
241 pub fn candidate_pairs(&self) -> Vec<(usize, usize)> {
245 let mut pairs = Vec::new();
246 let all_keys: Vec<(i64, i64, i64)> = self.cells.keys().copied().collect();
247 for &(cx, cy, cz) in &all_keys {
248 for dx in -1_i64..=1 {
249 for dy in -1_i64..=1 {
250 for dz in -1_i64..=1 {
251 let neighbor = (cx + dx, cy + dy, cz + dz);
252 if let Some(other) = self.cells.get(&neighbor)
253 && let Some(this) = self.cells.get(&(cx, cy, cz))
254 {
255 for &i in this {
256 for &j in other {
257 if i < j {
258 pairs.push((i, j));
259 }
260 }
261 }
262 }
263 }
264 }
265 }
266 }
267 pairs.sort_unstable();
268 pairs.dedup();
269 pairs
270 }
271
272 pub fn detect_self_collisions(
276 &self,
277 positions: &[[f64; 3]],
278 radius: f64,
279 ) -> Vec<(usize, usize, [f64; 3], f64)> {
280 let candidates = self.candidate_pairs();
281 let mut contacts = Vec::new();
282 for (i, j) in candidates {
283 if i >= positions.len() || j >= positions.len() {
284 continue;
285 }
286 let diff = sub3(positions[i], positions[j]);
287 let dist = len3(diff);
288 if dist < radius && dist > 1e-14 {
289 let normal = scale3(diff, 1.0 / dist);
290 contacts.push((i, j, normal, radius - dist));
291 }
292 }
293 contacts
294 }
295}
296
297#[derive(Debug, Clone)]
303pub struct DeformableImpulse {
304 pub vertex_index: usize,
306 pub delta_v: [f64; 3],
308 pub correction: [f64; 3],
310}
311
312pub fn compute_contact_impulses(
316 contacts: &[VertexTriangleContact],
317 velocities: &[[f64; 3]],
318 masses: &[f64],
319 restitution: f64,
320 friction_coeff: f64,
321) -> Vec<DeformableImpulse> {
322 let mut impulses = Vec::new();
323 for c in contacts {
324 let vi = c.vertex_index;
325 if vi >= velocities.len() || vi >= masses.len() {
326 continue;
327 }
328 let v = velocities[vi];
329 let m = masses[vi];
330 if m <= 0.0 {
331 continue;
332 }
333
334 let v_normal = dot3(v, c.normal);
335 if v_normal >= 0.0 {
336 continue;
338 }
339
340 let j_n = -(1.0 + restitution) * v_normal * m;
342 let impulse_n = scale3(c.normal, j_n / m);
343
344 let v_tang = sub3(v, scale3(c.normal, v_normal));
346 let v_tang_len = len3(v_tang);
347 let impulse_t = if v_tang_len > 1e-14 {
348 let friction_mag = friction_coeff * j_n.abs() / m;
349 scale3(v_tang, -friction_mag.min(v_tang_len) / v_tang_len)
350 } else {
351 [0.0; 3]
352 };
353
354 let delta_v = add3(impulse_n, impulse_t);
355 let correction = scale3(c.normal, c.depth.max(0.0));
356
357 impulses.push(DeformableImpulse {
358 vertex_index: vi,
359 delta_v,
360 correction,
361 });
362 }
363 impulses
364}
365
366#[derive(Debug, Clone)]
372pub struct EdgeCcdResult {
373 pub edge_a: usize,
375 pub edge_b: usize,
377 pub toi: f64,
379 pub normal: [f64; 3],
381}
382
383pub fn edge_edge_ccd(
387 p0a: [f64; 3],
388 p1a: [f64; 3],
389 q0a: [f64; 3],
390 q1a: [f64; 3],
391 p0b: [f64; 3],
392 p1b: [f64; 3],
393 q0b: [f64; 3],
394 q1b: [f64; 3],
395 radius: f64,
396) -> Option<f64> {
397 let steps = 64;
399 let dt = 1.0 / steps as f64;
400 for step in 0..steps {
401 let t = step as f64 * dt;
402 let lerp =
403 |a: [f64; 3], b: [f64; 3]| -> [f64; 3] { add3(scale3(a, 1.0 - t), scale3(b, t)) };
404 let pa = lerp(p0a, p1a);
405 let qa = lerp(q0a, q1a);
406 let pb = lerp(p0b, p1b);
407 let qb = lerp(q0b, q1b);
408 let da = sub3(qa, pa);
409 let db = sub3(qb, pb);
410 let r = sub3(pa, pb);
411 let a = dot3(da, da);
412 let e = dot3(db, db);
413 let f = dot3(db, r);
414 let eps = 1e-10;
415 let (s, t2) = if a <= eps && e <= eps {
416 (0.0, 0.0)
417 } else if a <= eps {
418 (0.0, (f / e).clamp(0.0, 1.0))
419 } else {
420 let c = dot3(da, r);
421 if e <= eps {
422 ((-c / a).clamp(0.0, 1.0), 0.0)
423 } else {
424 let b = dot3(da, db);
425 let denom = a * e - b * b;
426 let ss = if denom.abs() > eps {
427 ((b * f - c * e) / denom).clamp(0.0, 1.0)
428 } else {
429 0.0
430 };
431 let tt = (b * ss + f) / e;
432 if tt < 0.0 {
433 ((-c / a).clamp(0.0, 1.0), 0.0)
434 } else if tt > 1.0 {
435 (((b - c) / a).clamp(0.0, 1.0), 1.0)
436 } else {
437 (ss, tt)
438 }
439 }
440 };
441 let closest_a = add3(pa, scale3(da, s));
442 let closest_b = add3(pb, scale3(db, t2));
443 let diff = sub3(closest_a, closest_b);
444 let dist_sq = dot3(diff, diff);
445 if dist_sq <= radius * radius {
446 return Some(t);
447 }
448 }
449 None
450}
451
452pub fn batch_edge_edge_ccd(
456 edges_a: &[(usize, usize)],
457 verts0_a: &[[f64; 3]],
458 verts1_a: &[[f64; 3]],
459 edges_b: &[(usize, usize)],
460 verts0_b: &[[f64; 3]],
461 verts1_b: &[[f64; 3]],
462 radius: f64,
463) -> Vec<EdgeCcdResult> {
464 let mut results = Vec::new();
465 for (ea_idx, &(ia0, ia1)) in edges_a.iter().enumerate() {
466 for (eb_idx, &(ib0, ib1)) in edges_b.iter().enumerate() {
467 if ia0 == ib0 || ia0 == ib1 || ia1 == ib0 || ia1 == ib1 {
468 continue; }
470 if ia0 >= verts0_a.len()
471 || ia1 >= verts0_a.len()
472 || ib0 >= verts0_b.len()
473 || ib1 >= verts0_b.len()
474 {
475 continue;
476 }
477 if let Some(toi) = edge_edge_ccd(
478 verts0_a[ia0],
479 verts1_a[ia0],
480 verts0_a[ia1],
481 verts1_a[ia1],
482 verts0_b[ib0],
483 verts1_b[ib0],
484 verts0_b[ib1],
485 verts1_b[ib1],
486 radius,
487 ) {
488 let lerp = |a: [f64; 3], b: [f64; 3]| add3(scale3(a, 1.0 - toi), scale3(b, toi));
490 let pa = lerp(verts0_a[ia0], verts1_a[ia0]);
491 let pb = lerp(verts0_b[ib0], verts1_b[ib0]);
492 let diff = sub3(pa, pb);
493 let normal = normalize3(diff);
494 results.push(EdgeCcdResult {
495 edge_a: ea_idx,
496 edge_b: eb_idx,
497 toi,
498 normal,
499 });
500 }
501 }
502 }
503 results
504}
505
506pub struct PenaltySpring {
512 pub stiffness: f64,
514 pub damping: f64,
516}
517
518impl PenaltySpring {
519 pub fn new(stiffness: f64, damping: f64) -> Self {
521 PenaltySpring { stiffness, damping }
522 }
523
524 pub fn force(&self, depth: f64, normal: [f64; 3], rel_velocity: [f64; 3]) -> [f64; 3] {
530 if depth <= 0.0 {
531 return [0.0; 3];
532 }
533 let v_n = dot3(rel_velocity, normal);
534 let f_mag = self.stiffness * depth - self.damping * v_n;
535 scale3(normal, f_mag.max(0.0))
536 }
537
538 pub fn apply_all(
542 &self,
543 contacts: &[VertexTriangleContact],
544 velocities: &[[f64; 3]],
545 ) -> Vec<(usize, [f64; 3])> {
546 let mut forces = Vec::new();
547 for c in contacts {
548 let vi = c.vertex_index;
549 let vel = if vi < velocities.len() {
550 velocities[vi]
551 } else {
552 [0.0; 3]
553 };
554 let f = self.force(c.depth, c.normal, vel);
555 forces.push((vi, f));
556 }
557 forces
558 }
559}
560
561#[derive(Debug, Clone)]
567pub struct BvhAabb {
568 pub min: [f64; 3],
570 pub max: [f64; 3],
572}
573
574impl BvhAabb {
575 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
577 BvhAabb { min, max }
578 }
579
580 pub fn from_triangle(a: [f64; 3], b: [f64; 3], c: [f64; 3], margin: f64) -> Self {
582 let min = [
583 a[0].min(b[0]).min(c[0]) - margin,
584 a[1].min(b[1]).min(c[1]) - margin,
585 a[2].min(b[2]).min(c[2]) - margin,
586 ];
587 let max = [
588 a[0].max(b[0]).max(c[0]) + margin,
589 a[1].max(b[1]).max(c[1]) + margin,
590 a[2].max(b[2]).max(c[2]) + margin,
591 ];
592 BvhAabb { min, max }
593 }
594
595 pub fn overlaps(&self, other: &BvhAabb) -> bool {
597 self.min[0] <= other.max[0]
598 && self.max[0] >= other.min[0]
599 && self.min[1] <= other.max[1]
600 && self.max[1] >= other.min[1]
601 && self.min[2] <= other.max[2]
602 && self.max[2] >= other.min[2]
603 }
604
605 pub fn merge(&self, other: &BvhAabb) -> BvhAabb {
607 BvhAabb {
608 min: [
609 self.min[0].min(other.min[0]),
610 self.min[1].min(other.min[1]),
611 self.min[2].min(other.min[2]),
612 ],
613 max: [
614 self.max[0].max(other.max[0]),
615 self.max[1].max(other.max[1]),
616 self.max[2].max(other.max[2]),
617 ],
618 }
619 }
620
621 pub fn surface_area(&self) -> f64 {
623 let dx = self.max[0] - self.min[0];
624 let dy = self.max[1] - self.min[1];
625 let dz = self.max[2] - self.min[2];
626 2.0 * (dx * dy + dy * dz + dz * dx)
627 }
628}
629
630#[derive(Debug, Clone)]
632pub struct BvhNode {
633 pub aabb: BvhAabb,
635 pub left: usize,
637 pub right: usize,
639 pub tri_index: usize,
641}
642
643impl BvhNode {
644 pub fn is_leaf(&self) -> bool {
646 self.left == usize::MAX
647 }
648}
649
650pub struct SoftBodyBvh {
652 pub nodes: Vec<BvhNode>,
654}
655
656impl SoftBodyBvh {
657 pub fn build(triangles: &[[usize; 3]], positions: &[[f64; 3]], margin: f64) -> Self {
661 let mut aabbs: Vec<(usize, BvhAabb)> = triangles
662 .iter()
663 .enumerate()
664 .map(|(i, &tri)| {
665 let a = positions[tri[0]];
666 let b = positions[tri[1]];
667 let c = positions[tri[2]];
668 (i, BvhAabb::from_triangle(a, b, c, margin))
669 })
670 .collect();
671
672 let mut nodes = Vec::new();
673 Self::build_recursive(&mut aabbs, &mut nodes);
674 SoftBodyBvh { nodes }
675 }
676
677 fn build_recursive(leaves: &mut [(usize, BvhAabb)], nodes: &mut Vec<BvhNode>) -> usize {
678 if leaves.is_empty() {
679 return usize::MAX;
680 }
681 if leaves.len() == 1 {
682 let idx = nodes.len();
683 nodes.push(BvhNode {
684 aabb: leaves[0].1.clone(),
685 left: usize::MAX,
686 right: usize::MAX,
687 tri_index: leaves[0].0,
688 });
689 return idx;
690 }
691
692 let union = leaves
694 .iter()
695 .fold(leaves[0].1.clone(), |acc, (_, b)| acc.merge(b));
696
697 let dx = union.max[0] - union.min[0];
699 let dy = union.max[1] - union.min[1];
700 let dz = union.max[2] - union.min[2];
701 let axis = if dx >= dy && dx >= dz {
702 0
703 } else if dy >= dz {
704 1
705 } else {
706 2
707 };
708 let mid_val = (union.min[axis] + union.max[axis]) / 2.0;
709
710 let pivot = leaves
711 .iter()
712 .position(|(_, b)| (b.min[axis] + b.max[axis]) / 2.0 >= mid_val)
713 .unwrap_or(leaves.len() / 2)
714 .max(1)
715 .min(leaves.len() - 1);
716
717 let (left_leaves, right_leaves) = leaves.split_at_mut(pivot);
718
719 let left_idx = Self::build_recursive(left_leaves, nodes);
720 let right_idx = Self::build_recursive(right_leaves, nodes);
721
722 let node_idx = nodes.len();
723 let left_aabb = if left_idx < nodes.len() {
724 nodes[left_idx].aabb.clone()
725 } else {
726 union.clone()
727 };
728 let right_aabb = if right_idx < nodes.len() {
729 nodes[right_idx].aabb.clone()
730 } else {
731 union.clone()
732 };
733 nodes.push(BvhNode {
734 aabb: left_aabb.merge(&right_aabb),
735 left: left_idx,
736 right: right_idx,
737 tri_index: usize::MAX,
738 });
739 node_idx
740 }
741
742 pub fn query_sphere(&self, center: [f64; 3], radius: f64) -> Vec<usize> {
744 let sphere_aabb = BvhAabb {
745 min: [center[0] - radius, center[1] - radius, center[2] - radius],
746 max: [center[0] + radius, center[1] + radius, center[2] + radius],
747 };
748 let mut result = Vec::new();
749 if self.nodes.is_empty() {
750 return result;
751 }
752 let root = self.nodes.len() - 1;
753 self.query_recursive(root, &sphere_aabb, &mut result);
754 result
755 }
756
757 fn query_recursive(&self, idx: usize, query: &BvhAabb, result: &mut Vec<usize>) {
758 if idx == usize::MAX || idx >= self.nodes.len() {
759 return;
760 }
761 let node = &self.nodes[idx];
762 if !node.aabb.overlaps(query) {
763 return;
764 }
765 if node.is_leaf() {
766 result.push(node.tri_index);
767 return;
768 }
769 self.query_recursive(node.left, query, result);
770 self.query_recursive(node.right, query, result);
771 }
772}
773
774#[derive(Debug, Clone)]
780pub struct SoftContact {
781 pub vertex_index: usize,
783 pub normal: [f64; 3],
785 pub depth: f64,
787 pub position: [f64; 3],
789}
790
791pub struct SoftContactManifold {
793 pub contacts: Vec<SoftContact>,
795 pub max_contacts: usize,
797}
798
799impl SoftContactManifold {
800 pub fn new(max_contacts: usize) -> Self {
802 SoftContactManifold {
803 contacts: Vec::new(),
804 max_contacts,
805 }
806 }
807
808 pub fn add(&mut self, contact: SoftContact) {
810 if self.contacts.len() < self.max_contacts {
811 self.contacts.push(contact);
812 } else {
813 if let Some(idx) = self
815 .contacts
816 .iter()
817 .enumerate()
818 .min_by(|a, b| {
819 a.1.depth
820 .partial_cmp(&b.1.depth)
821 .unwrap_or(std::cmp::Ordering::Equal)
822 })
823 .map(|(i, _)| i)
824 && self.contacts[idx].depth < contact.depth
825 {
826 self.contacts[idx] = contact;
827 }
828 }
829 }
830
831 pub fn clear(&mut self) {
833 self.contacts.clear();
834 }
835
836 pub fn deepest(&self) -> Option<&SoftContact> {
838 self.contacts.iter().max_by(|a, b| {
839 a.depth
840 .partial_cmp(&b.depth)
841 .unwrap_or(std::cmp::Ordering::Equal)
842 })
843 }
844}
845
846#[derive(Debug, Clone, PartialEq)]
852pub struct TearableEdge {
853 pub va: usize,
855 pub vb: usize,
857 pub stress: f64,
859 pub threshold: f64,
861}
862
863impl TearableEdge {
864 pub fn new(va: usize, vb: usize, threshold: f64) -> Self {
866 TearableEdge {
867 va,
868 vb,
869 stress: 0.0,
870 threshold,
871 }
872 }
873
874 pub fn should_tear(&self) -> bool {
876 self.stress >= self.threshold
877 }
878
879 pub fn accumulate_stress(&mut self, force_magnitude: f64) {
881 self.stress += force_magnitude;
882 }
883}
884
885pub fn propagate_tears(edges: &mut [TearableEdge]) -> Vec<usize> {
889 let mut torn = Vec::new();
890 for (i, edge) in edges.iter_mut().enumerate() {
891 if edge.should_tear() {
892 torn.push(i);
893 }
894 }
895 torn
896}
897
898pub fn apply_collision_stress(
903 edges: &mut [TearableEdge],
904 vertex_index: usize,
905 contact_depth: f64,
906 stress_factor: f64,
907) {
908 for edge in edges.iter_mut() {
909 if edge.va == vertex_index || edge.vb == vertex_index {
910 edge.accumulate_stress(contact_depth * stress_factor);
911 }
912 }
913}
914
915pub struct GpuClothBuffer {
924 pub pos_x: Vec<f32>,
926 pub pos_y: Vec<f32>,
928 pub pos_z: Vec<f32>,
930 pub vel_x: Vec<f32>,
932 pub vel_y: Vec<f32>,
934 pub vel_z: Vec<f32>,
936 pub inv_mass: Vec<f32>,
938}
939
940impl GpuClothBuffer {
941 pub fn from_vertices(positions: &[[f64; 3]], velocities: &[[f64; 3]], masses: &[f64]) -> Self {
943 let n = positions.len();
944 let mut buf = GpuClothBuffer {
945 pos_x: Vec::with_capacity(n),
946 pos_y: Vec::with_capacity(n),
947 pos_z: Vec::with_capacity(n),
948 vel_x: Vec::with_capacity(n),
949 vel_y: Vec::with_capacity(n),
950 vel_z: Vec::with_capacity(n),
951 inv_mass: Vec::with_capacity(n),
952 };
953 for &p in positions.iter() {
954 buf.pos_x.push(p[0] as f32);
955 buf.pos_y.push(p[1] as f32);
956 buf.pos_z.push(p[2] as f32);
957 }
958 for &v in velocities.iter() {
959 buf.vel_x.push(v[0] as f32);
960 buf.vel_y.push(v[1] as f32);
961 buf.vel_z.push(v[2] as f32);
962 }
963 for &m in masses {
964 buf.inv_mass
965 .push(if m > 0.0 { (1.0 / m) as f32 } else { 0.0 });
966 }
967 buf
968 }
969
970 pub fn len(&self) -> usize {
972 self.pos_x.len()
973 }
974
975 pub fn is_empty(&self) -> bool {
977 self.pos_x.is_empty()
978 }
979
980 pub fn position(&self, i: usize) -> [f64; 3] {
982 [
983 self.pos_x[i] as f64,
984 self.pos_y[i] as f64,
985 self.pos_z[i] as f64,
986 ]
987 }
988}
989
990pub struct GpuContactBuffer {
994 pub vertex_indices: Vec<u32>,
996 pub normal_x: Vec<f32>,
998 pub normal_y: Vec<f32>,
1000 pub normal_z: Vec<f32>,
1002 pub depths: Vec<f32>,
1004}
1005
1006impl GpuContactBuffer {
1007 pub fn new() -> Self {
1009 GpuContactBuffer {
1010 vertex_indices: Vec::new(),
1011 normal_x: Vec::new(),
1012 normal_y: Vec::new(),
1013 normal_z: Vec::new(),
1014 depths: Vec::new(),
1015 }
1016 }
1017
1018 pub fn from_contacts(contacts: &[VertexTriangleContact]) -> Self {
1020 let mut buf = Self::new();
1021 for c in contacts {
1022 buf.vertex_indices.push(c.vertex_index as u32);
1023 buf.normal_x.push(c.normal[0] as f32);
1024 buf.normal_y.push(c.normal[1] as f32);
1025 buf.normal_z.push(c.normal[2] as f32);
1026 buf.depths.push(c.depth as f32);
1027 }
1028 buf
1029 }
1030
1031 pub fn len(&self) -> usize {
1033 self.vertex_indices.len()
1034 }
1035
1036 pub fn is_empty(&self) -> bool {
1038 self.vertex_indices.is_empty()
1039 }
1040}
1041
1042impl Default for GpuContactBuffer {
1043 fn default() -> Self {
1044 Self::new()
1045 }
1046}
1047
1048pub struct GpuEdgeBuffer {
1050 pub start: Vec<u32>,
1052 pub end: Vec<u32>,
1054}
1055
1056impl GpuEdgeBuffer {
1057 pub fn from_edges(edges: &[(usize, usize)]) -> Self {
1059 let start = edges.iter().map(|&(a, _)| a as u32).collect();
1060 let end = edges.iter().map(|&(_, b)| b as u32).collect();
1061 GpuEdgeBuffer { start, end }
1062 }
1063
1064 pub fn len(&self) -> usize {
1066 self.start.len()
1067 }
1068
1069 pub fn is_empty(&self) -> bool {
1071 self.start.is_empty()
1072 }
1073}
1074
1075#[cfg(test)]
1080mod tests {
1081 use super::*;
1082
1083 const EPS: f64 = 1e-10;
1084
1085 #[test]
1088 fn test_vtc_vertex_above_centre() {
1089 let a = [0.0, 0.0, 0.0];
1090 let b = [1.0, 0.0, 0.0];
1091 let c = [0.0, 1.0, 0.0];
1092 let p = [0.25, 0.25, 1.0];
1093 let (sq_d, bary) = vertex_triangle_closest(p, a, b, c);
1094 assert!((sq_d - 1.0).abs() < 1e-8);
1096 assert!((bary[0] + bary[1] + bary[2] - 1.0).abs() < EPS);
1097 }
1098
1099 #[test]
1100 fn test_vtc_vertex_at_corner() {
1101 let a = [0.0, 0.0, 0.0];
1102 let b = [1.0, 0.0, 0.0];
1103 let c = [0.0, 1.0, 0.0];
1104 let (sq_d, _bary) = vertex_triangle_closest(a, a, b, c);
1105 assert!(sq_d.abs() < EPS);
1106 }
1107
1108 #[test]
1109 fn test_vtc_vertex_outside_edge() {
1110 let a = [0.0, 0.0, 0.0];
1112 let b = [1.0, 0.0, 0.0];
1113 let c = [0.0, 1.0, 0.0];
1114 let p = [0.5, -1.0, 0.0];
1115 let (sq_d, _) = vertex_triangle_closest(p, a, b, c);
1116 assert!((sq_d - 1.0).abs() < 1e-8);
1118 }
1119
1120 #[test]
1121 fn test_vtc_bary_sum_to_one() {
1122 let a = [0.0, 0.0, 0.0];
1123 let b = [2.0, 0.0, 0.0];
1124 let c = [0.0, 2.0, 0.0];
1125 let p = [0.5, 0.5, 0.5];
1126 let (_, bary) = vertex_triangle_closest(p, a, b, c);
1127 let sum = bary[0] + bary[1] + bary[2];
1128 assert!((sum - 1.0).abs() < 1e-8);
1129 }
1130
1131 #[test]
1132 fn test_vtc_distance_non_negative() {
1133 let a = [0.0, 0.0, 0.0];
1134 let b = [1.0, 0.0, 0.0];
1135 let c = [0.0, 1.0, 0.0];
1136 let p = [5.0, 5.0, 5.0];
1137 let (sq_d, _) = vertex_triangle_closest(p, a, b, c);
1138 assert!(sq_d >= 0.0);
1139 }
1140
1141 #[test]
1144 fn test_detect_contacts_finds_close_vertex() {
1145 let verts = vec![[0.25, 0.25, 0.05]];
1146 let tris = vec![[0, 1, 2]];
1147 let tri_pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1148 let contacts = detect_vertex_triangle_contacts(&verts, &tris, &tri_pos, 0.1);
1149 assert_eq!(contacts.len(), 1);
1150 assert!(contacts[0].depth > 0.0);
1151 }
1152
1153 #[test]
1154 fn test_detect_contacts_misses_far_vertex() {
1155 let verts = vec![[0.25, 0.25, 5.0]];
1156 let tris = vec![[0, 1, 2]];
1157 let tri_pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1158 let contacts = detect_vertex_triangle_contacts(&verts, &tris, &tri_pos, 0.1);
1159 assert_eq!(contacts.len(), 0);
1160 }
1161
1162 #[test]
1163 fn test_detect_contacts_normal_points_away_from_tri() {
1164 let verts = vec![[0.25, 0.25, 0.05]];
1165 let tris = vec![[0, 1, 2]];
1166 let tri_pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1167 let contacts = detect_vertex_triangle_contacts(&verts, &tris, &tri_pos, 0.1);
1168 assert_eq!(contacts.len(), 1);
1169 assert!(contacts[0].normal[2] > 0.0);
1171 }
1172
1173 #[test]
1176 fn test_spatial_hash_finds_nearby_pair() {
1177 let mut sh = ClothSpatialHash::new(0.5);
1178 let positions = vec![[0.0, 0.0, 0.0], [0.1, 0.0, 0.0], [10.0, 0.0, 0.0]];
1179 sh.build(&positions);
1180 let contacts = sh.detect_self_collisions(&positions, 0.3);
1181 assert_eq!(contacts.len(), 1);
1182 assert_eq!((contacts[0].0, contacts[0].1), (0, 1));
1183 }
1184
1185 #[test]
1186 fn test_spatial_hash_no_contacts_far_apart() {
1187 let mut sh = ClothSpatialHash::new(1.0);
1188 let positions = vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]];
1189 sh.build(&positions);
1190 let contacts = sh.detect_self_collisions(&positions, 0.3);
1191 assert_eq!(contacts.len(), 0);
1192 }
1193
1194 #[test]
1195 fn test_spatial_hash_contact_normal_unit_length() {
1196 let mut sh = ClothSpatialHash::new(0.5);
1197 let positions = vec![[0.0, 0.0, 0.0], [0.1, 0.0, 0.0]];
1198 sh.build(&positions);
1199 let contacts = sh.detect_self_collisions(&positions, 0.3);
1200 for (_, _, n, _) in &contacts {
1201 let l = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
1202 assert!((l - 1.0).abs() < 1e-8);
1203 }
1204 }
1205
1206 #[test]
1209 fn test_impulse_separates_penetrating_vertex() {
1210 let contacts = vec![VertexTriangleContact {
1211 vertex_index: 0,
1212 triangle_index: 0,
1213 normal: [0.0, 1.0, 0.0],
1214 depth: 0.05,
1215 bary: [0.33, 0.33, 0.34],
1216 }];
1217 let velocities = vec![[0.0, -1.0, 0.0]]; let masses = vec![1.0];
1219 let impulses = compute_contact_impulses(&contacts, &velocities, &masses, 0.0, 0.0);
1220 assert_eq!(impulses.len(), 1);
1221 assert!(impulses[0].delta_v[1] > 0.0); }
1223
1224 #[test]
1225 fn test_impulse_no_response_for_separating_vertex() {
1226 let contacts = vec![VertexTriangleContact {
1227 vertex_index: 0,
1228 triangle_index: 0,
1229 normal: [0.0, 1.0, 0.0],
1230 depth: 0.05,
1231 bary: [0.33, 0.33, 0.34],
1232 }];
1233 let velocities = vec![[0.0, 1.0, 0.0]]; let masses = vec![1.0];
1235 let impulses = compute_contact_impulses(&contacts, &velocities, &masses, 0.0, 0.0);
1236 assert_eq!(impulses.len(), 0);
1237 }
1238
1239 #[test]
1242 fn test_edge_ccd_detects_approaching_edges() {
1243 let toi = edge_edge_ccd(
1246 [0.0, 0.0, 0.0],
1247 [0.0, 0.0, 0.0],
1248 [1.0, 0.0, 0.0],
1249 [1.0, 0.0, 0.0],
1250 [0.5, 0.5, 0.0],
1251 [0.5, 0.02, 0.0],
1252 [0.5, 0.5, 0.5],
1253 [0.5, 0.02, 0.5],
1254 0.1,
1255 );
1256 assert!(toi.is_some());
1257 }
1258
1259 #[test]
1260 fn test_edge_ccd_no_collision_when_parallel() {
1261 let toi = edge_edge_ccd(
1263 [0.0, 0.0, 0.0],
1264 [0.0, 0.0, 0.0],
1265 [1.0, 0.0, 0.0],
1266 [1.0, 0.0, 0.0],
1267 [0.0, 10.0, 0.0],
1268 [0.0, 10.0, 0.0],
1269 [1.0, 10.0, 0.0],
1270 [1.0, 10.0, 0.0],
1271 0.1,
1272 );
1273 assert!(toi.is_none());
1274 }
1275
1276 #[test]
1279 fn test_penalty_force_zero_at_zero_depth() {
1280 let ps = PenaltySpring::new(1000.0, 10.0);
1281 let f = ps.force(0.0, [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]);
1282 assert!(f[0].abs() < EPS && f[1].abs() < EPS && f[2].abs() < EPS);
1283 }
1284
1285 #[test]
1286 fn test_penalty_force_positive_for_penetration() {
1287 let ps = PenaltySpring::new(1000.0, 0.0);
1288 let f = ps.force(0.01, [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]);
1289 assert!(f[1] > 0.0);
1290 }
1291
1292 #[test]
1293 fn test_penalty_force_direction_matches_normal() {
1294 let ps = PenaltySpring::new(500.0, 0.0);
1295 let normal = normalize3([1.0, 1.0, 0.0]);
1296 let f = ps.force(0.05, normal, [0.0, 0.0, 0.0]);
1297 let f_len = len3(f);
1298 if f_len > 1e-14 {
1299 let f_dir = scale3(f, 1.0 / f_len);
1300 assert!((f_dir[0] - normal[0]).abs() < 1e-6);
1301 assert!((f_dir[1] - normal[1]).abs() < 1e-6);
1302 }
1303 }
1304
1305 #[test]
1308 fn test_bvh_aabb_overlaps() {
1309 let a = BvhAabb::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
1310 let b = BvhAabb::new([0.5, 0.5, 0.5], [1.5, 1.5, 1.5]);
1311 assert!(a.overlaps(&b));
1312 }
1313
1314 #[test]
1315 fn test_bvh_aabb_no_overlap() {
1316 let a = BvhAabb::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
1317 let b = BvhAabb::new([2.0, 2.0, 2.0], [3.0, 3.0, 3.0]);
1318 assert!(!a.overlaps(&b));
1319 }
1320
1321 #[test]
1322 fn test_bvh_aabb_merge() {
1323 let a = BvhAabb::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
1324 let b = BvhAabb::new([2.0, 2.0, 2.0], [3.0, 3.0, 3.0]);
1325 let merged = a.merge(&b);
1326 assert_eq!(merged.min, [0.0, 0.0, 0.0]);
1327 assert_eq!(merged.max, [3.0, 3.0, 3.0]);
1328 }
1329
1330 #[test]
1333 fn test_bvh_build_and_query() {
1334 let positions = vec![
1335 [0.0, 0.0, 0.0],
1336 [1.0, 0.0, 0.0],
1337 [0.0, 1.0, 0.0],
1338 [5.0, 5.0, 5.0],
1339 [6.0, 5.0, 5.0],
1340 [5.0, 6.0, 5.0],
1341 ];
1342 let triangles = vec![[0, 1, 2], [3, 4, 5]];
1343 let bvh = SoftBodyBvh::build(&triangles, &positions, 0.01);
1344 let hits = bvh.query_sphere([0.25, 0.25, 0.0], 0.5);
1345 assert!(!hits.is_empty());
1346 }
1347
1348 #[test]
1349 fn test_bvh_query_misses_distant_triangle() {
1350 let positions = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1351 let triangles = vec![[0, 1, 2]];
1352 let bvh = SoftBodyBvh::build(&triangles, &positions, 0.01);
1353 let hits = bvh.query_sphere([100.0, 100.0, 100.0], 0.5);
1354 assert_eq!(hits.len(), 0);
1355 }
1356
1357 #[test]
1360 fn test_manifold_add_and_deepest() {
1361 let mut manifold = SoftContactManifold::new(4);
1362 manifold.add(SoftContact {
1363 vertex_index: 0,
1364 normal: [0.0, 1.0, 0.0],
1365 depth: 0.1,
1366 position: [0.0, 0.0, 0.0],
1367 });
1368 manifold.add(SoftContact {
1369 vertex_index: 1,
1370 normal: [0.0, 1.0, 0.0],
1371 depth: 0.5,
1372 position: [1.0, 0.0, 0.0],
1373 });
1374 let deepest = manifold.deepest().unwrap();
1375 assert_eq!(deepest.vertex_index, 1);
1376 }
1377
1378 #[test]
1379 fn test_manifold_capacity_limit() {
1380 let mut manifold = SoftContactManifold::new(2);
1381 for i in 0..5 {
1382 manifold.add(SoftContact {
1383 vertex_index: i,
1384 normal: [0.0, 1.0, 0.0],
1385 depth: i as f64 * 0.1,
1386 position: [0.0, 0.0, 0.0],
1387 });
1388 }
1389 assert!(manifold.contacts.len() <= 2);
1390 }
1391
1392 #[test]
1393 fn test_manifold_clear() {
1394 let mut manifold = SoftContactManifold::new(4);
1395 manifold.add(SoftContact {
1396 vertex_index: 0,
1397 normal: [0.0, 1.0, 0.0],
1398 depth: 0.1,
1399 position: [0.0, 0.0, 0.0],
1400 });
1401 manifold.clear();
1402 assert!(manifold.contacts.is_empty());
1403 }
1404
1405 #[test]
1408 fn test_edge_tears_when_stress_exceeded() {
1409 let mut edge = TearableEdge::new(0, 1, 1.0);
1410 edge.accumulate_stress(1.5);
1411 assert!(edge.should_tear());
1412 }
1413
1414 #[test]
1415 fn test_edge_does_not_tear_below_threshold() {
1416 let mut edge = TearableEdge::new(0, 1, 1.0);
1417 edge.accumulate_stress(0.5);
1418 assert!(!edge.should_tear());
1419 }
1420
1421 #[test]
1422 fn test_propagate_tears_returns_torn_indices() {
1423 let mut edges = vec![TearableEdge::new(0, 1, 1.0), TearableEdge::new(1, 2, 1.0)];
1424 edges[0].stress = 2.0;
1425 edges[1].stress = 0.1;
1426 let torn = propagate_tears(&mut edges);
1427 assert_eq!(torn, vec![0]);
1428 }
1429
1430 #[test]
1431 fn test_apply_collision_stress() {
1432 let mut edges = vec![TearableEdge::new(0, 1, 10.0), TearableEdge::new(2, 3, 10.0)];
1433 apply_collision_stress(&mut edges, 0, 0.5, 2.0);
1434 assert!((edges[0].stress - 1.0).abs() < EPS);
1436 assert!(edges[1].stress.abs() < EPS);
1438 }
1439
1440 #[test]
1443 fn test_gpu_cloth_buffer_len() {
1444 let pos = vec![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
1445 let vel = vec![[0.0; 3]; 2];
1446 let mass = vec![1.0, 2.0];
1447 let buf = GpuClothBuffer::from_vertices(&pos, &vel, &mass);
1448 assert_eq!(buf.len(), 2);
1449 }
1450
1451 #[test]
1452 fn test_gpu_cloth_buffer_position_roundtrip() {
1453 let pos = vec![[1.5, -2.5, 3.0]];
1454 let vel = vec![[0.0; 3]];
1455 let mass = vec![1.0];
1456 let buf = GpuClothBuffer::from_vertices(&pos, &vel, &mass);
1457 let p = buf.position(0);
1458 assert!((p[0] - 1.5).abs() < 1e-5);
1459 assert!((p[1] - (-2.5)).abs() < 1e-5);
1460 assert!((p[2] - 3.0).abs() < 1e-5);
1461 }
1462
1463 #[test]
1464 fn test_gpu_cloth_buffer_pinned_vertex() {
1465 let pos = vec![[0.0; 3]];
1466 let vel = vec![[0.0; 3]];
1467 let mass = vec![0.0]; let buf = GpuClothBuffer::from_vertices(&pos, &vel, &mass);
1469 assert_eq!(buf.inv_mass[0], 0.0);
1470 }
1471
1472 #[test]
1475 fn test_gpu_contact_buffer_from_contacts() {
1476 let contacts = vec![VertexTriangleContact {
1477 vertex_index: 3,
1478 triangle_index: 0,
1479 normal: [0.0, 1.0, 0.0],
1480 depth: 0.05,
1481 bary: [0.33, 0.33, 0.34],
1482 }];
1483 let buf = GpuContactBuffer::from_contacts(&contacts);
1484 assert_eq!(buf.len(), 1);
1485 assert_eq!(buf.vertex_indices[0], 3);
1486 assert!((buf.depths[0] - 0.05).abs() < 1e-5);
1487 }
1488
1489 #[test]
1490 fn test_gpu_contact_buffer_empty() {
1491 let buf = GpuContactBuffer::new();
1492 assert!(buf.is_empty());
1493 }
1494
1495 #[test]
1498 fn test_gpu_edge_buffer() {
1499 let edges = vec![(0, 1), (1, 2), (2, 0)];
1500 let buf = GpuEdgeBuffer::from_edges(&edges);
1501 assert_eq!(buf.len(), 3);
1502 assert_eq!(buf.start[0], 0);
1503 assert_eq!(buf.end[2], 0);
1504 }
1505
1506 #[test]
1509 fn test_batch_ccd_empty_edges() {
1510 let results = batch_edge_edge_ccd(&[], &[], &[], &[], &[], &[], 0.01);
1511 assert!(results.is_empty());
1512 }
1513
1514 #[test]
1515 fn test_batch_ccd_skips_shared_vertices() {
1516 let edges_a = vec![(0, 1)];
1518 let edges_b = vec![(0, 2)];
1519 let v0 = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1520 let v1 = v0.clone();
1521 let results = batch_edge_edge_ccd(&edges_a, &v0, &v1, &edges_b, &v0, &v1, 0.01);
1522 assert!(results.is_empty());
1523 }
1524}