use super::simplex::{
compute_witness_points, get_metric, solve_simplex2, solve_simplex3, solve_simplex4, write_cache,
};
use super::types::{DistanceInput, DistanceOutput, ShapeProxy, Simplex, SimplexCache};
use crate::math_functions::{
add, blend2, blend3, cross, distance, dot, is_normalized, length_squared,
make_matrix_from_quat, max_float, mul_mv, mul_sv, neg, normalize, sub, transpose, Vec3,
VEC3_ZERO,
};
const MAX_SIMPLEX_VERTICES: i32 = 4;
const MAX_GJK_ITERATIONS: i32 = 32;
pub fn get_proxy_support(proxy: &ShapeProxy, axis: Vec3) -> i32 {
let count = proxy.count;
debug_assert!(count > 0);
let origin = proxy.points[0];
let mut max_index = 0;
let mut max_projection = 0.0;
for index in 1..count {
let projection = dot(axis, sub(proxy.points[index as usize], origin));
if projection > max_projection {
max_index = index;
max_projection = projection;
}
}
max_index
}
pub fn get_point_support(points: &[Vec3], axis: Vec3) -> i32 {
let count = points.len() as i32;
debug_assert!(count > 0);
let origin = points[0];
let mut max_index = 0;
let mut max_projection = 0.0;
for index in 1..count {
let projection = dot(axis, sub(points[index as usize], origin));
if projection > max_projection {
max_index = index;
max_projection = projection;
}
}
max_index
}
pub fn shape_distance(
input: &DistanceInput,
cache: &mut SimplexCache,
mut simplexes: Option<&mut [Simplex]>,
) -> DistanceOutput {
let xf = input.transform;
let m = make_matrix_from_quat(xf.q);
let mt = transpose(m);
let proxy_a = &input.proxy_a;
let proxy_b = &input.proxy_b;
debug_assert!(cache.count as i32 <= MAX_SIMPLEX_VERTICES);
let mut simplex = Simplex::default();
simplex.count = cache.count as i32;
for i in 0..cache.count as usize {
let index1 = cache.index_a[i] as i32;
let index2 = cache.index_b[i] as i32;
debug_assert!(0 <= index1 && index1 < proxy_a.count);
debug_assert!(0 <= index2 && index2 < proxy_b.count);
let vertex1 = proxy_a.points[index1 as usize];
let vertex2 = add(mul_mv(m, proxy_b.points[index2 as usize]), xf.p);
simplex.vertices[i].index_a = index1;
simplex.vertices[i].index_b = index2;
simplex.vertices[i].w_a = vertex1;
simplex.vertices[i].w_b = vertex2;
simplex.vertices[i].w = sub(vertex2, vertex1);
simplex.vertices[i].a = 0.0;
}
if simplex.count > 0 {
let metric1 = cache.metric;
let metric2 = get_metric(&simplex);
if 2.0 * metric1 < metric2 || metric2 < 0.5 * metric1 || metric2 < f32::EPSILON {
simplex.count = 0;
}
}
if simplex.count == 0 {
let vertex1 = proxy_a.points[0];
let vertex2 = add(mul_mv(m, proxy_b.points[0]), xf.p);
simplex.count = 1;
simplex.vertices[0].index_a = 0;
simplex.vertices[0].index_b = 0;
simplex.vertices[0].w_a = vertex1;
simplex.vertices[0].w_b = vertex2;
simplex.vertices[0].w = sub(vertex2, vertex1);
simplex.vertices[0].a = 0.0;
}
let mut backup = Simplex::default();
let mut simplex_index = 0usize;
if let Some(buffer) = simplexes.as_mut() {
if simplex_index < buffer.len() {
buffer[simplex_index] = simplex;
simplex_index += 1;
}
}
let mut distance_output = DistanceOutput::default();
let mut distance_sq = f32::MAX;
let mut normal = VEC3_ZERO;
let mut iteration = 0i32;
while iteration < MAX_GJK_ITERATIONS {
let solved = match simplex.count {
1 => {
simplex.vertices[0].a = 1.0;
true
}
2 => solve_simplex2(&mut simplex),
3 => solve_simplex3(&mut simplex),
4 => solve_simplex4(&mut simplex),
_ => {
debug_assert!(false, "Should never get here!");
false
}
};
if !solved {
debug_assert!(backup.count != 0);
simplex = backup;
break;
}
if let Some(buffer) = simplexes.as_mut() {
if simplex_index < buffer.len() {
buffer[simplex_index] = simplex;
simplex_index += 1;
distance_output.iterations = iteration;
distance_output.simplex_count = simplex_index as i32;
}
}
if simplex.count == MAX_SIMPLEX_VERTICES {
let (local_point_a, local_point_b) = compute_witness_points(&simplex);
distance_output.point_a = local_point_a;
distance_output.point_b = local_point_b;
return distance_output;
}
let old_distance_sq = distance_sq;
let closest_point = match simplex.count {
1 => simplex.vertices[0].w,
2 => blend2(
simplex.vertices[0].a,
simplex.vertices[0].w,
simplex.vertices[1].a,
simplex.vertices[1].w,
),
3 => blend3(
simplex.vertices[0].a,
simplex.vertices[0].w,
simplex.vertices[1].a,
simplex.vertices[1].w,
simplex.vertices[2].a,
simplex.vertices[2].w,
),
4 => add(
blend2(
simplex.vertices[0].a,
simplex.vertices[0].w,
simplex.vertices[1].a,
simplex.vertices[1].w,
),
blend2(
simplex.vertices[2].a,
simplex.vertices[2].w,
simplex.vertices[3].a,
simplex.vertices[3].w,
),
),
_ => {
debug_assert!(false, "Should never get here!");
VEC3_ZERO
}
};
distance_sq = dot(closest_point, closest_point);
if distance_sq >= old_distance_sq {
debug_assert!(backup.count != 0);
simplex = backup;
break;
}
let search_direction = match simplex.count {
1 => {
neg(simplex.vertices[0].w)
}
2 => {
let a = simplex.vertices[0].w;
let b = simplex.vertices[1].w;
let ab = sub(b, a);
cross(cross(ab, neg(a)), ab)
}
3 => {
let a = simplex.vertices[0].w;
let b = simplex.vertices[1].w;
let c = simplex.vertices[2].w;
let ab = sub(b, a);
let ac = sub(c, a);
let n = cross(ab, ac);
if dot(n, a) < 0.0 {
n
} else {
neg(n)
}
}
_ => {
debug_assert!(false, "Should never get here!");
VEC3_ZERO
}
};
if length_squared(search_direction) < 1000.0 * f32::MIN_POSITIVE {
let (local_point_a, local_point_b) = compute_witness_points(&simplex);
distance_output.point_a = local_point_a;
distance_output.point_b = local_point_b;
debug_assert!(distance(local_point_a, local_point_b) < f32::EPSILON);
return distance_output;
}
normal = neg(search_direction);
let search_direction1 = search_direction;
let index_a = get_proxy_support(&input.proxy_a, neg(search_direction1));
let support_a = input.proxy_a.points[index_a as usize];
let search_direction2 = mul_mv(mt, search_direction);
let index_b = get_proxy_support(&input.proxy_b, search_direction2);
let support_b = add(mul_mv(m, input.proxy_b.points[index_b as usize]), xf.p);
backup = simplex;
let mut duplicate = false;
for i in 0..simplex.count as usize {
if simplex.vertices[i].index_a == index_a && simplex.vertices[i].index_b == index_b {
duplicate = true;
break;
}
}
if duplicate {
break;
}
let count = simplex.count as usize;
simplex.vertices[count].index_a = index_a;
simplex.vertices[count].index_b = index_b;
simplex.vertices[count].w_a = support_a;
simplex.vertices[count].w_b = support_b;
simplex.vertices[count].w = sub(support_b, support_a);
simplex.count += 1;
iteration += 1;
}
normal = normalize(normal);
if !is_normalized(normal) {
return distance_output;
}
let (local_point_a, local_point_b) = compute_witness_points(&simplex);
write_cache(cache, &simplex);
distance_output.point_a = local_point_a;
distance_output.point_b = local_point_b;
distance_output.distance = distance(local_point_a, local_point_b);
distance_output.normal = normal;
distance_output.iterations = iteration;
distance_output.simplex_count = simplex_index as i32;
if input.use_radii {
let r_a = input.proxy_a.radius;
let r_b = input.proxy_b.radius;
distance_output.distance = max_float(0.0, distance_output.distance - r_a - r_b);
distance_output.point_a = add(distance_output.point_a, mul_sv(r_a, normal));
distance_output.point_b = sub(distance_output.point_b, mul_sv(r_b, normal));
}
distance_output
}