use glam::Vec3A;
use crate::bullet::{
collision::{
narrowphase::persistent_manifold::{ContactAddedCallback, PersistentManifold},
shapes::{compound_shape::CompoundShape, sphere_shape::SphereShape},
},
dynamics::rigid_body::RigidBody,
linear_math::AffineExt,
};
fn get_sphere_penetration(box_extents: Vec3A, sphere_from_local: Vec3A) -> (f32, Vec3A, Vec3A) {
let mut min_dist = box_extents.x - sphere_from_local.x;
let mut closest = sphere_from_local;
closest.x = box_extents.x;
let mut normal = Vec3A::X;
let mut test_face = |face_dist: f32, axis: usize, sign: f32| {
if face_dist < min_dist {
min_dist = face_dist;
closest = sphere_from_local;
closest[axis] = box_extents[axis] * sign;
normal = Vec3A::ZERO;
normal[axis] = sign;
}
};
test_face(box_extents.x + sphere_from_local.x, 0, -1.0);
test_face(box_extents.y - sphere_from_local.y, 1, 1.0);
test_face(box_extents.y + sphere_from_local.y, 1, -1.0);
test_face(box_extents.z - sphere_from_local.z, 2, 1.0);
test_face(box_extents.z + sphere_from_local.z, 2, -1.0);
(min_dist, closest, normal)
}
pub fn process_collision<T: ContactAddedCallback>(
sphere_obj: &RigidBody,
sphere_shape: &SphereShape,
obb_obj: &RigidBody,
obb_shape: &CompoundShape,
contact_added_callback: &mut T,
out: &mut Option<PersistentManifold>,
) {
debug_assert!(out.is_none());
let sphere_trans = sphere_obj.get_world_trans();
let aabb_1 = sphere_shape.get_aabb(sphere_trans.translation);
let org_trans = obb_obj.get_world_trans();
let aabb_2 = obb_shape.get_aabb(org_trans);
if !aabb_1.intersects(&aabb_2) {
return;
}
let child_trans = &obb_shape.child_trans;
let new_child_world_trans = org_trans * child_trans;
let box_shape = &obb_shape.child_shape;
let box_extents = box_shape.get_half_extents();
let sphere_from_local = new_child_world_trans.inv_x_form(sphere_trans.translation);
let mut closest = sphere_from_local.clamp(-box_extents, box_extents);
let mut delta = sphere_from_local - closest;
let dist_sq = delta.length_squared();
let radius = sphere_shape.get_radius();
let box_margin = box_shape.get_margin();
let mut manifold = PersistentManifold::new(sphere_obj, obb_obj);
let intersection_dist = radius + box_margin;
let contact_dist = intersection_dist + manifold.contact_breaking_threshold;
if dist_sq > contact_dist * contact_dist {
return;
}
let mut dist;
if dist_sq > f32::EPSILON {
dist = dist_sq.sqrt();
delta /= dist;
} else {
(dist, closest, delta) = get_sphere_penetration(box_extents, sphere_from_local);
dist *= -1.0;
};
let normal_on_box = new_child_world_trans.transform_vector3a(delta);
let point_on_box = new_child_world_trans.transform_point3a(closest);
let depth = dist - intersection_dist;
let point_on_box_plus_margin = point_on_box + (normal_on_box * box_margin);
manifold.add_contact_point(
sphere_obj,
obb_obj,
normal_on_box,
point_on_box_plus_margin,
depth,
None,
contact_added_callback,
);
manifold.refresh_contact_points(sphere_obj, obb_obj);
if manifold.point_cache.is_empty() {
return;
}
*out = Some(manifold);
}