1#[inline]
16pub fn vec3_sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
17 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
18}
19
20#[inline]
22pub fn vec3_add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
23 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
24}
25
26#[inline]
28pub fn vec3_scale(a: [f64; 3], s: f64) -> [f64; 3] {
29 [a[0] * s, a[1] * s, a[2] * s]
30}
31
32#[inline]
34pub fn vec3_dot(a: [f64; 3], b: [f64; 3]) -> f64 {
35 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
36}
37
38#[inline]
40pub fn vec3_cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
41 [
42 a[1] * b[2] - a[2] * b[1],
43 a[2] * b[0] - a[0] * b[2],
44 a[0] * b[1] - a[1] * b[0],
45 ]
46}
47
48#[inline]
50pub fn vec3_len_sq(a: [f64; 3]) -> f64 {
51 vec3_dot(a, a)
52}
53
54#[inline]
56pub fn vec3_len(a: [f64; 3]) -> f64 {
57 vec3_len_sq(a).sqrt()
58}
59
60#[inline]
62pub fn vec3_norm(a: [f64; 3]) -> [f64; 3] {
63 let l = vec3_len(a);
64 if l < 1e-30 {
65 [0.0; 3]
66 } else {
67 vec3_scale(a, 1.0 / l)
68 }
69}
70
71#[inline]
73pub fn vec3_min(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
74 [a[0].min(b[0]), a[1].min(b[1]), a[2].min(b[2])]
75}
76
77#[inline]
79pub fn vec3_max(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
80 [a[0].max(b[0]), a[1].max(b[1]), a[2].max(b[2])]
81}
82
83#[inline]
85pub fn vec3_lerp(a: [f64; 3], b: [f64; 3], t: f64) -> [f64; 3] {
86 vec3_add(vec3_scale(a, 1.0 - t), vec3_scale(b, t))
87}
88
89#[derive(Clone, Debug, PartialEq)]
95pub struct MeshAabb {
96 pub min: [f64; 3],
98 pub max: [f64; 3],
100}
101
102impl MeshAabb {
103 pub fn empty() -> Self {
105 Self {
106 min: [f64::INFINITY; 3],
107 max: [f64::NEG_INFINITY; 3],
108 }
109 }
110
111 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
113 Self { min, max }
114 }
115
116 pub fn expand_point(&mut self, p: [f64; 3]) {
118 self.min = vec3_min(self.min, p);
119 self.max = vec3_max(self.max, p);
120 }
121
122 pub fn expand_aabb(&mut self, other: &MeshAabb) {
124 self.min = vec3_min(self.min, other.min);
125 self.max = vec3_max(self.max, other.max);
126 }
127
128 pub fn centre(&self) -> [f64; 3] {
130 vec3_scale(vec3_add(self.min, self.max), 0.5)
131 }
132
133 pub fn half_extents(&self) -> [f64; 3] {
135 vec3_scale(vec3_sub(self.max, self.min), 0.5)
136 }
137
138 pub fn surface_area(&self) -> f64 {
140 let d = vec3_sub(self.max, self.min);
141 2.0 * (d[0] * d[1] + d[1] * d[2] + d[2] * d[0])
142 }
143
144 pub fn overlaps(&self, other: &MeshAabb) -> bool {
146 self.min[0] <= other.max[0]
147 && self.max[0] >= other.min[0]
148 && self.min[1] <= other.max[1]
149 && self.max[1] >= other.min[1]
150 && self.min[2] <= other.max[2]
151 && self.max[2] >= other.min[2]
152 }
153
154 pub fn contains_point(&self, p: [f64; 3]) -> bool {
156 p[0] >= self.min[0]
157 && p[0] <= self.max[0]
158 && p[1] >= self.min[1]
159 && p[1] <= self.max[1]
160 && p[2] >= self.min[2]
161 && p[2] <= self.max[2]
162 }
163
164 pub fn from_triangle(a: [f64; 3], b: [f64; 3], c: [f64; 3]) -> Self {
166 let mut aabb = Self::empty();
167 aabb.expand_point(a);
168 aabb.expand_point(b);
169 aabb.expand_point(c);
170 aabb
171 }
172
173 pub fn fattened(&self, margin: f64) -> Self {
175 Self {
176 min: [
177 self.min[0] - margin,
178 self.min[1] - margin,
179 self.min[2] - margin,
180 ],
181 max: [
182 self.max[0] + margin,
183 self.max[1] + margin,
184 self.max[2] + margin,
185 ],
186 }
187 }
188}
189
190#[derive(Clone, Debug)]
196pub struct Triangle {
197 pub v0: [f64; 3],
199 pub v1: [f64; 3],
201 pub v2: [f64; 3],
203}
204
205impl Triangle {
206 pub fn new(v0: [f64; 3], v1: [f64; 3], v2: [f64; 3]) -> Self {
208 Self { v0, v1, v2 }
209 }
210
211 pub fn normal(&self) -> [f64; 3] {
213 let e1 = vec3_sub(self.v1, self.v0);
214 let e2 = vec3_sub(self.v2, self.v0);
215 vec3_norm(vec3_cross(e1, e2))
216 }
217
218 pub fn area(&self) -> f64 {
220 let e1 = vec3_sub(self.v1, self.v0);
221 let e2 = vec3_sub(self.v2, self.v0);
222 vec3_len(vec3_cross(e1, e2)) * 0.5
223 }
224
225 pub fn centroid(&self) -> [f64; 3] {
227 [
228 (self.v0[0] + self.v1[0] + self.v2[0]) / 3.0,
229 (self.v0[1] + self.v1[1] + self.v2[1]) / 3.0,
230 (self.v0[2] + self.v1[2] + self.v2[2]) / 3.0,
231 ]
232 }
233
234 pub fn aabb(&self) -> MeshAabb {
236 MeshAabb::from_triangle(self.v0, self.v1, self.v2)
237 }
238
239 pub fn barycentric_point(&self, u: f64, v: f64) -> [f64; 3] {
241 let w = 1.0 - u - v;
242 [
243 w * self.v0[0] + u * self.v1[0] + v * self.v2[0],
244 w * self.v0[1] + u * self.v1[1] + v * self.v2[1],
245 w * self.v0[2] + u * self.v1[2] + v * self.v2[2],
246 ]
247 }
248}
249
250#[derive(Clone, Debug)]
256pub struct RayTriHit {
257 pub t: f64,
259 pub u: f64,
261 pub v: f64,
263}
264
265pub fn ray_triangle_mt(
270 origin: [f64; 3],
271 direction: [f64; 3],
272 tri: &Triangle,
273 t_min: f64,
274 t_max: f64,
275) -> Option<RayTriHit> {
276 const EPS: f64 = 1e-10;
277 let e1 = vec3_sub(tri.v1, tri.v0);
278 let e2 = vec3_sub(tri.v2, tri.v0);
279 let h = vec3_cross(direction, e2);
280 let a = vec3_dot(e1, h);
281 if a.abs() < EPS {
282 return None;
283 }
284 let f = 1.0 / a;
285 let s = vec3_sub(origin, tri.v0);
286 let u = f * vec3_dot(s, h);
287 if !(0.0..=1.0).contains(&u) {
288 return None;
289 }
290 let q = vec3_cross(s, e1);
291 let v = f * vec3_dot(direction, q);
292 if v < 0.0 || u + v > 1.0 {
293 return None;
294 }
295 let t = f * vec3_dot(e2, q);
296 if t < t_min || t > t_max {
297 return None;
298 }
299 Some(RayTriHit { t, u, v })
300}
301
302pub fn triangle_triangle_intersect(t1: &Triangle, t2: &Triangle) -> bool {
310 let n1 = t1.normal();
312 let d1 = -vec3_dot(n1, t1.v0);
313
314 let dv20 = vec3_dot(n1, t2.v0) + d1;
316 let dv21 = vec3_dot(n1, t2.v1) + d1;
317 let dv22 = vec3_dot(n1, t2.v2) + d1;
318
319 if dv20 * dv21 > 0.0 && dv20 * dv22 > 0.0 {
321 return false;
322 }
323
324 let n2 = t2.normal();
326 let d2 = -vec3_dot(n2, t2.v0);
327
328 let dv10 = vec3_dot(n2, t1.v0) + d2;
329 let dv11 = vec3_dot(n2, t1.v1) + d2;
330 let dv12 = vec3_dot(n2, t1.v2) + d2;
331
332 if dv10 * dv11 > 0.0 && dv10 * dv12 > 0.0 {
333 return false;
334 }
335
336 let d = vec3_cross(n1, n2);
338
339 let abs_d = [d[0].abs(), d[1].abs(), d[2].abs()];
341 let axis = if abs_d[0] >= abs_d[1] && abs_d[0] >= abs_d[2] {
342 0
343 } else if abs_d[1] >= abs_d[2] {
344 1
345 } else {
346 2
347 };
348
349 let p10 = t1.v0[axis];
351 let p11 = t1.v1[axis];
352 let p12 = t1.v2[axis];
353
354 let i1 = compute_interval(p10, p11, p12, dv10, dv11, dv12);
356
357 let p20 = t2.v0[axis];
359 let p21 = t2.v1[axis];
360 let p22 = t2.v2[axis];
361
362 let i2 = compute_interval(p20, p21, p22, dv20, dv21, dv22);
363
364 !(i1.1 < i2.0 || i2.1 < i1.0)
366}
367
368fn compute_interval(p0: f64, p1: f64, p2: f64, d0: f64, d1: f64, d2: f64) -> (f64, f64) {
369 let lerp = |pa: f64, pb: f64, da: f64, db: f64| -> f64 { pa + (pb - pa) * da / (da - db) };
371 if d0 * d1 > 0.0 {
372 let t1 = lerp(p0, p2, d0, d2);
374 let t2 = lerp(p1, p2, d1, d2);
375 (t1.min(t2), t1.max(t2))
376 } else if d0 * d2 > 0.0 {
377 let t1 = lerp(p0, p1, d0, d1);
378 let t2 = lerp(p2, p1, d2, d1);
379 (t1.min(t2), t1.max(t2))
380 } else {
381 let t1 = lerp(p1, p0, d1, d0);
382 let t2 = lerp(p2, p0, d2, d0);
383 (t1.min(t2), t1.max(t2))
384 }
385}
386
387#[derive(Clone, Debug)]
393pub struct BvhNode {
394 pub aabb: MeshAabb,
396 pub left: usize,
398 pub right: usize,
400 pub tri_idx: usize,
402 pub is_leaf: bool,
404}
405
406impl BvhNode {
407 pub fn leaf(aabb: MeshAabb, tri_idx: usize) -> Self {
409 Self {
410 aabb,
411 left: usize::MAX,
412 right: usize::MAX,
413 tri_idx,
414 is_leaf: true,
415 }
416 }
417
418 pub fn internal(aabb: MeshAabb, left: usize, right: usize) -> Self {
420 Self {
421 aabb,
422 left,
423 right,
424 tri_idx: usize::MAX,
425 is_leaf: false,
426 }
427 }
428}
429
430pub struct TriMeshBvh {
436 pub triangles: Vec<Triangle>,
438 pub nodes: Vec<BvhNode>,
440 pub root: usize,
442}
443
444impl TriMeshBvh {
445 pub fn build(triangles: Vec<Triangle>) -> Self {
447 let n = triangles.len();
448 let mut nodes: Vec<BvhNode> = Vec::with_capacity(2 * n);
449 let indices: Vec<usize> = (0..n).collect();
450 let root = Self::build_recursive(&triangles, &indices, &mut nodes);
451 Self {
452 triangles,
453 nodes,
454 root,
455 }
456 }
457
458 fn build_recursive(tris: &[Triangle], indices: &[usize], nodes: &mut Vec<BvhNode>) -> usize {
459 let mut aabb = MeshAabb::empty();
461 for &i in indices {
462 aabb.expand_aabb(&tris[i].aabb());
463 }
464
465 if indices.len() == 1 {
466 let node = BvhNode::leaf(aabb, indices[0]);
467 let idx = nodes.len();
468 nodes.push(node);
469 return idx;
470 }
471
472 let ext = vec3_sub(aabb.max, aabb.min);
474 let axis = if ext[0] >= ext[1] && ext[0] >= ext[2] {
475 0
476 } else if ext[1] >= ext[2] {
477 1
478 } else {
479 2
480 };
481
482 let mut sorted: Vec<usize> = indices.to_vec();
484 sorted.sort_by(|&a, &b| {
485 tris[a].centroid()[axis]
486 .partial_cmp(&tris[b].centroid()[axis])
487 .unwrap_or(std::cmp::Ordering::Equal)
488 });
489
490 let mid = sorted.len() / 2;
491 let left = Self::build_recursive(tris, &sorted[..mid], nodes);
492 let right = Self::build_recursive(tris, &sorted[mid..], nodes);
493
494 let node = BvhNode::internal(aabb, left, right);
495 let idx = nodes.len();
496 nodes.push(node);
497 idx
498 }
499
500 pub fn ray_cast(
502 &self,
503 origin: [f64; 3],
504 direction: [f64; 3],
505 t_max: f64,
506 ) -> Option<(RayTriHit, usize)> {
507 let mut best: Option<(RayTriHit, usize)> = None;
508 let mut stack = vec![self.root];
509 let mut t_cur = t_max;
510
511 while let Some(node_idx) = stack.pop() {
512 if node_idx >= self.nodes.len() {
513 continue;
514 }
515 let node = &self.nodes[node_idx];
516 if !ray_aabb_intersect(origin, direction, &node.aabb, 0.0, t_cur) {
518 continue;
519 }
520 if node.is_leaf {
521 let tri = &self.triangles[node.tri_idx];
522 if let Some(hit) = ray_triangle_mt(origin, direction, tri, 0.0, t_cur) {
523 t_cur = hit.t;
524 best = Some((hit, node.tri_idx));
525 }
526 } else {
527 stack.push(node.left);
528 stack.push(node.right);
529 }
530 }
531 best
532 }
533
534 pub fn query_aabb(&self, query: &MeshAabb) -> Vec<usize> {
536 let mut result = Vec::new();
537 let mut stack = vec![self.root];
538 while let Some(node_idx) = stack.pop() {
539 if node_idx >= self.nodes.len() {
540 continue;
541 }
542 let node = &self.nodes[node_idx];
543 if !node.aabb.overlaps(query) {
544 continue;
545 }
546 if node.is_leaf {
547 result.push(node.tri_idx);
548 } else {
549 stack.push(node.left);
550 stack.push(node.right);
551 }
552 }
553 result
554 }
555
556 pub fn refit(&mut self) {
558 Self::refit_recursive(&self.triangles, &mut self.nodes, self.root);
559 }
560
561 fn refit_recursive(tris: &[Triangle], nodes: &mut Vec<BvhNode>, node_idx: usize) {
562 if node_idx >= nodes.len() {
563 return;
564 }
565 let is_leaf = nodes[node_idx].is_leaf;
566 if is_leaf {
567 let tri_idx = nodes[node_idx].tri_idx;
568 nodes[node_idx].aabb = tris[tri_idx].aabb();
569 return;
570 }
571 let left = nodes[node_idx].left;
572 let right = nodes[node_idx].right;
573 Self::refit_recursive(tris, nodes, left);
574 Self::refit_recursive(tris, nodes, right);
575 let mut aabb = nodes[left].aabb.clone();
576 aabb.expand_aabb(&nodes[right].aabb.clone());
577 nodes[node_idx].aabb = aabb;
578 }
579
580 pub fn num_triangles(&self) -> usize {
582 self.triangles.len()
583 }
584
585 pub fn num_nodes(&self) -> usize {
587 self.nodes.len()
588 }
589}
590
591pub fn ray_aabb_intersect(
593 origin: [f64; 3],
594 direction: [f64; 3],
595 aabb: &MeshAabb,
596 t_min: f64,
597 t_max: f64,
598) -> bool {
599 let mut tmin = t_min;
600 let mut tmax = t_max;
601 for i in 0..3 {
602 let inv = if direction[i].abs() > 1e-30 {
603 1.0 / direction[i]
604 } else {
605 f64::INFINITY
606 };
607 let t1 = (aabb.min[i] - origin[i]) * inv;
608 let t2 = (aabb.max[i] - origin[i]) * inv;
609 let (lo, hi) = if t1 < t2 { (t1, t2) } else { (t2, t1) };
610 tmin = tmin.max(lo);
611 tmax = tmax.min(hi);
612 if tmax < tmin {
613 return false;
614 }
615 }
616 true
617}
618
619#[derive(Clone, Debug)]
625pub struct TriPair {
626 pub tri_a: usize,
628 pub tri_b: usize,
630}
631
632pub fn mesh_broad_phase(a: &TriMeshBvh, b: &TriMeshBvh) -> Vec<TriPair> {
634 let mut pairs = Vec::new();
635 mesh_broad_phase_recursive(a, b, a.root, b.root, &mut pairs);
636 pairs
637}
638
639fn mesh_broad_phase_recursive(
640 a: &TriMeshBvh,
641 b: &TriMeshBvh,
642 na: usize,
643 nb: usize,
644 pairs: &mut Vec<TriPair>,
645) {
646 if na >= a.nodes.len() || nb >= b.nodes.len() {
647 return;
648 }
649 if !a.nodes[na].aabb.overlaps(&b.nodes[nb].aabb) {
650 return;
651 }
652
653 let a_leaf = a.nodes[na].is_leaf;
654 let b_leaf = b.nodes[nb].is_leaf;
655
656 match (a_leaf, b_leaf) {
657 (true, true) => {
658 pairs.push(TriPair {
659 tri_a: a.nodes[na].tri_idx,
660 tri_b: b.nodes[nb].tri_idx,
661 });
662 }
663 (false, true) => {
664 mesh_broad_phase_recursive(a, b, a.nodes[na].left, nb, pairs);
665 mesh_broad_phase_recursive(a, b, a.nodes[na].right, nb, pairs);
666 }
667 (true, false) => {
668 mesh_broad_phase_recursive(a, b, na, b.nodes[nb].left, pairs);
669 mesh_broad_phase_recursive(a, b, na, b.nodes[nb].right, pairs);
670 }
671 (false, false) => {
672 let sa = a.nodes[na].aabb.surface_area();
674 let sb = b.nodes[nb].aabb.surface_area();
675 if sa >= sb {
676 mesh_broad_phase_recursive(a, b, a.nodes[na].left, nb, pairs);
677 mesh_broad_phase_recursive(a, b, a.nodes[na].right, nb, pairs);
678 } else {
679 mesh_broad_phase_recursive(a, b, na, b.nodes[nb].left, pairs);
680 mesh_broad_phase_recursive(a, b, na, b.nodes[nb].right, pairs);
681 }
682 }
683 }
684}
685
686pub fn mesh_narrow_phase(a: &TriMeshBvh, b: &TriMeshBvh, pairs: &[TriPair]) -> Vec<TriPair> {
688 pairs
689 .iter()
690 .filter(|p| triangle_triangle_intersect(&a.triangles[p.tri_a], &b.triangles[p.tri_b]))
691 .cloned()
692 .collect()
693}
694
695pub fn mesh_mesh_collision(a: &TriMeshBvh, b: &TriMeshBvh) -> Vec<TriPair> {
697 let candidates = mesh_broad_phase(a, b);
698 mesh_narrow_phase(a, b, &candidates)
699}
700
701#[derive(Clone, Debug)]
707pub struct ClothVertex {
708 pub pos: [f64; 3],
710 pub prev_pos: [f64; 3],
712 pub vel: [f64; 3],
714 pub mass: f64,
716 pub pinned: bool,
718}
719
720impl ClothVertex {
721 pub fn new(pos: [f64; 3], mass: f64) -> Self {
723 Self {
724 pos,
725 prev_pos: pos,
726 vel: [0.0; 3],
727 mass,
728 pinned: false,
729 }
730 }
731}
732
733#[derive(Clone, Debug)]
735pub struct ClothEdge {
736 pub i: usize,
738 pub j: usize,
740 pub rest_length: f64,
742 pub stiffness: f64,
744}
745
746impl ClothEdge {
747 pub fn new(i: usize, j: usize, rest_length: f64, stiffness: f64) -> Self {
749 Self {
750 i,
751 j,
752 rest_length,
753 stiffness,
754 }
755 }
756
757 pub fn project(&self, verts: &mut [ClothVertex]) {
759 if verts[self.i].pinned && verts[self.j].pinned {
760 return;
761 }
762 let pi = verts[self.i].pos;
763 let pj = verts[self.j].pos;
764 let diff = vec3_sub(pi, pj);
765 let l = vec3_len(diff);
766 if l < 1e-15 {
767 return;
768 }
769 let correction = self.stiffness * (l - self.rest_length) / l;
770 let delta = vec3_scale(diff, correction);
771 let wi = if verts[self.i].pinned {
772 0.0
773 } else {
774 1.0 / verts[self.i].mass
775 };
776 let wj = if verts[self.j].pinned {
777 0.0
778 } else {
779 1.0 / verts[self.j].mass
780 };
781 let w_sum = wi + wj;
782 if w_sum < 1e-30 {
783 return;
784 }
785 if !verts[self.i].pinned {
786 let c = vec3_scale(delta, -wi / w_sum);
787 verts[self.i].pos = vec3_add(verts[self.i].pos, c);
788 }
789 if !verts[self.j].pinned {
790 let c = vec3_scale(delta, wj / w_sum);
791 verts[self.j].pos = vec3_add(verts[self.j].pos, c);
792 }
793 }
794}
795
796pub fn cloth_self_collision(
801 vertices: &mut [ClothVertex],
802 triangles: &[[usize; 3]],
803 thickness: f64,
804) -> usize {
805 let mut n_collisions = 0;
806 let n = vertices.len();
807 for i in 0..n {
808 for j in (i + 1)..n {
809 let shared = triangles.iter().any(|t| {
811 (t[0] == i || t[1] == i || t[2] == i) && (t[0] == j || t[1] == j || t[2] == j)
812 });
813 if shared {
814 continue;
815 }
816 let diff = vec3_sub(vertices[i].pos, vertices[j].pos);
817 let dist = vec3_len(diff);
818 if dist < thickness && dist > 1e-15 {
819 let push = vec3_scale(vec3_norm(diff), (thickness - dist) * 0.5);
820 if !vertices[i].pinned {
821 vertices[i].pos = vec3_add(vertices[i].pos, push);
822 }
823 if !vertices[j].pinned {
824 vertices[j].pos = vec3_sub(vertices[j].pos, push);
825 }
826 n_collisions += 1;
827 }
828 }
829 }
830 n_collisions
831}
832
833#[derive(Clone, Debug)]
839pub struct CcdHit {
840 pub toi: f64,
842 pub normal: [f64; 3],
844 pub u: f64,
846 pub v: f64,
848}
849
850pub fn vertex_triangle_ccd(
857 vp0: [f64; 3],
858 vp1: [f64; 3], tp0: [f64; 3],
860 tp1: [f64; 3], tq0: [f64; 3],
862 tq1: [f64; 3], tr0: [f64; 3],
864 tr1: [f64; 3], ) -> Option<CcdHit> {
866 let v_at = |t: f64| vec3_lerp(vp0, vp1, t);
868 let p_at = |t: f64| vec3_lerp(tp0, tp1, t);
869 let q_at = |t: f64| vec3_lerp(tq0, tq1, t);
870 let r_at = |t: f64| vec3_lerp(tr0, tr1, t);
871
872 const N: usize = 64;
874 let mut prev_side: Option<f64> = None;
875
876 for step in 0..=N {
877 let t = step as f64 / N as f64;
878 let v = v_at(t);
879 let p = p_at(t);
880 let q = q_at(t);
881 let r = r_at(t);
882 let n = vec3_cross(vec3_sub(q, p), vec3_sub(r, p));
883 let dist = vec3_dot(vec3_sub(v, p), n);
884 if let Some(prev) = prev_side
885 && prev * dist < 0.0
886 {
887 let tri = Triangle::new(p, q, r);
889 let normal = vec3_norm(n);
890 let bary = point_triangle_barycentric(v, &tri);
892 if bary[0] >= 0.0 && bary[1] >= 0.0 && bary[2] >= 0.0 {
893 let toi = (step as f64 - 1.0) / N as f64;
894 return Some(CcdHit {
895 toi,
896 normal,
897 u: bary[0],
898 v: bary[1],
899 });
900 }
901 }
902 prev_side = Some(dist);
903 }
904 None
905}
906
907pub fn point_triangle_barycentric(p: [f64; 3], tri: &Triangle) -> [f64; 3] {
909 let v0 = vec3_sub(tri.v1, tri.v0);
910 let v1 = vec3_sub(tri.v2, tri.v0);
911 let v2 = vec3_sub(p, tri.v0);
912 let d00 = vec3_dot(v0, v0);
913 let d01 = vec3_dot(v0, v1);
914 let d11 = vec3_dot(v1, v1);
915 let d20 = vec3_dot(v2, v0);
916 let d21 = vec3_dot(v2, v1);
917 let denom = d00 * d11 - d01 * d01;
918 if denom.abs() < 1e-30 {
919 return [1.0 / 3.0; 3];
920 }
921 let v = (d11 * d20 - d01 * d21) / denom;
922 let w = (d00 * d21 - d01 * d20) / denom;
923 let u = 1.0 - v - w;
924 [v, w, u]
925}
926
927pub fn point_triangle_dist_sq(p: [f64; 3], tri: &Triangle) -> f64 {
933 let ab = vec3_sub(tri.v1, tri.v0);
934 let ac = vec3_sub(tri.v2, tri.v0);
935 let ap = vec3_sub(p, tri.v0);
936
937 let d1 = vec3_dot(ab, ap);
938 let d2 = vec3_dot(ac, ap);
939 if d1 <= 0.0 && d2 <= 0.0 {
940 return vec3_len_sq(ap);
941 }
942
943 let bp = vec3_sub(p, tri.v1);
944 let d3 = vec3_dot(ab, bp);
945 let d4 = vec3_dot(ac, bp);
946 if d3 >= 0.0 && d4 <= d3 {
947 return vec3_len_sq(bp);
948 }
949
950 let cp = vec3_sub(p, tri.v2);
951 let d5 = vec3_dot(ab, cp);
952 let d6 = vec3_dot(ac, cp);
953 if d6 >= 0.0 && d5 <= d6 {
954 return vec3_len_sq(cp);
955 }
956
957 let vc = d1 * d4 - d3 * d2;
958 if vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0 {
959 let v = d1 / (d1 - d3);
960 let proj = vec3_add(tri.v0, vec3_scale(ab, v));
961 return vec3_len_sq(vec3_sub(p, proj));
962 }
963
964 let vb = d5 * d2 - d1 * d6;
965 if vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0 {
966 let w = d2 / (d2 - d6);
967 let proj = vec3_add(tri.v0, vec3_scale(ac, w));
968 return vec3_len_sq(vec3_sub(p, proj));
969 }
970
971 let va = d3 * d6 - d5 * d4;
972 if va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0 {
973 let w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
974 let proj = vec3_add(tri.v1, vec3_scale(vec3_sub(tri.v2, tri.v1), w));
975 return vec3_len_sq(vec3_sub(p, proj));
976 }
977
978 let denom = 1.0 / (va + vb + vc);
979 let v = vb * denom;
980 let w = vc * denom;
981 let proj = vec3_add(tri.v0, vec3_add(vec3_scale(ab, v), vec3_scale(ac, w)));
982 vec3_len_sq(vec3_sub(p, proj))
983}
984
985pub fn closest_point_on_triangle(p: [f64; 3], tri: &Triangle) -> [f64; 3] {
987 let bary = point_triangle_barycentric(p, tri);
988 let u = bary[0].clamp(0.0, 1.0);
989 let v = bary[1].clamp(0.0, 1.0);
990 let w_total = u + v;
991 let (u, v) = if w_total > 1.0 {
992 (u / w_total, v / w_total)
993 } else {
994 (u, v)
995 };
996 tri.barycentric_point(u, v)
997}
998
999pub fn build_proximity_field(
1001 mesh: &[Triangle],
1002 origin: [f64; 3],
1003 nx: usize,
1004 ny: usize,
1005 nz: usize,
1006 dx: f64,
1007) -> Vec<f64> {
1008 let total = nx * ny * nz;
1009 let mut field = vec![f64::INFINITY; total];
1010 for iz in 0..nz {
1011 for iy in 0..ny {
1012 for ix in 0..nx {
1013 let p = [
1014 origin[0] + ix as f64 * dx,
1015 origin[1] + iy as f64 * dx,
1016 origin[2] + iz as f64 * dx,
1017 ];
1018 let mut min_d2 = f64::INFINITY;
1019 for tri in mesh {
1020 let d2 = point_triangle_dist_sq(p, tri);
1021 if d2 < min_d2 {
1022 min_d2 = d2;
1023 }
1024 }
1025 field[ix * ny * nz + iy * nz + iz] = min_d2.sqrt();
1026 }
1027 }
1028 }
1029 field
1030}
1031
1032pub fn swept_triangle_vs_mesh(
1040 moving_tri: &Triangle,
1041 displacement: [f64; 3],
1042 static_mesh: &TriMeshBvh,
1043) -> Option<f64> {
1044 let mut swept_aabb = moving_tri.aabb();
1046 let tri_end = Triangle::new(
1047 vec3_add(moving_tri.v0, displacement),
1048 vec3_add(moving_tri.v1, displacement),
1049 vec3_add(moving_tri.v2, displacement),
1050 );
1051 swept_aabb.expand_aabb(&tri_end.aabb());
1052
1053 let candidates = static_mesh.query_aabb(&swept_aabb);
1055 if candidates.is_empty() {
1056 return None;
1057 }
1058
1059 let mut earliest = f64::INFINITY;
1060
1061 for &tri_idx in &candidates {
1062 let static_tri = &static_mesh.triangles[tri_idx];
1063 for &mv in &[moving_tri.v0, moving_tri.v1, moving_tri.v2] {
1065 let mv_end = vec3_add(mv, displacement);
1066 let hit = vertex_triangle_ccd(
1067 mv,
1068 mv_end,
1069 static_tri.v0,
1070 static_tri.v0,
1071 static_tri.v1,
1072 static_tri.v1,
1073 static_tri.v2,
1074 static_tri.v2,
1075 );
1076 if let Some(h) = hit
1077 && h.toi < earliest
1078 {
1079 earliest = h.toi;
1080 }
1081 }
1082 }
1083
1084 if earliest.is_finite() {
1085 Some(earliest)
1086 } else {
1087 None
1088 }
1089}
1090
1091pub fn penetration_depth_estimate(a: &TriMeshBvh, b: &TriMeshBvh, n_samples: usize) -> f64 {
1098 let pairs = mesh_mesh_collision(a, b);
1099 if pairs.is_empty() {
1100 return 0.0;
1101 }
1102
1103 let mut max_depth = 0.0_f64;
1104 for pair in &pairs {
1105 let tri_a = &a.triangles[pair.tri_a];
1106 let tri_b = &b.triangles[pair.tri_b];
1107 for k in 0..n_samples {
1109 let t = k as f64 / (n_samples as f64 - 1.0).max(1.0);
1110 let u = t * 0.5;
1111 let v = (1.0 - t) * 0.5;
1112 let p = tri_a.barycentric_point(u, v);
1113 let d2 = point_triangle_dist_sq(p, tri_b);
1114 if d2 > max_depth {
1115 max_depth = d2;
1116 }
1117 }
1118 }
1119 max_depth.sqrt()
1120}
1121
1122#[derive(Clone, Debug)]
1128pub struct MeshContact {
1129 pub position: [f64; 3],
1131 pub normal: [f64; 3],
1133 pub depth: f64,
1135 pub tri_a: usize,
1137 pub tri_b: usize,
1139}
1140
1141#[derive(Clone, Debug, Default)]
1143pub struct MeshContactManifold {
1144 pub contacts: Vec<MeshContact>,
1146 pub avg_normal: [f64; 3],
1148 pub max_depth: f64,
1150}
1151
1152impl MeshContactManifold {
1153 pub fn new() -> Self {
1155 Self::default()
1156 }
1157
1158 pub fn add_contact(&mut self, c: MeshContact) {
1160 self.max_depth = self.max_depth.max(c.depth);
1161 self.contacts.push(c);
1162 self.update_avg_normal();
1163 }
1164
1165 fn update_avg_normal(&mut self) {
1166 if self.contacts.is_empty() {
1167 return;
1168 }
1169 let mut n = [0.0; 3];
1170 for c in &self.contacts {
1171 n = vec3_add(n, c.normal);
1172 }
1173 self.avg_normal = vec3_norm(n);
1174 }
1175
1176 pub fn num_contacts(&self) -> usize {
1178 self.contacts.len()
1179 }
1180}
1181
1182pub fn generate_mesh_contact_manifold(a: &TriMeshBvh, b: &TriMeshBvh) -> MeshContactManifold {
1184 let pairs = mesh_mesh_collision(a, b);
1185 let mut manifold = MeshContactManifold::new();
1186
1187 for pair in &pairs {
1188 let tri_a = &a.triangles[pair.tri_a];
1189 let tri_b = &b.triangles[pair.tri_b];
1190
1191 let na = tri_a.normal();
1192 let nb = tri_b.normal();
1193
1194 let normal = vec3_norm(vec3_sub(na, nb));
1196
1197 let ca = tri_a.centroid();
1199 let cb = tri_b.centroid();
1200 let position = vec3_scale(vec3_add(ca, cb), 0.5);
1201
1202 let n2 = tri_b.normal();
1204 let d = vec3_dot(n2, vec3_sub(ca, tri_b.v0)).abs();
1205
1206 manifold.add_contact(MeshContact {
1207 position,
1208 normal,
1209 depth: d,
1210 tri_a: pair.tri_a,
1211 tri_b: pair.tri_b,
1212 });
1213 }
1214 manifold
1215}
1216
1217pub struct DeformableMesh {
1223 pub vertices: Vec<[f64; 3]>,
1225 pub face_indices: Vec<[usize; 3]>,
1227 pub bvh: TriMeshBvh,
1229}
1230
1231impl DeformableMesh {
1232 pub fn build(vertices: Vec<[f64; 3]>, face_indices: Vec<[usize; 3]>) -> Self {
1234 let triangles: Vec<Triangle> = face_indices
1235 .iter()
1236 .map(|&[a, b, c]| Triangle::new(vertices[a], vertices[b], vertices[c]))
1237 .collect();
1238 let bvh = TriMeshBvh::build(triangles);
1239 Self {
1240 vertices,
1241 face_indices,
1242 bvh,
1243 }
1244 }
1245
1246 pub fn update_vertex(&mut self, idx: usize, new_pos: [f64; 3]) {
1248 self.vertices[idx] = new_pos;
1249 for (fi, &[a, b, c]) in self.face_indices.iter().enumerate() {
1251 if a == idx || b == idx || c == idx {
1252 self.bvh.triangles[fi] =
1253 Triangle::new(self.vertices[a], self.vertices[b], self.vertices[c]);
1254 }
1255 }
1256 self.bvh.refit();
1257 }
1258
1259 pub fn apply_displacement(&mut self, displacements: &[[f64; 3]]) {
1261 for (i, d) in displacements.iter().enumerate() {
1262 if i < self.vertices.len() {
1263 self.vertices[i] = vec3_add(self.vertices[i], *d);
1264 }
1265 }
1266 for (fi, &[a, b, c]) in self.face_indices.iter().enumerate() {
1267 self.bvh.triangles[fi] =
1268 Triangle::new(self.vertices[a], self.vertices[b], self.vertices[c]);
1269 }
1270 self.bvh.refit();
1271 }
1272
1273 pub fn num_faces(&self) -> usize {
1275 self.face_indices.len()
1276 }
1277}
1278
1279pub fn sdf_sphere(p: [f64; 3], centre: [f64; 3], radius: f64) -> f64 {
1285 vec3_len(vec3_sub(p, centre)) - radius
1286}
1287
1288pub fn sdf_aabb(p: [f64; 3], aabb: &MeshAabb) -> f64 {
1290 let c = aabb.centre();
1291 let h = aabb.half_extents();
1292 let d = [
1293 (p[0] - c[0]).abs() - h[0],
1294 (p[1] - c[1]).abs() - h[1],
1295 (p[2] - c[2]).abs() - h[2],
1296 ];
1297 let outside = [d[0].max(0.0), d[1].max(0.0), d[2].max(0.0)];
1298 let inside = d[0].max(d[1]).max(d[2]).min(0.0);
1299 vec3_len(outside) + inside
1300}
1301
1302pub fn mesh_sdf_grid(
1306 mesh: &[Triangle],
1307 origin: [f64; 3],
1308 nx: usize,
1309 ny: usize,
1310 nz: usize,
1311 dx: f64,
1312) -> Vec<f64> {
1313 let total = nx * ny * nz;
1314 let mut field = vec![0.0_f64; total];
1315 for iz in 0..nz {
1316 for iy in 0..ny {
1317 for ix in 0..nx {
1318 let p = [
1319 origin[0] + ix as f64 * dx,
1320 origin[1] + iy as f64 * dx,
1321 origin[2] + iz as f64 * dx,
1322 ];
1323 let mut min_d2 = f64::INFINITY;
1324 let mut closest_n = [0.0; 3];
1325 let mut closest_diff = [0.0; 3];
1326 for tri in mesh {
1327 let d2 = point_triangle_dist_sq(p, tri);
1328 if d2 < min_d2 {
1329 min_d2 = d2;
1330 closest_n = tri.normal();
1331 closest_diff = vec3_sub(p, tri.centroid());
1332 }
1333 }
1334 let sign = if vec3_dot(closest_diff, closest_n) >= 0.0 {
1335 1.0
1336 } else {
1337 -1.0
1338 };
1339 field[ix * ny * nz + iy * nz + iz] = sign * min_d2.sqrt();
1340 }
1341 }
1342 }
1343 field
1344}
1345
1346#[cfg(test)]
1351mod tests {
1352 use super::*;
1353
1354 #[test]
1357 fn test_vec3_dot_orthogonal() {
1358 assert!((vec3_dot([1.0, 0.0, 0.0], [0.0, 1.0, 0.0])).abs() < 1e-15);
1359 }
1360
1361 #[test]
1362 fn test_vec3_cross_unit() {
1363 let c = vec3_cross([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1364 assert!((c[2] - 1.0).abs() < 1e-15);
1365 }
1366
1367 #[test]
1368 fn test_vec3_norm_unit_length() {
1369 let n = vec3_norm([3.0, 4.0, 0.0]);
1370 assert!((vec3_len(n) - 1.0).abs() < 1e-14);
1371 }
1372
1373 #[test]
1374 fn test_vec3_lerp_midpoint() {
1375 let m = vec3_lerp([0.0; 3], [2.0, 2.0, 2.0], 0.5);
1376 for &c in &m {
1377 assert!((c - 1.0).abs() < 1e-14);
1378 }
1379 }
1380
1381 #[test]
1384 fn test_aabb_overlaps() {
1385 let a = MeshAabb::new([0.0; 3], [1.0; 3]);
1386 let b = MeshAabb::new([0.5; 3], [1.5; 3]);
1387 assert!(a.overlaps(&b));
1388 }
1389
1390 #[test]
1391 fn test_aabb_not_overlaps() {
1392 let a = MeshAabb::new([0.0; 3], [1.0; 3]);
1393 let b = MeshAabb::new([2.0; 3], [3.0; 3]);
1394 assert!(!a.overlaps(&b));
1395 }
1396
1397 #[test]
1398 fn test_aabb_contains_point() {
1399 let a = MeshAabb::new([0.0; 3], [1.0; 3]);
1400 assert!(a.contains_point([0.5, 0.5, 0.5]));
1401 assert!(!a.contains_point([1.5, 0.5, 0.5]));
1402 }
1403
1404 #[test]
1405 fn test_aabb_from_triangle() {
1406 let a = MeshAabb::from_triangle([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1407 assert!(a.min[2].abs() < 1e-15);
1408 assert!((a.max[0] - 1.0).abs() < 1e-15);
1409 }
1410
1411 #[test]
1412 fn test_aabb_fattened() {
1413 let a = MeshAabb::new([0.0; 3], [1.0; 3]);
1414 let fat = a.fattened(0.1);
1415 for i in 0..3 {
1416 assert!((fat.min[i] - (-0.1)).abs() < 1e-14);
1417 assert!((fat.max[i] - 1.1).abs() < 1e-14);
1418 }
1419 }
1420
1421 #[test]
1424 fn test_triangle_normal_unit() {
1425 let tri = Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1426 let n = tri.normal();
1427 assert!((vec3_len(n) - 1.0).abs() < 1e-14);
1428 }
1429
1430 #[test]
1431 fn test_triangle_area() {
1432 let tri = Triangle::new([0.0; 3], [2.0, 0.0, 0.0], [0.0, 2.0, 0.0]);
1433 assert!((tri.area() - 2.0).abs() < 1e-14);
1434 }
1435
1436 #[test]
1437 fn test_triangle_centroid() {
1438 let tri = Triangle::new([0.0; 3], [3.0, 0.0, 0.0], [0.0, 3.0, 0.0]);
1439 let c = tri.centroid();
1440 assert!((c[0] - 1.0).abs() < 1e-14);
1441 assert!((c[1] - 1.0).abs() < 1e-14);
1442 }
1443
1444 #[test]
1447 fn test_ray_triangle_hit() {
1448 let tri = Triangle::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1449 let hit = ray_triangle_mt([0.25, 0.25, 1.0], [0.0, 0.0, -1.0], &tri, 0.0, 10.0);
1450 assert!(hit.is_some(), "Should hit the triangle");
1451 let h = hit.unwrap();
1452 assert!((h.t - 1.0).abs() < 1e-10);
1453 }
1454
1455 #[test]
1456 fn test_ray_triangle_miss() {
1457 let tri = Triangle::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1458 let hit = ray_triangle_mt([5.0, 5.0, 1.0], [0.0, 0.0, -1.0], &tri, 0.0, 10.0);
1459 assert!(hit.is_none());
1460 }
1461
1462 #[test]
1463 fn test_ray_triangle_back_face_culled_by_t() {
1464 let tri = Triangle::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1466 let hit = ray_triangle_mt([0.25, 0.25, -1.0], [0.0, 0.0, -1.0], &tri, 0.0, 10.0);
1467 assert!(hit.is_none(), "Ray moving away from triangle");
1468 }
1469
1470 #[test]
1473 fn test_ray_aabb_hit() {
1474 let aabb = MeshAabb::new([0.0; 3], [1.0; 3]);
1475 assert!(ray_aabb_intersect(
1476 [0.5, 0.5, 2.0],
1477 [0.0, 0.0, -1.0],
1478 &aabb,
1479 0.0,
1480 10.0
1481 ));
1482 }
1483
1484 #[test]
1485 fn test_ray_aabb_miss() {
1486 let aabb = MeshAabb::new([0.0; 3], [1.0; 3]);
1487 assert!(!ray_aabb_intersect(
1488 [5.0, 5.0, 2.0],
1489 [0.0, 0.0, -1.0],
1490 &aabb,
1491 0.0,
1492 10.0
1493 ));
1494 }
1495
1496 #[test]
1499 fn test_tri_tri_intersect_coplanar_overlapping() {
1500 let t1 = Triangle::new([0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [0.0, 2.0, 0.0]);
1501 let t2 = Triangle::new([1.0, 0.0, 0.0], [3.0, 0.0, 0.0], [1.0, 2.0, 0.0]);
1502 let _ = triangle_triangle_intersect(&t1, &t2); }
1505
1506 #[test]
1507 fn test_tri_tri_separated() {
1508 let t1 = Triangle::new([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1510 let t2 = Triangle::new([0.0, 0.0, 2.0], [1.0, 0.0, 2.0], [0.0, 1.0, 2.0]);
1511 assert!(!triangle_triangle_intersect(&t1, &t2));
1512 }
1513
1514 #[test]
1517 fn test_bvh_build_single_triangle() {
1518 let tris = vec![Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])];
1519 let bvh = TriMeshBvh::build(tris);
1520 assert_eq!(bvh.num_triangles(), 1);
1521 assert!(bvh.num_nodes() >= 1);
1522 }
1523
1524 #[test]
1525 fn test_bvh_build_multiple_triangles() {
1526 let tris: Vec<Triangle> = (0..8)
1527 .map(|i| {
1528 let x = i as f64;
1529 Triangle::new([x, 0.0, 0.0], [x + 1.0, 0.0, 0.0], [x, 1.0, 0.0])
1530 })
1531 .collect();
1532 let bvh = TriMeshBvh::build(tris);
1533 assert_eq!(bvh.num_triangles(), 8);
1534 }
1535
1536 #[test]
1537 fn test_bvh_ray_cast_hits() {
1538 let tris = vec![Triangle::new(
1539 [0.0, 0.0, 0.0],
1540 [1.0, 0.0, 0.0],
1541 [0.0, 1.0, 0.0],
1542 )];
1543 let bvh = TriMeshBvh::build(tris);
1544 let result = bvh.ray_cast([0.25, 0.25, 2.0], [0.0, 0.0, -1.0], 10.0);
1545 assert!(result.is_some());
1546 }
1547
1548 #[test]
1549 fn test_bvh_ray_cast_miss() {
1550 let tris = vec![Triangle::new(
1551 [0.0, 0.0, 0.0],
1552 [1.0, 0.0, 0.0],
1553 [0.0, 1.0, 0.0],
1554 )];
1555 let bvh = TriMeshBvh::build(tris);
1556 let result = bvh.ray_cast([5.0, 5.0, 2.0], [0.0, 0.0, -1.0], 10.0);
1557 assert!(result.is_none());
1558 }
1559
1560 #[test]
1561 fn test_bvh_query_aabb() {
1562 let tris: Vec<Triangle> = (0..4)
1563 .map(|i| {
1564 let x = i as f64 * 3.0;
1565 Triangle::new([x, 0.0, 0.0], [x + 1.0, 0.0, 0.0], [x, 1.0, 0.0])
1566 })
1567 .collect();
1568 let bvh = TriMeshBvh::build(tris);
1569 let q = MeshAabb::new([0.0; 3], [1.5, 1.5, 1.5]);
1570 let hits = bvh.query_aabb(&q);
1571 assert!(!hits.is_empty());
1572 }
1573
1574 #[test]
1577 fn test_mesh_broad_phase_overlapping() {
1578 let tris_a = vec![Triangle::new(
1579 [0.0, 0.0, 0.0],
1580 [1.0, 0.0, 0.0],
1581 [0.0, 1.0, 0.0],
1582 )];
1583 let tris_b = vec![Triangle::new(
1584 [0.3, 0.3, -0.1],
1585 [0.6, 0.3, 0.0],
1586 [0.3, 0.6, 0.0],
1587 )];
1588 let bvh_a = TriMeshBvh::build(tris_a);
1589 let bvh_b = TriMeshBvh::build(tris_b);
1590 let pairs = mesh_broad_phase(&bvh_a, &bvh_b);
1591 assert!(!pairs.is_empty());
1592 }
1593
1594 #[test]
1595 fn test_mesh_broad_phase_separated() {
1596 let tris_a = vec![Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])];
1597 let tris_b = vec![Triangle::new(
1598 [10.0, 0.0, 0.0],
1599 [11.0, 0.0, 0.0],
1600 [10.0, 1.0, 0.0],
1601 )];
1602 let bvh_a = TriMeshBvh::build(tris_a);
1603 let bvh_b = TriMeshBvh::build(tris_b);
1604 let pairs = mesh_broad_phase(&bvh_a, &bvh_b);
1605 assert!(pairs.is_empty());
1606 }
1607
1608 #[test]
1611 fn test_point_triangle_dist_vertex_case() {
1612 let tri = Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1613 let d2 = point_triangle_dist_sq([-1.0, 0.0, 0.0], &tri);
1614 assert!((d2 - 1.0).abs() < 1e-10);
1615 }
1616
1617 #[test]
1618 fn test_point_triangle_dist_above() {
1619 let tri = Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1620 let d2 = point_triangle_dist_sq([0.25, 0.25, 2.0], &tri);
1621 assert!((d2 - 4.0).abs() < 1e-10);
1622 }
1623
1624 #[test]
1625 fn test_closest_point_on_triangle_inside() {
1626 let tri = Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
1627 let cp = closest_point_on_triangle([0.25, 0.25, 1.0], &tri);
1628 assert!(
1630 (cp[2]).abs() < 0.1,
1631 "Closest point z should be near 0, got {}",
1632 cp[2]
1633 );
1634 }
1635
1636 #[test]
1639 fn test_cloth_self_collision_separates_vertices() {
1640 let mut verts = vec![
1641 ClothVertex::new([0.0, 0.0, 0.0], 1.0),
1642 ClothVertex::new([0.001, 0.0, 0.0], 1.0),
1643 ];
1644 let tris: Vec<[usize; 3]> = vec![];
1645 let n = cloth_self_collision(&mut verts, &tris, 0.05);
1646 assert!(n > 0);
1647 let dist = vec3_len(vec3_sub(verts[0].pos, verts[1].pos));
1648 assert!(dist >= 0.049, "Vertices should be separated, dist={}", dist);
1649 }
1650
1651 #[test]
1652 fn test_cloth_edge_constraint_projection() {
1653 let mut verts = vec![
1654 ClothVertex::new([0.0, 0.0, 0.0], 1.0),
1655 ClothVertex::new([2.0, 0.0, 0.0], 1.0),
1656 ];
1657 let edge = ClothEdge::new(0, 1, 1.0, 1.0);
1658 edge.project(&mut verts);
1659 let d = vec3_len(vec3_sub(verts[0].pos, verts[1].pos));
1660 assert!(
1661 (d - 1.0).abs() < 0.01,
1662 "After projection edge length should be ~1, got {}",
1663 d
1664 );
1665 }
1666
1667 #[test]
1670 fn test_deformable_mesh_build() {
1671 let verts = vec![[0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1672 let faces = vec![[0, 1, 2]];
1673 let dm = DeformableMesh::build(verts, faces);
1674 assert_eq!(dm.num_faces(), 1);
1675 }
1676
1677 #[test]
1678 fn test_deformable_mesh_update_vertex() {
1679 let verts = vec![[0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1680 let faces = vec![[0, 1, 2]];
1681 let mut dm = DeformableMesh::build(verts, faces);
1682 dm.update_vertex(0, [0.5, 0.5, 0.0]);
1683 assert!((dm.vertices[0][0] - 0.5).abs() < 1e-14);
1684 }
1685
1686 #[test]
1689 fn test_sdf_sphere_outside() {
1690 let d = sdf_sphere([2.0, 0.0, 0.0], [0.0; 3], 1.0);
1691 assert!((d - 1.0).abs() < 1e-14);
1692 }
1693
1694 #[test]
1695 fn test_sdf_sphere_inside() {
1696 let d = sdf_sphere([0.5, 0.0, 0.0], [0.0; 3], 1.0);
1697 assert!(d < 0.0, "Point inside sphere → negative SDF");
1698 }
1699
1700 #[test]
1701 fn test_sdf_aabb_outside() {
1702 let aabb = MeshAabb::new([0.0; 3], [1.0; 3]);
1703 let d = sdf_aabb([2.0, 0.5, 0.5], &aabb);
1704 assert!((d - 1.0).abs() < 1e-14);
1705 }
1706
1707 #[test]
1708 fn test_sdf_aabb_inside() {
1709 let aabb = MeshAabb::new([0.0; 3], [2.0; 3]);
1710 let d = sdf_aabb([1.0, 1.0, 1.0], &aabb);
1711 assert!(d < 0.0, "Centre of AABB → negative SDF");
1712 }
1713
1714 #[test]
1717 fn test_contact_manifold_add_contact() {
1718 let mut m = MeshContactManifold::new();
1719 m.add_contact(MeshContact {
1720 position: [0.0; 3],
1721 normal: [0.0, 0.0, 1.0],
1722 depth: 0.05,
1723 tri_a: 0,
1724 tri_b: 0,
1725 });
1726 assert_eq!(m.num_contacts(), 1);
1727 assert!((m.max_depth - 0.05).abs() < 1e-14);
1728 }
1729
1730 #[test]
1731 fn test_generate_manifold_no_collision() {
1732 let tris_a = vec![Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])];
1733 let tris_b = vec![Triangle::new(
1734 [10.0, 0.0, 0.0],
1735 [11.0, 0.0, 0.0],
1736 [10.0, 1.0, 0.0],
1737 )];
1738 let bvh_a = TriMeshBvh::build(tris_a);
1739 let bvh_b = TriMeshBvh::build(tris_b);
1740 let manifold = generate_mesh_contact_manifold(&bvh_a, &bvh_b);
1741 assert_eq!(manifold.num_contacts(), 0);
1742 }
1743
1744 #[test]
1747 fn test_penetration_depth_no_overlap() {
1748 let tris_a = vec![Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])];
1749 let tris_b = vec![Triangle::new(
1750 [5.0, 0.0, 0.0],
1751 [6.0, 0.0, 0.0],
1752 [5.0, 1.0, 0.0],
1753 )];
1754 let a = TriMeshBvh::build(tris_a);
1755 let b = TriMeshBvh::build(tris_b);
1756 let d = penetration_depth_estimate(&a, &b, 4);
1757 assert!((d - 0.0).abs() < 1e-14);
1758 }
1759
1760 #[test]
1763 fn test_bvh_refit_after_vertex_move() {
1764 let verts = vec![[0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1765 let faces = vec![[0, 1, 2]];
1766 let mut dm = DeformableMesh::build(verts, faces);
1767 dm.update_vertex(1, [5.0, 0.0, 0.0]);
1768 let root_aabb = &dm.bvh.nodes[dm.bvh.root].aabb;
1769 assert!(root_aabb.max[0] >= 4.9, "BVH should cover moved vertex");
1770 }
1771
1772 #[test]
1775 fn test_proximity_field_near_triangle() {
1776 let tris = vec![Triangle::new([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])];
1777 let field = build_proximity_field(&tris, [0.0, 0.0, 0.0], 3, 3, 3, 0.5);
1778 assert!(field[0] < 1e-10, "Point on triangle → zero distance");
1780 }
1781
1782 #[test]
1785 fn test_barycentric_centroid() {
1786 let tri = Triangle::new([0.0; 3], [3.0, 0.0, 0.0], [0.0, 3.0, 0.0]);
1787 let c = tri.centroid();
1788 let bary = point_triangle_barycentric(c, &tri);
1789 assert!((bary[0] + bary[1] + bary[2] - 1.0).abs() < 0.1);
1791 }
1792}