use crate::constants::overlap_slop;
use crate::core::NULL_INDEX;
use crate::distance::{
make_proxy, shape_cast, shape_distance, CastOutput, DistanceInput, ShapeCastPairInput,
ShapeProxy, SimplexCache,
};
use crate::geometry::types::{
Capsule, MassData, PlaneResult, RayCastInput, ShapeCastInput, ShapeExtent,
};
use crate::hull::types::{
get_hull_edges, get_hull_faces, get_hull_planes, get_hull_points, HullData,
};
use crate::math_functions::{
aabb_transform, aabb_union, abs, add, cross, dot, inv_mul_transforms, max, max_float, mul_sm,
mul_sv, sub, Aabb, Plane, Transform, Vec3, TRANSFORM_IDENTITY, VEC3_ZERO,
};
pub fn compute_hull_projected_area(hull: &HullData, direction: Vec3) -> f32 {
let mut area = 0.0;
let face_count = hull.face_count;
let hull_faces = get_hull_faces(hull);
let hull_edges = get_hull_edges(hull);
let hull_points = get_hull_points(hull);
for i in 0..face_count {
let face = &hull_faces[i as usize];
let base_edge = face.edge as i32;
let mut edge = &hull_edges[base_edge as usize];
let p1 = hull_points[edge.origin as usize];
let mut edge_index = edge.next as i32;
edge = &hull_edges[edge_index as usize];
let mut p2 = hull_points[edge.origin as usize];
edge_index = edge.next as i32;
loop {
edge = &hull_edges[edge_index as usize];
let p3 = hull_points[edge.origin as usize];
let e1 = sub(p2, p1);
let e2 = sub(p3, p1);
let n = cross(e1, e2);
let a = dot(n, direction);
area += max_float(a, 0.0);
p2 = p3;
edge_index = edge.next as i32;
if edge_index == base_edge {
break;
}
}
}
0.5 * area
}
pub fn compute_hull_mass(shape: &HullData, density: f32) -> MassData {
MassData {
mass: density * shape.volume,
center: shape.center,
inertia: mul_sm(density, shape.central_inertia),
}
}
pub fn compute_hull_extent(hull: &HullData, origin: Vec3) -> ShapeExtent {
let points = get_hull_points(hull);
let mut extent = ShapeExtent {
min_extent: hull.inner_radius,
max_extent: VEC3_ZERO,
};
for point in points {
extent.max_extent = max(extent.max_extent, abs(sub(*point, origin)));
}
extent
}
pub fn compute_hull_aabb(shape: &HullData, transform: Transform) -> Aabb {
aabb_transform(transform, shape.aabb)
}
pub fn compute_swept_hull_aabb(shape: &HullData, xf1: Transform, xf2: Transform) -> Aabb {
let aabb1 = aabb_transform(xf1, shape.aabb);
let aabb2 = aabb_transform(xf2, shape.aabb);
aabb_union(aabb1, aabb2)
}
pub fn overlap_hull(shape: &HullData, shape_transform: Transform, proxy: &ShapeProxy) -> bool {
let points = get_hull_points(shape);
let input = DistanceInput {
proxy_a: make_proxy(points, 0.0),
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_hull(shape: &HullData, input: &RayCastInput) -> CastOutput {
debug_assert!({
use crate::constants::huge;
use crate::math_functions::{is_valid_float, is_valid_vec3};
is_valid_vec3(input.origin)
&& is_valid_vec3(input.translation)
&& is_valid_float(input.max_fraction)
&& 0.0 <= input.max_fraction
&& input.max_fraction < huge()
});
let mut output = CastOutput::default();
let mut lower = 0.0;
let mut upper = input.max_fraction;
let mut best_face = NULL_INDEX;
let planes = get_hull_planes(shape);
for face_index in 0..shape.face_count {
let plane = planes[face_index as usize];
let distance = plane.offset - dot(plane.normal, input.origin);
let denominator = dot(plane.normal, input.translation);
if denominator == 0.0 {
if distance < 0.0 {
return output;
}
} else {
let fraction = distance / denominator;
if denominator < 0.0 {
if fraction > lower {
best_face = face_index;
lower = fraction;
}
} else if fraction < upper {
upper = fraction;
}
if upper < lower {
return output;
}
}
}
if best_face >= 0 {
output.point = add(input.origin, mul_sv(lower, input.translation));
output.normal = planes[best_face as usize].normal;
output.fraction = lower;
output.hit = true;
} else {
output.point = input.origin;
output.hit = true;
}
output
}
pub fn shape_cast_hull(shape: &HullData, input: &ShapeCastInput) -> CastOutput {
let points = get_hull_points(shape);
let pair_input = ShapeCastPairInput {
proxy_a: make_proxy(points, 0.0),
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_hull(result: &mut PlaneResult, shape: &HullData, mover: &Capsule) -> i32 {
let points = get_hull_points(shape);
let distance_input = DistanceInput {
proxy_a: make_proxy(points, 0.0),
proxy_b: make_proxy(&[mover.center1, mover.center2], mover.radius),
transform: TRANSFORM_IDENTITY,
use_radii: false,
};
let total_radius = mover.radius;
let mut cache = SimplexCache::default();
let distance_output = shape_distance(&distance_input, &mut cache, None);
if distance_output.distance == 0.0 {
return 0;
}
if distance_output.distance <= total_radius {
result.plane = Plane {
normal: distance_output.normal,
offset: total_radius - distance_output.distance,
};
result.point = distance_output.point_a;
return 1;
}
0
}