use core::ops::Not;
use glam_det::nums::{f32x4, u32x4, Bool, Float, Num, NumConstEx, PartialOrdEx};
use glam_det::{
Cross, Dot, Mat3x4, Point3, UnitQuat, UnitQuatx4, UnitVec3, UnitVec3x4, Vec3, Vec3x4,
};
use crate::collision_tasks::common::{NormalizeExt, EPS_3, EPS_5, EPS_8};
use crate::collision_tasks::traits::TransformWide;
use crate::collision_tasks::{tootbird, ShapeWideTester};
use crate::convex_contact_manifold::Convex4ContactManifoldWide;
use crate::shapes::{BundleIndex, CapsuleWide, ConvexHullWide};
use crate::traits::{ContactContext, ContactManifoldWide, CreateShapeWide, PairWideTest};
use crate::{
Capsule, ConvexContactManifold, ConvexHull, ConvexHullId, PairTest, ShapeContainer, ShapeTester,
};
impl PairWideTest<CapsuleWide, ConvexHullWide> for ShapeWideTester {
#[inline]
fn test(
a: &CapsuleWide,
b: &ConvexHullWide,
contact_context: &ContactContext,
manifold: &mut Convex4ContactManifoldWide,
) {
const MAXIMUM_ITERRATIONS: usize = 25;
let container = contact_context
.complex_shape_container
.expect("container must be Some");
let rb = Mat3x4::from_quat(*contact_context.orientation_b);
let rb_inverse = rb.transpose();
let ra = rb_inverse * Mat3x4::from_quat(*contact_context.orientation_a);
let ra_y_axis = &ra.y_axis;
let shift_b = b.get_convexhull_shift_wide(container, contact_context.pair_count);
let center_b = b.get_center(container, contact_context.pair_count);
let local_offset_a = -(rb_inverse * contact_context.offset_b) - shift_b;
let normal_b_to_a = (local_offset_a + center_b).normalize_or(Vec3x4::Y, EPS_8);
let mut inactive_lanes =
u32x4::splat(contact_context.pair_count as u32).le(u32x4::from([0, 1, 2, 3]));
let hull_epsilon_scale = b.estimate_epsilon_scale(inactive_lanes, container);
let epsilon_scale = a.radius.min(hull_epsilon_scale);
let minimum_accepted_depth = -contact_context.speculative_margin;
let toot_bird_result = tootbird::find_minimum_depth(
b,
a,
&TransformWide::new(local_offset_a, &ra),
normal_b_to_a,
&tootbird::IterContext::new(
inactive_lanes,
epsilon_scale * EPS_5,
minimum_accepted_depth,
MAXIMUM_ITERRATIONS,
contact_context.complex_shape_container,
true,
),
);
inactive_lanes = inactive_lanes | toot_bird_result.depth.lt(minimum_accepted_depth);
if inactive_lanes.all() {
manifold.reset(2);
return;
}
let bounding_plane_epsilon = EPS_3 * epsilon_scale;
let mut face_normal_x4 = UnitVec3x4::X;
let mut min_capsule_to_edge_x4 = f32x4::ZERO;
let mut min_direction_a_dot_edge_plane_normal_x4 = f32x4::ZERO;
let mut max_capsule_to_edge_x4 = f32x4::ZERO;
let mut max_direction_a_dot_edge_plane_normal_x4 = f32x4::ZERO;
for (lane_index, inactive) in <[bool; 4]>::from(inactive_lanes)
.into_iter()
.enumerate()
.take(contact_context.pair_count)
{
if inactive {
continue;
}
let hull_id = b.get_id(lane_index);
let hull = container
.get::<ConvexHull>(hull_id)
.expect("hull must be Some");
let face = hull.pick_representative_face(
toot_bird_result.normal.extract_lane(lane_index),
Point3::from_vec3(toot_bird_result.closest_point_on_a.extract_lane(lane_index)),
bounding_plane_epsilon.extract(lane_index),
);
let face_vertex_indices = hull.get_vertex_indices(face.face_index);
face_normal_x4.replace_lane(lane_index, face.normal);
let (
min_capsule_to_edge,
min_direction_a_dot_edge_plane_normal,
max_capsule_to_edge,
max_direction_a_dot_edge_plane_normal,
) = select_range_on_segment_vs_hull_face(
ra.y_axis.extract_lane(lane_index),
local_offset_a.extract_lane(lane_index),
a.half_height.extract(lane_index),
toot_bird_result.normal.extract_lane(lane_index),
hull,
face_vertex_indices,
);
min_capsule_to_edge_x4.replace(lane_index, min_capsule_to_edge);
min_direction_a_dot_edge_plane_normal_x4
.replace(lane_index, min_direction_a_dot_edge_plane_normal);
max_capsule_to_edge_x4.replace(lane_index, max_capsule_to_edge);
max_direction_a_dot_edge_plane_normal_x4
.replace(lane_index, max_direction_a_dot_edge_plane_normal);
}
let mut t_min = min_capsule_to_edge_x4 / min_direction_a_dot_edge_plane_normal_x4;
let mut t_max = max_capsule_to_edge_x4 / max_direction_a_dot_edge_plane_normal_x4;
let negative_half_length = -a.half_height;
t_min = t_min.clamp(negative_half_length, a.half_height);
t_max = t_max.clamp(negative_half_length, a.half_height);
let local_offset_0 = ra_y_axis * t_min;
let local_offset_1 = ra_y_axis * t_max;
let a_to_point_on_hull_face = toot_bird_result.closest_point_on_a - local_offset_a;
let scale = face_normal_x4.dot(toot_bird_result.normal.as_vec3x4());
let scale_recip = scale.recip();
let contact0_to_hull_face = a_to_point_on_hull_face - local_offset_0;
let contact1_to_hull_face = a_to_point_on_hull_face - local_offset_1;
let depth_on_face_normal_0 = face_normal_x4.dot(contact0_to_hull_face);
let depth_on_face_normal_1 = face_normal_x4.dot(contact1_to_hull_face);
let depth_on_closest_normal_0 = depth_on_face_normal_0 * scale_recip;
let depth_on_closest_normal_1 = depth_on_face_normal_1 * scale_recip;
manifold.depth[0] = a.radius + depth_on_closest_normal_0;
manifold.depth[1] = a.radius + depth_on_closest_normal_1;
manifold.feature_id[0] = u32x4::splat(0);
manifold.feature_id[1] = u32x4::splat(1);
manifold.contact_exists[0] =
manifold.depth[0].ge(minimum_accepted_depth) & (inactive_lanes.not());
manifold.contact_exists[1] = (t_max - t_min).gt(a.half_height * EPS_3)
& manifold.depth[1].ge(minimum_accepted_depth)
& (inactive_lanes.not());
manifold.offset_a[0] = rb * local_offset_0;
manifold.offset_a[1] = rb * local_offset_1;
manifold.normal = (rb * toot_bird_result.normal).as_unit_vec3x4_unchecked();
let contact_offset_0 = manifold.normal * depth_on_closest_normal_0;
let contact_offset_1 = manifold.normal * depth_on_closest_normal_1;
manifold.offset_a[0] += contact_offset_0;
manifold.offset_a[1] += contact_offset_1;
manifold.offset_a[0] -= manifold.normal * manifold.depth[0];
manifold.offset_a[1] -= manifold.normal * manifold.depth[1];
}
#[inline]
fn should_reset_manifold_before_test() -> bool {
false
}
}
pub(super) fn select_range_on_segment_vs_hull_face(
direction_a: Vec3,
local_offset_a: Vec3,
a_half_length: f32,
contact_normal: UnitVec3,
hull: &ConvexHull,
face_vertex_indices: &[BundleIndex],
) -> (f32, f32, f32, f32) {
const MIN: f32 = 1e-5;
const MAX: f32 = 3e-4;
const INVERSE_SPAN: f32 = 1.0f32 / (MAX - MIN);
let last_index = face_vertex_indices[face_vertex_indices.len() - 1];
let mut vertex = hull.get_point(last_index);
let mut min_segment_to_edge = f32::MAX;
let mut min_direction_a_dot_edge_plane_normal = -1.0;
let mut max_segment_to_edge = f32::MAX;
let mut max_direction_a_dot_edge_plane_normal = 1.0;
for ¤t_index in face_vertex_indices {
let neighbour = hull.get_point(current_index);
let edge_offset = neighbour - vertex;
let edge_plane_normal = edge_offset.cross(contact_normal);
let a_to_vertex = vertex.as_vec3() - local_offset_a;
let mut segment_to_edge_on_plane_normal = a_to_vertex.dot(edge_plane_normal);
let direction_a_dot_edge_plane_normal = direction_a.dot(edge_plane_normal);
vertex = neighbour;
let edge_length_squared = edge_offset.length_squared();
let scale_squared = direction_a_dot_edge_plane_normal * direction_a_dot_edge_plane_normal;
if scale_squared > MIN * edge_length_squared {
if scale_squared < MAX * edge_length_squared {
let mut restrict_weight =
(scale_squared / edge_length_squared - MIN) * INVERSE_SPAN;
restrict_weight = restrict_weight.clamp(0.0, 1.0);
let mut unrestricted_numerator = a_half_length * direction_a_dot_edge_plane_normal;
if direction_a_dot_edge_plane_normal < 0f32 {
unrestricted_numerator = -unrestricted_numerator;
}
segment_to_edge_on_plane_normal = restrict_weight * segment_to_edge_on_plane_normal
+ (1.0 - restrict_weight) * unrestricted_numerator;
}
if direction_a_dot_edge_plane_normal < 0f32 {
if segment_to_edge_on_plane_normal * min_direction_a_dot_edge_plane_normal
> min_segment_to_edge * direction_a_dot_edge_plane_normal
{
min_segment_to_edge = segment_to_edge_on_plane_normal;
min_direction_a_dot_edge_plane_normal = direction_a_dot_edge_plane_normal;
}
} else if segment_to_edge_on_plane_normal * max_direction_a_dot_edge_plane_normal
< max_segment_to_edge * direction_a_dot_edge_plane_normal
{
max_segment_to_edge = segment_to_edge_on_plane_normal;
max_direction_a_dot_edge_plane_normal = direction_a_dot_edge_plane_normal;
}
}
}
(
min_segment_to_edge,
min_direction_a_dot_edge_plane_normal,
max_segment_to_edge,
max_direction_a_dot_edge_plane_normal,
)
}
#[cfg(test)]
mod tests {
use approx_det::assert_relative_eq;
use glam_det::nums::{f32x4, Num};
use glam_det::{Isometry3, UnitQuat, UnitQuatx4, Vec3, Vec3x4};
use wasm_bindgen_test::wasm_bindgen_test;
use crate::collision_tasks::ShapeWideTester;
use crate::convex_contact_manifold::Convex4ContactManifoldWide;
use crate::shapes::{CapsuleWide, ConvexHullWide, CuboidExt};
use crate::traits::{ContactContext, CreateShapeWide, PairWideTest};
use crate::{Capsule, ConvexHull, Cuboid, ShapeContainer};
#[test]
#[wasm_bindgen_test]
fn test_contact_point_is_on_face() {
let _ = env_logger::builder().is_test(true).try_init();
let a = Capsule::new(0.5, 0.5);
let b = Cuboid::new(Vec3::splat(2.0));
let points_b = (0..8).map(|i| b.get_vertex(i)).collect::<Vec<_>>();
let b = ConvexHull::new_unchecked(&points_b);
let mut container = ShapeContainer::default();
let b = container.add(b);
let transform_a = Isometry3::IDENTITY;
let transform_b = Isometry3::from_translation(Vec3::new(0.0, 0.0, 1.4));
let a_wide = <CapsuleWide as CreateShapeWide<1>>::create([&a].into_iter());
let b_wide = <ConvexHullWide as CreateShapeWide<1>>::create([&b].into_iter());
let speculative_margin = f32x4::splat(0.01);
let offset_b = Vec3x4::splat_soa(transform_b.translation - transform_a.translation);
let orientation_a = UnitQuatx4::splat_soa(transform_a.rotation);
let orientation_b = UnitQuatx4::splat_soa(transform_b.rotation);
let mut manifold = Convex4ContactManifoldWide::default();
let context = ContactContext {
orientation_a: &orientation_a,
orientation_b: &orientation_b,
offset_b: &offset_b,
speculative_margin,
complex_shape_container: Some(&container),
pair_count: 1,
};
ShapeWideTester::test(&a_wide, &b_wide, &context, &mut manifold);
assert_relative_eq!(manifold.offset_a[0].extract_lane(0).z, 0.5f32);
assert_relative_eq!(manifold.offset_a[1].extract_lane(0).z, 0.5f32);
}
#[test]
#[wasm_bindgen_test]
fn test_contact_point_is_on_face2() {
let _ = env_logger::builder().is_test(true).try_init();
let a = Capsule::new(0.5, 0.5);
let b = Cuboid::new(Vec3::splat(2.0));
let points_b = (0..8).map(|i| b.get_vertex(i)).collect::<Vec<_>>();
let b = ConvexHull::new_unchecked(&points_b);
let mut container = ShapeContainer::default();
let b = container.add(b);
let transform_a = Isometry3::from_quat(UnitQuat::from_rotation_x(90f32.to_radians()));
let transform_b = Isometry3::from_translation(Vec3::new(0.0, 0.0, 1.9));
let a_wide = <CapsuleWide as CreateShapeWide<1>>::create([&a].into_iter());
let b_wide = <ConvexHullWide as CreateShapeWide<1>>::create([&b].into_iter());
let speculative_margin = f32x4::splat(0.01);
let offset_b = Vec3x4::splat_soa(transform_b.translation - transform_a.translation);
let orientation_a = UnitQuatx4::splat_soa(transform_a.rotation);
let orientation_b = UnitQuatx4::splat_soa(transform_b.rotation);
let mut manifold = Convex4ContactManifoldWide::default();
let context = ContactContext {
orientation_a: &orientation_a,
orientation_b: &orientation_b,
offset_b: &offset_b,
speculative_margin,
complex_shape_container: Some(&container),
pair_count: 1,
};
ShapeWideTester::test(&a_wide, &b_wide, &context, &mut manifold);
assert_relative_eq!(manifold.offset_a[0].extract_lane(0).z, 0.0f32);
assert_relative_eq!(manifold.offset_a[1].extract_lane(0).z, 1.0f32);
}
}
impl_pair_narrowphase!(Capsule, ConvexHullId, CapsuleWide, ConvexHullWide, 2);