use super::types::{Capsule, MassData, PlaneResult, RayCastInput, ShapeCastInput, Sphere};
use crate::constants::{linear_slop, overlap_slop};
use crate::distance::{
make_proxy, shape_cast, shape_distance, CastOutput, DistanceInput, ShapeCastPairInput,
ShapeProxy, SimplexCache,
};
use crate::math_functions::{
add, get_length_and_normalize, inv_mul_transforms, length_squared, make_diagonal_matrix, max,
min, mul_add, normalize, perp, point_to_segment_distance, sub, transform_point, Aabb, Plane,
Transform, Vec3, TRANSFORM_IDENTITY, VEC3_AXIS_Y,
};
pub fn compute_sphere_mass(shape: &Sphere, density: f32) -> MassData {
let center = shape.center;
let radius = shape.radius;
let volume = 4.0 / 3.0 * crate::math_functions::PI * radius * radius * radius;
let mass = volume * density;
let ixx = 0.4 * mass * radius * radius;
MassData {
mass,
center,
inertia: make_diagonal_matrix(ixx, ixx, ixx),
}
}
pub fn compute_sphere_aabb(shape: &Sphere, transform: Transform) -> Aabb {
let center = transform_point(transform, shape.center);
let radius = shape.radius;
let extent = Vec3 {
x: radius,
y: radius,
z: radius,
};
Aabb {
lower_bound: sub(center, extent),
upper_bound: add(center, extent),
}
}
pub fn compute_swept_sphere_aabb(shape: &Sphere, xf1: Transform, xf2: Transform) -> Aabb {
let r = Vec3 {
x: shape.radius,
y: shape.radius,
z: shape.radius,
};
let center1 = transform_point(xf1, shape.center);
let center2 = transform_point(xf2, shape.center);
Aabb {
lower_bound: sub(min(center1, center2), r),
upper_bound: add(max(center1, center2), r),
}
}
pub fn overlap_sphere(shape: &Sphere, shape_transform: Transform, proxy: &ShapeProxy) -> bool {
let input = DistanceInput {
proxy_a: make_proxy(&[shape.center], shape.radius),
proxy_b: *proxy,
transform: inv_mul_transforms(shape_transform, TRANSFORM_IDENTITY),
use_radii: true,
};
let mut cache = SimplexCache::default();
let output = shape_distance(&input, &mut cache, None);
output.distance < overlap_slop()
}
pub fn ray_cast_sphere(shape: &Sphere, input: &RayCastInput) -> CastOutput {
debug_assert!(super::is_valid_ray(input));
let mut output = CastOutput::default();
let p = shape.center;
let s = sub(input.origin, p);
let r = shape.radius;
let rr = r * r;
let mut length = 0.0;
let d = get_length_and_normalize(&mut length, input.translation);
if length == 0.0 {
if length_squared(s) < rr {
output.point = input.origin;
output.hit = true;
}
return output;
}
let t = -crate::math_functions::dot(s, d);
let c = mul_add(s, t, d);
let cc = crate::math_functions::dot(c, c);
if cc > rr {
return output;
}
let h = (rr - cc).sqrt();
let fraction = t - h;
if fraction < 0.0 || input.max_fraction * length < fraction {
if length_squared(s) < rr {
output.point = input.origin;
output.hit = true;
}
return output;
}
let hit_point = mul_add(s, fraction, d);
output.fraction = fraction / length;
if output.fraction > input.max_fraction {
output.fraction = input.max_fraction;
}
output.normal = normalize(hit_point);
output.point = mul_add(p, shape.radius, output.normal);
output.hit = true;
output
}
pub fn ray_cast_hollow_sphere(sphere: &Sphere, input: &RayCastInput) -> CastOutput {
let p = sphere.center;
let mut output = CastOutput::default();
let s = sub(input.origin, p);
let d = normalize(input.translation);
let t = -crate::math_functions::dot(s, d);
let c = mul_add(s, t, d);
let cc = crate::math_functions::dot(c, c);
let r = sphere.radius;
let rr = r * r;
if cc > rr {
return output;
}
let h = (rr - cc).sqrt();
let mut fraction = t - h;
if fraction < 0.0 {
fraction = t + h;
}
if fraction < 0.0 {
return output;
}
if fraction > input.max_fraction {
return output;
}
let hit_point = mul_add(s, fraction, d);
output.fraction = fraction;
output.normal = normalize(hit_point);
output.point = mul_add(p, sphere.radius, output.normal);
output.hit = true;
output
}
pub fn shape_cast_sphere(sphere: &Sphere, input: &ShapeCastInput) -> CastOutput {
let pair_input = ShapeCastPairInput {
proxy_a: make_proxy(&[sphere.center], sphere.radius),
proxy_b: input.proxy,
transform: TRANSFORM_IDENTITY,
translation_b: input.translation,
max_fraction: input.max_fraction,
can_encroach: input.can_encroach,
};
shape_cast(&pair_input)
}
pub fn collide_mover_and_sphere(result: &mut PlaneResult, shape: &Sphere, mover: &Capsule) -> i32 {
let total_radius = mover.radius + shape.radius;
let closest = point_to_segment_distance(mover.center1, mover.center2, shape.center);
let mut distance = 0.0;
let mut normal = get_length_and_normalize(&mut distance, sub(closest, shape.center));
if distance > total_radius {
return 0;
}
let linear_slop = linear_slop();
if distance < linear_slop {
let mut length = 0.0;
let axis = get_length_and_normalize(&mut length, sub(mover.center2, mover.center1));
normal = if length > linear_slop {
perp(axis)
} else {
VEC3_AXIS_Y
};
distance = 0.0;
}
result.plane = Plane {
normal,
offset: total_radius - distance,
};
result.point = shape.center;
1
}