khora_core/physics/
collision.rs1use super::{ColliderShape, ContactManifold};
18use crate::math::{AffineTransform, Vec3, Vec4};
19
20pub struct NarrowPhase;
22
23impl NarrowPhase {
24 pub fn new() -> Self {
26 Self
27 }
28
29 pub fn detect(
33 &self,
34 shape_a: &ColliderShape,
35 trans_a: &AffineTransform,
36 shape_b: &ColliderShape,
37 trans_b: &AffineTransform,
38 ) -> Option<ContactManifold> {
39 match (shape_a, shape_b) {
40 (ColliderShape::Sphere(ra), ColliderShape::Sphere(rb)) => {
41 let pa = trans_a.translation();
42 let pb = trans_b.translation();
43 let delta = pb - pa;
44 let dist_sq = delta.length_squared();
45 let total_r = ra + rb;
46 if dist_sq < total_r * total_r {
47 let dist = dist_sq.sqrt();
48 let normal = if dist > 0.0001 {
49 delta / dist
50 } else {
51 Vec3::new(0.0, 1.0, 0.0)
52 };
53 Some(ContactManifold {
54 normal,
55 depth: total_r - dist,
56 point: pa + normal * (*ra),
57 })
58 } else {
59 None
60 }
61 }
62 (ColliderShape::Sphere(ra), ColliderShape::Box(half_b)) => {
63 let pa = trans_a.translation();
64 let inv_b = trans_b.inverse()?;
66 let local_pa = (inv_b.0 * Vec4::from_vec3(pa, 1.0)).truncate();
67
68 let closest = Vec3::new(
70 local_pa.x.clamp(-half_b.x, half_b.x),
71 local_pa.y.clamp(-half_b.y, half_b.y),
72 local_pa.z.clamp(-half_b.z, half_b.z),
73 );
74
75 let delta = local_pa - closest;
76 let dist_sq = delta.length_squared();
77 if dist_sq < ra * ra {
78 let dist = dist_sq.sqrt();
79 let local_normal = if dist > 0.0001 {
80 delta / dist
81 } else {
82 Vec3::new(0.0, 1.0, 0.0)
83 };
84 let normal = trans_b.rotation().rotate_vec3(local_normal);
86 Some(ContactManifold {
87 normal,
88 depth: ra - dist,
89 point: (trans_b.0 * Vec4::from_vec3(closest, 1.0)).truncate(),
90 })
91 } else {
92 None
93 }
94 }
95 (ColliderShape::Box(_half_a), ColliderShape::Sphere(_rb)) => self
97 .detect(shape_b, trans_b, shape_a, trans_a)
98 .map(|m| m.inverted()),
99 _ => None,
100 }
101 }
102}
103
104impl Default for NarrowPhase {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::math::Vec3;
114
115 #[test]
116 fn test_sphere_sphere_collision() {
117 let narrow = NarrowPhase::new();
118 let sphere_a = ColliderShape::Sphere(1.0);
119 let sphere_b = ColliderShape::Sphere(1.0);
120 let trans_a = AffineTransform::from_translation(Vec3::new(0.0, 0.0, 0.0));
121 let trans_b = AffineTransform::from_translation(Vec3::new(1.5, 0.0, 0.0));
122
123 let manifold = narrow
124 .detect(&sphere_a, &trans_a, &sphere_b, &trans_b)
125 .unwrap();
126 assert!(manifold.depth > 0.0);
127 assert!((manifold.normal.x - 1.0).abs() < 0.001);
128 assert!((manifold.depth - 0.5).abs() < 0.001);
129 }
130}