1use super::functions::*;
6
7#[derive(Clone, Debug)]
9pub struct PolyhedraContact {
10 pub normal: [f64; 3],
12 pub depth: f64,
14 pub feature: ContactFeature,
16}
17#[derive(Clone, Debug)]
20pub struct Obb {
21 pub center: [f64; 3],
23 pub axes: [[f64; 3]; 3],
25 pub half_extents: [f64; 3],
27}
28impl Obb {
29 pub fn axis_aligned(center: [f64; 3], half_extents: [f64; 3]) -> Self {
31 Obb {
32 center,
33 axes: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
34 half_extents,
35 }
36 }
37 pub fn from_aabb(min: [f64; 3], max: [f64; 3]) -> Self {
39 let center = [
40 (min[0] + max[0]) * 0.5,
41 (min[1] + max[1]) * 0.5,
42 (min[2] + max[2]) * 0.5,
43 ];
44 let half_extents = [
45 (max[0] - min[0]) * 0.5,
46 (max[1] - min[1]) * 0.5,
47 (max[2] - min[2]) * 0.5,
48 ];
49 Obb {
50 center,
51 axes: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
52 half_extents,
53 }
54 }
55 pub fn vertices(&self) -> [[f64; 3]; 8] {
57 let c = self.center;
58 let a = self.axes;
59 let h = self.half_extents;
60 let mut verts = [[0.0f64; 3]; 8];
61 for (i, s) in [
62 [-1.0f64, -1.0, -1.0],
63 [1.0, -1.0, -1.0],
64 [-1.0, 1.0, -1.0],
65 [1.0, 1.0, -1.0],
66 [-1.0, -1.0, 1.0],
67 [1.0, -1.0, 1.0],
68 [-1.0, 1.0, 1.0],
69 [1.0, 1.0, 1.0],
70 ]
71 .iter()
72 .enumerate()
73 {
74 verts[i] = add(
75 add(add(c, scale(a[0], s[0] * h[0])), scale(a[1], s[1] * h[1])),
76 scale(a[2], s[2] * h[2]),
77 );
78 }
79 verts
80 }
81 pub fn support(&self, dir: [f64; 3]) -> [f64; 3] {
83 let verts = self.vertices();
84 let mut best = verts[0];
85 let mut best_d = dot(best, dir);
86 for &v in verts.iter().skip(1) {
87 let d = dot(v, dir);
88 if d > best_d {
89 best_d = d;
90 best = v;
91 }
92 }
93 best
94 }
95 pub fn project_onto_axis(&self, axis: [f64; 3]) -> (f64, f64) {
97 let center_proj = dot(self.center, axis);
98 let radius = self.half_extents[0] * dot(self.axes[0], axis).abs()
99 + self.half_extents[1] * dot(self.axes[1], axis).abs()
100 + self.half_extents[2] * dot(self.axes[2], axis).abs();
101 (center_proj - radius, center_proj + radius)
102 }
103}
104#[derive(Clone, Debug, PartialEq)]
106pub enum ContactFeature {
107 FaceFace { face_a: usize, face_b: usize },
109 FaceEdge { face: usize, edge: usize },
111 EdgeEdge { edge_a: usize, edge_b: usize },
113 VertexFace { vertex: usize, face: usize },
115}
116pub struct ObbCollision;
118impl ObbCollision {
119 pub fn test_overlap_on_axis(obb_a: &Obb, obb_b: &Obb, axis: [f64; 3]) -> Option<f64> {
122 let len = length(axis);
123 if len < 1e-12 {
124 return None;
125 }
126 let norm_axis = scale(axis, 1.0 / len);
127 let (min_a, max_a) = obb_a.project_onto_axis(norm_axis);
128 let (min_b, max_b) = obb_b.project_onto_axis(norm_axis);
129 let overlap = f64::min(max_a, max_b) - f64::max(min_a, min_b);
130 if overlap < 0.0 { None } else { Some(overlap) }
131 }
132 pub fn obb_obb_sat(obb_a: &Obb, obb_b: &Obb) -> Option<SatContact> {
136 let mut min_depth = f64::MAX;
137 let mut best_normal = [0.0f64; 3];
138 let mut best_feature = ContactFeature::FaceFace {
139 face_a: 0,
140 face_b: 0,
141 };
142 let mut test = |axis: [f64; 3], feature: ContactFeature| -> bool {
143 match Self::test_overlap_on_axis(obb_a, obb_b, axis) {
144 None => false,
145 Some(overlap) => {
146 if overlap < min_depth {
147 min_depth = overlap;
148 let diff = sub(obb_a.center, obb_b.center);
149 let norm = normalize(axis);
150 best_normal = if dot(norm, diff) >= 0.0 {
151 norm
152 } else {
153 scale(norm, -1.0)
154 };
155 best_feature = feature;
156 }
157 true
158 }
159 }
160 };
161 for (i, &ax) in obb_a.axes.iter().enumerate() {
162 if !test(
163 ax,
164 ContactFeature::FaceFace {
165 face_a: i * 2,
166 face_b: 0,
167 },
168 ) {
169 return None;
170 }
171 }
172 for (j, &bx) in obb_b.axes.iter().enumerate() {
173 if !test(
174 bx,
175 ContactFeature::FaceFace {
176 face_a: 0,
177 face_b: j * 2,
178 },
179 ) {
180 return None;
181 }
182 }
183 for (i, &ai) in obb_a.axes.iter().enumerate() {
184 for (j, &bj) in obb_b.axes.iter().enumerate() {
185 let axis = cross(ai, bj);
186 if length(axis) > 1e-6
187 && !test(
188 axis,
189 ContactFeature::EdgeEdge {
190 edge_a: i,
191 edge_b: j,
192 },
193 )
194 {
195 return None;
196 }
197 }
198 }
199 Some(SatContact {
200 normal: best_normal,
201 depth: min_depth,
202 contact_type: best_feature,
203 })
204 }
205 pub fn box_box_fast(a: &Obb, b: &Obb) -> bool {
207 Self::obb_obb_sat(a, b).is_some()
208 }
209}
210#[derive(Clone, Debug)]
212pub struct SatContact {
213 pub normal: [f64; 3],
215 pub depth: f64,
217 pub contact_type: ContactFeature,
219}
220pub struct SatContactPointGenerator;
222impl SatContactPointGenerator {
223 pub fn from_obb_obb(obb_a: &Obb, obb_b: &Obb, contact: &SatContact) -> Vec<[f64; 3]> {
228 match &contact.contact_type {
229 ContactFeature::FaceFace { .. } => {
230 let support_b = obb_b.support(scale(contact.normal, -1.0));
231 let ref_d = dot(obb_a.center, contact.normal) + contact.depth * 0.5;
232 let proj = dot(support_b, contact.normal);
233 let adjusted = add(support_b, scale(contact.normal, ref_d - proj));
234 vec![adjusted]
235 }
236 ContactFeature::EdgeEdge { edge_a, edge_b } => {
237 let dir_a = obb_a.axes[edge_a % 3];
238 let dir_b = obb_b.axes[edge_b % 3];
239 let p_a = obb_a.support(scale(contact.normal, -1.0));
240 let p_b = obb_b.support(contact.normal);
241 let (cp, _) = edge_edge_contact(p_a, dir_a, p_b, dir_b);
242 vec![cp]
243 }
244 ContactFeature::VertexFace { .. } | ContactFeature::FaceEdge { .. } => {
245 let v = obb_b.support(scale(contact.normal, -1.0));
246 vec![v]
247 }
248 }
249 }
250}
251pub struct ObbTriangleCollision;
253impl ObbTriangleCollision {
254 pub fn test(obb: &Obb, tri: [[f64; 3]; 3]) -> Option<SatContact> {
258 let mut min_depth = f64::MAX;
259 let mut best_normal = [0.0f64; 3];
260 let mut best_feature = ContactFeature::FaceFace {
261 face_a: 0,
262 face_b: 0,
263 };
264 let tri_edges = [
265 sub(tri[1], tri[0]),
266 sub(tri[2], tri[1]),
267 sub(tri[0], tri[2]),
268 ];
269 let test_axis = |axis: [f64; 3]| -> Option<f64> {
270 let len = length(axis);
271 if len < 1e-12 {
272 return Some(f64::MAX);
273 }
274 let norm = scale(axis, 1.0 / len);
275 let (min_obb, max_obb) = obb.project_onto_axis(norm);
276 let tri_proj: Vec<f64> = tri.iter().map(|&v| dot(v, norm)).collect();
277 let min_tri = tri_proj.iter().cloned().fold(f64::MAX, f64::min);
278 let max_tri = tri_proj.iter().cloned().fold(f64::MIN, f64::max);
279 let overlap = f64::min(max_obb, max_tri) - f64::max(min_obb, min_tri);
280 if overlap < 0.0 { None } else { Some(overlap) }
281 };
282 for (i, &ax) in obb.axes.iter().enumerate() {
283 match test_axis(ax) {
284 None => return None,
285 Some(depth) if depth < min_depth => {
286 min_depth = depth;
287 best_normal = ax;
288 best_feature = ContactFeature::FaceFace {
289 face_a: i,
290 face_b: 0,
291 };
292 }
293 _ => {}
294 }
295 }
296 let tri_normal = normalize(cross(tri_edges[0], tri_edges[2]));
297 match test_axis(tri_normal) {
298 None => return None,
299 Some(depth) if depth < min_depth => {
300 min_depth = depth;
301 best_normal = tri_normal;
302 best_feature = ContactFeature::FaceFace {
303 face_a: 0,
304 face_b: 1,
305 };
306 }
307 _ => {}
308 }
309 for (i, &ai) in obb.axes.iter().enumerate() {
310 for (j, &te) in tri_edges.iter().enumerate() {
311 let axis = cross(ai, te);
312 if length(axis) < 1e-9 {
313 continue;
314 }
315 match test_axis(axis) {
316 None => return None,
317 Some(depth) if depth < min_depth => {
318 min_depth = depth;
319 best_normal = normalize(axis);
320 best_feature = ContactFeature::EdgeEdge {
321 edge_a: i,
322 edge_b: j,
323 };
324 }
325 _ => {}
326 }
327 }
328 }
329 let tri_centroid = scale(add(add(tri[0], tri[1]), tri[2]), 1.0 / 3.0);
330 let dir_to_tri = sub(tri_centroid, obb.center);
331 if dot(best_normal, dir_to_tri) < 0.0 {
332 best_normal = scale(best_normal, -1.0);
333 }
334 Some(SatContact {
335 normal: best_normal,
336 depth: min_depth,
337 contact_type: best_feature,
338 })
339 }
340}
341#[derive(Clone, Debug)]
343pub struct ConvexPolytope {
344 pub vertices: Vec<[f64; 3]>,
346 pub faces: Vec<Vec<usize>>,
348 pub edges: Vec<[usize; 2]>,
350 pub face_normals: Vec<[f64; 3]>,
352}
353impl ConvexPolytope {
354 pub fn from_obb(obb: &Obb) -> Self {
356 let verts = obb.vertices();
357 let vertices: Vec<[f64; 3]> = verts.to_vec();
358 let faces: Vec<Vec<usize>> = vec![
359 vec![0, 2, 3, 1],
360 vec![4, 5, 7, 6],
361 vec![0, 1, 5, 4],
362 vec![2, 6, 7, 3],
363 vec![0, 4, 6, 2],
364 vec![1, 3, 7, 5],
365 ];
366 let face_normals: Vec<[f64; 3]> = vec![
367 scale(obb.axes[2], -1.0),
368 obb.axes[2],
369 scale(obb.axes[1], -1.0),
370 obb.axes[1],
371 scale(obb.axes[0], -1.0),
372 obb.axes[0],
373 ];
374 let edges: Vec<[usize; 2]> = vec![
375 [0, 1],
376 [2, 3],
377 [4, 5],
378 [6, 7],
379 [0, 2],
380 [1, 3],
381 [4, 6],
382 [5, 7],
383 [0, 4],
384 [1, 5],
385 [2, 6],
386 [3, 7],
387 ];
388 ConvexPolytope {
389 vertices,
390 faces,
391 edges,
392 face_normals,
393 }
394 }
395 pub fn support(&self, dir: [f64; 3], transform: ([f64; 3], [[f64; 3]; 3])) -> [f64; 3] {
398 let (translation, axes) = transform;
399 let local_dir = [dot(dir, axes[0]), dot(dir, axes[1]), dot(dir, axes[2])];
400 let mut best_local = self.vertices[0];
401 let mut best_d = dot(self.vertices[0], local_dir);
402 for &v in self.vertices.iter().skip(1) {
403 let d = dot(v, local_dir);
404 if d > best_d {
405 best_d = d;
406 best_local = v;
407 }
408 }
409 add(translation, rotate(axes, best_local))
410 }
411}
412pub struct PolytopeCollision;
414impl PolytopeCollision {
415 pub fn sat_test(
418 a: &ConvexPolytope,
419 ta: ([f64; 3], [[f64; 3]; 3]),
420 b: &ConvexPolytope,
421 tb: ([f64; 3], [[f64; 3]; 3]),
422 ) -> Option<SatContact> {
423 let mut min_depth = f64::MAX;
424 let mut best_normal = [0.0f64; 3];
425 let mut best_feature = ContactFeature::FaceFace {
426 face_a: 0,
427 face_b: 0,
428 };
429 let project = |poly: &ConvexPolytope, t: ([f64; 3], [[f64; 3]; 3]), axis: [f64; 3]| {
430 let (trans, axes) = t;
431 let mut mn = f64::MAX;
432 let mut mx = f64::MIN;
433 for &lv in &poly.vertices {
434 let wv = add(trans, rotate(axes, lv));
435 let p = dot(wv, axis);
436 if p < mn {
437 mn = p;
438 }
439 if p > mx {
440 mx = p;
441 }
442 }
443 (mn, mx)
444 };
445 let mut test_axis = |axis: [f64; 3], feature: ContactFeature| -> bool {
446 let len = length(axis);
447 if len < 1e-9 {
448 return true;
449 }
450 let norm = scale(axis, 1.0 / len);
451 let (min_a, max_a) = project(a, ta, norm);
452 let (min_b, max_b) = project(b, tb, norm);
453 let overlap = f64::min(max_a, max_b) - f64::max(min_a, min_b);
454 if overlap < 0.0 {
455 return false;
456 }
457 if overlap < min_depth {
458 min_depth = overlap;
459 let diff = sub(ta.0, tb.0);
460 best_normal = if dot(norm, diff) >= 0.0 {
461 norm
462 } else {
463 scale(norm, -1.0)
464 };
465 best_feature = feature;
466 }
467 true
468 };
469 for (fi, &ln) in a.face_normals.iter().enumerate() {
470 let wn = rotate(ta.1, ln);
471 if !test_axis(
472 wn,
473 ContactFeature::FaceFace {
474 face_a: fi,
475 face_b: 0,
476 },
477 ) {
478 return None;
479 }
480 }
481 for (fi, &ln) in b.face_normals.iter().enumerate() {
482 let wn = rotate(tb.1, ln);
483 if !test_axis(
484 wn,
485 ContactFeature::FaceFace {
486 face_a: 0,
487 face_b: fi,
488 },
489 ) {
490 return None;
491 }
492 }
493 for (ei, &[ia0, ia1]) in a.edges.iter().enumerate() {
494 let ea = normalize(sub(
495 add(ta.0, rotate(ta.1, a.vertices[ia1])),
496 add(ta.0, rotate(ta.1, a.vertices[ia0])),
497 ));
498 for (ej, &[ib0, ib1]) in b.edges.iter().enumerate() {
499 let eb = normalize(sub(
500 add(tb.0, rotate(tb.1, b.vertices[ib1])),
501 add(tb.0, rotate(tb.1, b.vertices[ib0])),
502 ));
503 let axis = cross(ea, eb);
504 if !test_axis(
505 axis,
506 ContactFeature::EdgeEdge {
507 edge_a: ei,
508 edge_b: ej,
509 },
510 ) {
511 return None;
512 }
513 }
514 }
515 Some(SatContact {
516 normal: best_normal,
517 depth: min_depth,
518 contact_type: best_feature,
519 })
520 }
521}
522#[derive(Clone, Debug)]
524pub struct ConvexPolyhedron {
525 pub vertices: Vec<[f64; 3]>,
527 pub face_normals: Vec<[f64; 3]>,
529 pub edge_directions: Vec<[f64; 3]>,
531}
532impl ConvexPolyhedron {
533 pub fn new(
535 vertices: Vec<[f64; 3]>,
536 face_normals: Vec<[f64; 3]>,
537 edge_directions: Vec<[f64; 3]>,
538 ) -> Self {
539 ConvexPolyhedron {
540 vertices,
541 face_normals,
542 edge_directions,
543 }
544 }
545 pub fn support(&self, dir: [f64; 3]) -> [f64; 3] {
547 self.vertices
548 .iter()
549 .max_by(|a, b| {
550 dot(**a, dir)
551 .partial_cmp(&dot(**b, dir))
552 .unwrap_or(std::cmp::Ordering::Equal)
553 })
554 .copied()
555 .unwrap_or([0.0; 3])
556 }
557 pub fn project(&self, axis: [f64; 3]) -> (f64, f64) {
559 let mut mn = f64::INFINITY;
560 let mut mx = f64::NEG_INFINITY;
561 for &v in &self.vertices {
562 let d = dot(v, axis);
563 mn = mn.min(d);
564 mx = mx.max(d);
565 }
566 (mn, mx)
567 }
568}
569#[derive(Clone, Debug)]
575pub struct SatAxisCache {
576 pub axis: [f64; 3],
578 pub overlap: f64,
580 pub valid: bool,
582}
583impl SatAxisCache {
584 pub fn new() -> Self {
586 SatAxisCache {
587 axis: [0.0, 1.0, 0.0],
588 overlap: 0.0,
589 valid: false,
590 }
591 }
592 pub fn invalidate(&mut self) {
594 self.valid = false;
595 }
596 pub fn update(&mut self, axis: [f64; 3], overlap: f64) {
598 self.axis = axis;
599 self.overlap = overlap;
600 self.valid = true;
601 }
602 pub fn check(&self, obb_a: &Obb, obb_b: &Obb) -> Option<f64> {
607 if !self.valid {
608 return None;
609 }
610 ObbCollision::test_overlap_on_axis(obb_a, obb_b, self.axis)
611 }
612}
613#[derive(Clone, Debug, Default)]
615pub struct ObbBvh {
616 pub nodes: Vec<ObbBvhNode>,
618}
619impl ObbBvh {
620 pub fn new() -> Self {
622 ObbBvh { nodes: Vec::new() }
623 }
624 pub fn build(leaf_obbs: &[Obb]) -> Self {
626 if leaf_obbs.is_empty() {
627 return Self::new();
628 }
629 let mut bvh = ObbBvh { nodes: Vec::new() };
630 let indices: Vec<usize> = (0..leaf_obbs.len()).collect();
631 bvh.build_recursive(leaf_obbs, &indices);
632 bvh
633 }
634 fn build_recursive(&mut self, obbs: &[Obb], indices: &[usize]) -> usize {
635 let node_idx = self.nodes.len();
636 if indices.len() == 1 {
637 self.nodes.push(ObbBvhNode {
638 bounds: obbs[indices[0]].clone(),
639 left: usize::MAX,
640 right: usize::MAX,
641 geometry_index: Some(indices[0]),
642 });
643 return node_idx;
644 }
645 let mut mn = [f64::INFINITY; 3];
646 let mut mx = [f64::NEG_INFINITY; 3];
647 for &i in indices {
648 let verts = obbs[i].vertices();
649 for v in &verts {
650 for k in 0..3 {
651 mn[k] = mn[k].min(v[k]);
652 mx[k] = mx[k].max(v[k]);
653 }
654 }
655 }
656 let merged = Obb::from_aabb(mn, mx);
657 let extents = [mx[0] - mn[0], mx[1] - mn[1], mx[2] - mn[2]];
658 let split_axis = if extents[0] >= extents[1] && extents[0] >= extents[2] {
659 0
660 } else if extents[1] >= extents[2] {
661 1
662 } else {
663 2
664 };
665 let split_val = (mn[split_axis] + mx[split_axis]) * 0.5;
666 let (mut left_idx, mut right_idx): (Vec<usize>, Vec<usize>) = indices
667 .iter()
668 .partition(|&&i| obbs[i].center[split_axis] <= split_val);
669 if left_idx.is_empty() || right_idx.is_empty() {
670 left_idx = indices[..indices.len() / 2].to_vec();
671 right_idx = indices[indices.len() / 2..].to_vec();
672 }
673 self.nodes.push(ObbBvhNode {
674 bounds: merged,
675 left: usize::MAX,
676 right: usize::MAX,
677 geometry_index: None,
678 });
679 let left_child = self.build_recursive(obbs, &left_idx);
680 let right_child = self.build_recursive(obbs, &right_idx);
681 self.nodes[node_idx].left = left_child;
682 self.nodes[node_idx].right = right_child;
683 node_idx
684 }
685 pub fn query_overlapping(&self, query_obb: &Obb) -> Vec<usize> {
687 if self.nodes.is_empty() {
688 return Vec::new();
689 }
690 let mut results = Vec::new();
691 self.query_recursive(query_obb, 0, &mut results);
692 results
693 }
694 fn query_recursive(&self, query: &Obb, node_idx: usize, results: &mut Vec<usize>) {
695 let node = &self.nodes[node_idx];
696 if ObbCollision::obb_obb_sat(&node.bounds, query).is_none() {
697 return;
698 }
699 if node.is_leaf() {
700 if let Some(g) = node.geometry_index {
701 results.push(g);
702 }
703 } else {
704 if node.left < self.nodes.len() {
705 self.query_recursive(query, node.left, results);
706 }
707 if node.right < self.nodes.len() {
708 self.query_recursive(query, node.right, results);
709 }
710 }
711 }
712 pub fn leaf_count(&self) -> usize {
714 self.nodes.iter().filter(|n| n.is_leaf()).count()
715 }
716 pub fn node_count(&self) -> usize {
718 self.nodes.len()
719 }
720}
721#[derive(Clone, Debug)]
723pub struct Capsule {
724 pub p0: [f64; 3],
726 pub p1: [f64; 3],
728 pub radius: f64,
730}
731impl Capsule {
732 pub fn new(p0: [f64; 3], p1: [f64; 3], radius: f64) -> Self {
734 Self { p0, p1, radius }
735 }
736 pub fn axis(&self) -> [f64; 3] {
738 sub(self.p1, self.p0)
739 }
740 pub fn half_length(&self) -> f64 {
742 length(self.axis()) * 0.5
743 }
744 pub fn closest_axis_point(&self, q: [f64; 3]) -> [f64; 3] {
746 let ax = self.axis();
747 let ax_len_sq = dot(ax, ax);
748 if ax_len_sq < 1e-24 {
749 return self.p0;
750 }
751 let t = dot(sub(q, self.p0), ax) / ax_len_sq;
752 let t_clamped = t.clamp(0.0, 1.0);
753 add(self.p0, scale(ax, t_clamped))
754 }
755 pub fn support(&self, dir: [f64; 3]) -> [f64; 3] {
757 let d = dot(self.axis(), dir);
758 let axis_pt = if d >= 0.0 { self.p1 } else { self.p0 };
759 let dir_len = length(dir);
760 if dir_len < 1e-12 {
761 return axis_pt;
762 }
763 add(axis_pt, scale(dir, self.radius / dir_len))
764 }
765}
766#[derive(Debug)]
768pub struct ObbTree {
769 pub obb: Obb,
771 pub left: Option<Box<ObbTree>>,
773 pub right: Option<Box<ObbTree>>,
775 pub triangle_indices: Vec<usize>,
777}
778impl ObbTree {
779 pub fn build(vertices: &[[f64; 3]], triangles: &[[usize; 3]]) -> Self {
781 let indices: Vec<usize> = (0..triangles.len()).collect();
782 Self::build_recursive(vertices, triangles, &indices)
783 }
784 fn triangle_centroid(vertices: &[[f64; 3]], tri: [usize; 3]) -> [f64; 3] {
785 let (a, b, c) = (vertices[tri[0]], vertices[tri[1]], vertices[tri[2]]);
786 scale(add(add(a, b), c), 1.0 / 3.0)
787 }
788 fn compute_obb_for_triangles(
789 vertices: &[[f64; 3]],
790 triangles: &[[usize; 3]],
791 indices: &[usize],
792 ) -> Obb {
793 let mut pts: Vec<[f64; 3]> = Vec::new();
794 for &ti in indices {
795 for &vi in &triangles[ti] {
796 pts.push(vertices[vi]);
797 }
798 }
799 if pts.is_empty() {
800 return Obb {
801 center: [0.0; 3],
802 axes: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
803 half_extents: [0.0; 3],
804 };
805 }
806 let mut mn = pts[0];
807 let mut mx = pts[0];
808 for &p in &pts {
809 for k in 0..3 {
810 if p[k] < mn[k] {
811 mn[k] = p[k];
812 }
813 if p[k] > mx[k] {
814 mx[k] = p[k];
815 }
816 }
817 }
818 Obb::from_aabb(mn, mx)
819 }
820 fn build_recursive(vertices: &[[f64; 3]], triangles: &[[usize; 3]], indices: &[usize]) -> Self {
821 let obb = Self::compute_obb_for_triangles(vertices, triangles, indices);
822 if indices.len() <= 1 {
823 return ObbTree {
824 obb,
825 left: None,
826 right: None,
827 triangle_indices: indices.to_vec(),
828 };
829 }
830 let longest_axis = {
831 let he = obb.half_extents;
832 if he[0] >= he[1] && he[0] >= he[2] {
833 0
834 } else if he[1] >= he[2] {
835 1
836 } else {
837 2
838 }
839 };
840 let mut sorted_indices = indices.to_vec();
841 sorted_indices.sort_by(|&ia, &ib| {
842 let ca = Self::triangle_centroid(vertices, triangles[ia]);
843 let cb = Self::triangle_centroid(vertices, triangles[ib]);
844 ca[longest_axis]
845 .partial_cmp(&cb[longest_axis])
846 .unwrap_or(std::cmp::Ordering::Equal)
847 });
848 let mid = sorted_indices.len() / 2;
849 let left_indices = &sorted_indices[..mid];
850 let right_indices = &sorted_indices[mid..];
851 let left = Box::new(Self::build_recursive(vertices, triangles, left_indices));
852 let right = Box::new(Self::build_recursive(vertices, triangles, right_indices));
853 ObbTree {
854 obb,
855 left: Some(left),
856 right: Some(right),
857 triangle_indices: Vec::new(),
858 }
859 }
860 pub fn query_overlap(&self, other: &ObbTree) -> Vec<(usize, usize)> {
862 let mut result = Vec::new();
863 self.query_recursive(other, &mut result);
864 result
865 }
866 fn query_recursive(&self, other: &ObbTree, result: &mut Vec<(usize, usize)>) {
867 if ObbCollision::box_box_fast(&self.obb, &other.obb) {
868 match (&self.left, &self.right, &other.left, &other.right) {
869 (None, None, None, None) => {
870 for &ta in &self.triangle_indices {
871 for &tb in &other.triangle_indices {
872 result.push((ta, tb));
873 }
874 }
875 }
876 _ => {
877 let self_children: Vec<&ObbTree> = [&self.left, &self.right]
878 .iter()
879 .filter_map(|c| c.as_deref())
880 .collect();
881 let other_children: Vec<&ObbTree> = [&other.left, &other.right]
882 .iter()
883 .filter_map(|c| c.as_deref())
884 .collect();
885 if self_children.is_empty() {
886 for oc in &other_children {
887 self.query_recursive(oc, result);
888 }
889 } else {
890 for sc in &self_children {
891 sc.query_recursive(other, result);
892 }
893 }
894 }
895 }
896 }
897 }
898}
899pub struct ContactManifoldBuilder;
901impl ContactManifoldBuilder {
902 pub fn clip_polygon_by_plane(
905 polygon: &[[f64; 3]],
906 plane_normal: [f64; 3],
907 plane_d: f64,
908 ) -> Vec<[f64; 3]> {
909 if polygon.is_empty() {
910 return Vec::new();
911 }
912 let mut output = Vec::new();
913 let n = polygon.len();
914 for i in 0..n {
915 let a = polygon[i];
916 let b = polygon[(i + 1) % n];
917 let da = dot(a, plane_normal) - plane_d;
918 let db = dot(b, plane_normal) - plane_d;
919 if da >= 0.0 {
920 output.push(a);
921 }
922 if (da >= 0.0) != (db >= 0.0) {
923 let t = da / (da - db);
924 output.push(add(a, scale(sub(b, a), t)));
925 }
926 }
927 output
928 }
929 pub fn build_face_face(
932 poly_a: &ConvexPolytope,
933 face_a_idx: usize,
934 ta: ([f64; 3], [[f64; 3]; 3]),
935 poly_b: &ConvexPolytope,
936 face_b_idx: usize,
937 tb: ([f64; 3], [[f64; 3]; 3]),
938 ) -> Vec<[f64; 3]> {
939 let face_a = &poly_a.faces[face_a_idx];
940 let ref_verts: Vec<[f64; 3]> = face_a
941 .iter()
942 .map(|&vi| add(ta.0, rotate(ta.1, poly_a.vertices[vi])))
943 .collect();
944 let face_b = &poly_b.faces[face_b_idx];
945 let mut clipped: Vec<[f64; 3]> = face_b
946 .iter()
947 .map(|&vi| add(tb.0, rotate(tb.1, poly_b.vertices[vi])))
948 .collect();
949 let nref = ref_verts.len();
950 let ref_normal = rotate(ta.1, poly_a.face_normals[face_a_idx]);
951 for i in 0..nref {
952 if clipped.is_empty() {
953 break;
954 }
955 let edge_start = ref_verts[i];
956 let edge_end = ref_verts[(i + 1) % nref];
957 let edge_dir = sub(edge_end, edge_start);
958 let side_normal = normalize(cross(ref_normal, edge_dir));
959 let plane_d = dot(side_normal, edge_start);
960 clipped = Self::clip_polygon_by_plane(&clipped, side_normal, plane_d);
961 }
962 let ref_plane_d = dot(ref_normal, ref_verts[0]);
963 clipped.retain(|p| dot(*p, ref_normal) <= ref_plane_d + 1e-9);
964 clipped
965 }
966}
967pub struct ObbCapsuleCollision;
969impl ObbCapsuleCollision {
970 pub fn closest_point_on_obb(obb: &Obb, q: [f64; 3]) -> [f64; 3] {
972 let d = sub(q, obb.center);
973 let mut result = obb.center;
974 for i in 0..3 {
975 let dist = dot(d, obb.axes[i]);
976 let clamped = dist.clamp(-obb.half_extents[i], obb.half_extents[i]);
977 result = add(result, scale(obb.axes[i], clamped));
978 }
979 result
980 }
981 pub fn test(obb: &Obb, capsule: &Capsule) -> Option<(f64, [f64; 3], [f64; 3])> {
985 let closest_on_axis = capsule.closest_axis_point(obb.center);
986 let closest_on_obb = Self::closest_point_on_obb(obb, closest_on_axis);
987 let diff = sub(closest_on_axis, closest_on_obb);
988 let dist = length(diff);
989 if dist < capsule.radius {
990 let depth = capsule.radius - dist;
991 let normal = if dist > 1e-10 {
992 scale(diff, 1.0 / dist)
993 } else {
994 let center_diff = sub(closest_on_axis, obb.center);
995 let cl = length(center_diff);
996 if cl > 1e-10 {
997 scale(center_diff, 1.0 / cl)
998 } else {
999 [0.0, 1.0, 0.0]
1000 }
1001 };
1002 let contact_point = add(closest_on_obb, scale(normal, depth * 0.5));
1003 Some((depth, contact_point, normal))
1004 } else {
1005 None
1006 }
1007 }
1008}
1009pub struct ConvexPolyhedraSat;
1011impl ConvexPolyhedraSat {
1012 pub fn test(a: &ConvexPolyhedron, b: &ConvexPolyhedron) -> Option<PolyhedraContact> {
1017 let mut min_depth = f64::INFINITY;
1018 let mut best_normal = [0.0_f64, 1.0, 0.0];
1019 let mut best_feature = ContactFeature::FaceFace {
1020 face_a: 0,
1021 face_b: 0,
1022 };
1023 for (i, &n) in a.face_normals.iter().enumerate() {
1024 let len = length(n);
1025 if len < 1e-12 {
1026 continue;
1027 }
1028 let axis = scale(n, 1.0 / len);
1029 let (a_min, a_max) = a.project(axis);
1030 let (b_min, b_max) = b.project(axis);
1031 let overlap = a_max.min(b_max) - a_min.max(b_min);
1032 if overlap < 0.0 {
1033 return None;
1034 }
1035 if overlap < min_depth {
1036 min_depth = overlap;
1037 best_normal = axis;
1038 best_feature = ContactFeature::FaceFace {
1039 face_a: i,
1040 face_b: 0,
1041 };
1042 }
1043 }
1044 for (j, &n) in b.face_normals.iter().enumerate() {
1045 let len = length(n);
1046 if len < 1e-12 {
1047 continue;
1048 }
1049 let axis = scale(n, 1.0 / len);
1050 let (a_min, a_max) = a.project(axis);
1051 let (b_min, b_max) = b.project(axis);
1052 let overlap = a_max.min(b_max) - a_min.max(b_min);
1053 if overlap < 0.0 {
1054 return None;
1055 }
1056 if overlap < min_depth {
1057 min_depth = overlap;
1058 best_normal = axis;
1059 best_feature = ContactFeature::FaceFace {
1060 face_a: 0,
1061 face_b: j,
1062 };
1063 }
1064 }
1065 for (ia, &ea) in a.edge_directions.iter().enumerate() {
1066 for (ib, &eb) in b.edge_directions.iter().enumerate() {
1067 let axis = cross(ea, eb);
1068 let len = length(axis);
1069 if len < 1e-12 {
1070 continue;
1071 }
1072 let axis = scale(axis, 1.0 / len);
1073 let (a_min, a_max) = a.project(axis);
1074 let (b_min, b_max) = b.project(axis);
1075 let overlap = a_max.min(b_max) - a_min.max(b_min);
1076 if overlap < 0.0 {
1077 return None;
1078 }
1079 if overlap < min_depth {
1080 min_depth = overlap;
1081 best_normal = axis;
1082 best_feature = ContactFeature::EdgeEdge {
1083 edge_a: ia,
1084 edge_b: ib,
1085 };
1086 }
1087 }
1088 }
1089 Some(PolyhedraContact {
1090 normal: best_normal,
1091 depth: min_depth,
1092 feature: best_feature,
1093 })
1094 }
1095 pub fn is_separated(a: &ConvexPolyhedron, b: &ConvexPolyhedron) -> bool {
1097 Self::test(a, b).is_none()
1098 }
1099}
1100#[derive(Clone, Debug)]
1102pub struct ObbBvhNode {
1103 pub bounds: Obb,
1105 pub left: usize,
1107 pub right: usize,
1109 pub geometry_index: Option<usize>,
1111}
1112impl ObbBvhNode {
1113 pub fn is_leaf(&self) -> bool {
1115 self.left == usize::MAX && self.right == usize::MAX
1116 }
1117}