use glam_det::nums::{bool32x4, f32x4, u32x4, Float, Num, NumConstEx, PartialOrdEx, Signed};
use glam_det::{Cross, Dot, UnitVec3x4, Vec3x4};
use crate::collision_tasks::cuboid_cuboid_tester_helper::structure::{
EdgeCoordinate, FaceVertices, PointEdgeCoordinates, Vec3x4WithLengthInfo,
};
use crate::collision_tasks::traits::{Axis, AxisInfo, TransformativeWide};
use crate::convex_contact_manifold::ManifoldCandidateWide;
use crate::shapes::CuboidWide;
use crate::traits::OrientationWide;
pub(super) const MAX_U32X4: u32x4 = u32x4::const_splat(u32::MAX);
pub(super) const THREE_U32X4: u32x4 = u32x4::const_splat(3);
pub(super) const FOUR_U32X4: u32x4 = u32x4::const_splat(4);
pub(super) const EIGHT_U32X4: u32x4 = u32x4::const_splat(8);
pub(super) const SIXTEEN_U32X4: u32x4 = u32x4::const_splat(16);
pub(super) const LOCAL_X_ID: u32x4 = u32x4::ONE;
pub(super) const LOCAL_Y_ID: u32x4 = FOUR_U32X4;
pub(super) const LOCAL_Z_ID: u32x4 = SIXTEEN_U32X4;
const EDGE_FEATURE_ID_OFFSET: u32x4 = u32x4::const_splat(64);
pub use structure::Candidates;
mod structure {
use std::ops::Index;
use glam_det::nums::{
bool32x4, f32x4, i32x4, u32x4, Bool, Float, Num, NumConstEx, PartialOrdEx, Signed,
};
use glam_det::{Dot, UnitVec3x4, Vec3x4};
use crate::collision_tasks::cuboid_cuboid_tester_helper::{
DotProductWide, FacePairInfo, MultipliedByMatrixWide, ReduceContext, Vec3x4ElementAccess,
};
use crate::collision_tasks::manifold_candidate_helper::conditional_select;
use crate::collision_tasks::traits::{Axis, AxisInfo};
use crate::convex_contact_manifold::ManifoldCandidateWide;
pub(super) struct Vec3x4WithLengthInfo {
pub value: Vec3x4,
pub x_square: f32x4,
pub y_square: f32x4,
pub z_square: f32x4,
}
impl Vec3x4WithLengthInfo {
#[inline]
pub fn new(value: Vec3x4) -> Self {
Self {
value,
x_square: value.x * value.x,
y_square: value.y * value.y,
z_square: value.z * value.z,
}
}
}
impl Vec3x4ElementAccess for UnitVec3x4 {
#[inline]
fn x(&self) -> f32x4 {
self.x
}
#[inline]
fn y(&self) -> f32x4 {
self.y
}
#[inline]
fn z(&self) -> f32x4 {
self.z
}
#[inline]
fn abs(&self) -> Self {
UnitVec3x4::abs(*self)
}
}
impl Vec3x4ElementAccess for Vec3x4 {
#[inline]
fn x(&self) -> f32x4 {
self.x
}
#[inline]
fn y(&self) -> f32x4 {
self.y
}
#[inline]
fn z(&self) -> f32x4 {
self.z
}
#[inline]
fn abs(&self) -> Self {
Vec3x4::abs(*self)
}
}
pub(super) struct Vec3x4Ignoring<T: Vec3x4ElementAccess, const IGNORE: usize> {
value: T,
}
impl<T: Vec3x4ElementAccess, const IGNORE: usize> Vec3x4Ignoring<T, IGNORE> {
#[inline]
pub fn new(value: T) -> Self {
Self { value }
}
#[inline]
pub fn abs(&self) -> Self {
Self::new(self.value.abs())
}
}
impl<T: Vec3x4ElementAccess> MultipliedByMatrixWide for Vec3x4Ignoring<T, 0> {
type ValueType = Vec3x4;
#[inline]
fn multiplied_by(&self, other: &impl AxisInfo) -> Self::ValueType {
let x = self.value.y() * other.x_axis().y + self.value.z() * other.x_axis().z;
let y = self.value.y() * other.y_axis().y + self.value.z() * other.y_axis().z;
let z = self.value.y() * other.z_axis().y + self.value.z() * other.z_axis().z;
Vec3x4::new(x, y, z)
}
}
impl<T: Vec3x4ElementAccess> MultipliedByMatrixWide for Vec3x4Ignoring<T, 1> {
type ValueType = Vec3x4;
#[inline]
fn multiplied_by(&self, other: &impl AxisInfo) -> Self::ValueType {
let x = self.value.x() * other.x_axis().x + self.value.z() * other.x_axis().z;
let y = self.value.x() * other.y_axis().x + self.value.z() * other.y_axis().z;
let z = self.value.x() * other.z_axis().x + self.value.z() * other.z_axis().z;
Vec3x4::new(x, y, z)
}
}
impl<T: Vec3x4ElementAccess> MultipliedByMatrixWide for Vec3x4Ignoring<T, 2> {
type ValueType = Vec3x4;
#[inline]
fn multiplied_by(&self, other: &impl AxisInfo) -> Self::ValueType {
let x = self.value.x() * other.x_axis().x + self.value.y() * other.x_axis().y;
let y = self.value.x() * other.y_axis().x + self.value.y() * other.y_axis().y;
let z = self.value.x() * other.z_axis().x + self.value.y() * other.z_axis().y;
Vec3x4::new(x, y, z)
}
}
impl<T: Vec3x4ElementAccess> DotProductWide for Vec3x4Ignoring<T, 0> {
type ValueType = f32x4;
#[inline]
fn dot(&self, other: &impl Vec3x4ElementAccess) -> Self::ValueType {
self.value.y() * other.y() + self.value.z() * other.z()
}
}
impl<T: Vec3x4ElementAccess> DotProductWide for Vec3x4Ignoring<T, 1> {
type ValueType = f32x4;
#[inline]
fn dot(&self, other: &impl Vec3x4ElementAccess) -> Self::ValueType {
self.value.x() * other.x() + self.value.z() * other.z()
}
}
impl<T: Vec3x4ElementAccess> DotProductWide for Vec3x4Ignoring<T, 2> {
type ValueType = f32x4;
#[inline]
fn dot(&self, other: &impl Vec3x4ElementAccess) -> Self::ValueType {
self.value.x() * other.x() + self.value.y() * other.y()
}
}
pub struct AxisDepthInfo {
pub normal: UnitVec3x4,
pub depth: f32x4,
}
impl Default for AxisDepthInfo {
#[inline]
fn default() -> Self {
Self {
normal: UnitVec3x4::X,
depth: f32x4::ZERO,
}
}
}
impl AxisDepthInfo {
#[inline]
pub fn new(normal: UnitVec3x4, depth: f32x4) -> Self {
Self { normal, depth }
}
#[inline]
pub fn update(&mut self, other: &AxisDepthInfo) {
let use_candidate = other.depth.lt(self.depth);
self.depth = other.depth.select(use_candidate, self.depth);
self.normal = UnitVec3x4::lane_select(use_candidate, other.normal, self.normal);
}
#[inline]
pub(super) fn normal_by_ignore<const IGNORE: usize>(
&self,
) -> Vec3x4Ignoring<UnitVec3x4, IGNORE> {
Vec3x4Ignoring::<_, IGNORE>::new(self.normal)
}
}
pub struct EdgeClipContext {
pub pair_count: usize,
pub allow_contacts: bool32x4,
pub epsilon_scale: f32x4,
pub feature_id_x0: u32x4,
pub feature_id_x1: u32x4,
pub feature_id_y0: u32x4,
pub feature_id_y1: u32x4,
}
impl EdgeClipContext {
#[inline]
pub fn new(
pair_count: usize,
allow_contacts: bool32x4,
epsilon_scale: f32x4,
feature_id_x0: u32x4,
feature_id_x1: u32x4,
feature_id_y0: u32x4,
feature_id_y1: u32x4,
) -> Self {
Self {
pair_count,
allow_contacts,
epsilon_scale,
feature_id_x0,
feature_id_x1,
feature_id_y0,
feature_id_y1,
}
}
}
pub struct VertexClipContext {
pub pair_count: usize,
pub allow_contacts: bool32x4,
pub feature_id_v00: u32x4,
pub feature_id_v01: u32x4,
pub feature_id_v10: u32x4,
pub feature_id_v11: u32x4,
}
impl VertexClipContext {
#[inline]
pub fn new(
pair_count: usize,
allow_contacts: bool32x4,
feature_id_v00: u32x4,
feature_id_v01: u32x4,
feature_id_v10: u32x4,
feature_id_v11: u32x4,
) -> Self {
Self {
pair_count,
allow_contacts,
feature_id_v00,
feature_id_v01,
feature_id_v10,
feature_id_v11,
}
}
}
pub struct Candidates<const MAX_COUNT: usize> {
pub value: [ManifoldCandidateWide; MAX_COUNT],
pub count: u32x4,
}
impl<const MAX_COUNT: usize> Default for Candidates<MAX_COUNT> {
#[inline]
fn default() -> Self {
Self {
value: [ManifoldCandidateWide::default(); MAX_COUNT],
count: u32x4::ZERO,
}
}
}
impl<const MAX_COUNT: usize> Candidates<MAX_COUNT> {
#[inline]
pub fn reduce_cuboid_pair(
&mut self,
face_pair: &FacePairInfo,
minimum_depth: f32x4,
epsilon_scale: f32x4,
pair_count: usize,
contact_exists: &mut [bool32x4; 4],
) {
let b_center_to_a_center = face_pair.a.center - face_pair.b.center;
let reduce_context = ReduceContext {
face_a_normal: &face_pair.a.normal,
b_center_to_a_center: &b_center_to_a_center,
face_b_tangent_x: &face_pair.b.tangent_x,
face_b_tangent_y: &face_pair.b.tangent_y,
};
self.reduce(
&reduce_context,
minimum_depth,
epsilon_scale,
pair_count,
face_pair.contact_normal,
contact_exists,
);
}
#[inline]
pub fn reduce(
&mut self,
context: &ReduceContext,
minimum_depth: f32x4,
epsilon_scale: f32x4,
pair_count: usize,
contact_normal: &UnitVec3x4,
contact_exists: &mut [bool32x4; 4],
) {
let (max_count, mask_contact_count) = self.calculate_depths_and_get_max_count(
context,
contact_normal,
minimum_depth,
pair_count,
);
self.reduce_by_make_max_area(
minimum_depth,
epsilon_scale,
contact_exists,
max_count,
mask_contact_count,
);
}
fn reduce_by_make_max_area(
&mut self,
minimum_depth: f32x4,
epsilon_scale: f32x4,
contact_exists: &mut [bool32x4; 4],
max_count: usize,
mask_contact_count: u32x4,
) {
if max_count == 0 {
contact_exists.iter_mut().for_each(
#[inline]
|c| *c = bool32x4::FALSE,
);
return;
}
let mut contacts = [ManifoldCandidateWide::default(); 4];
let invalid_score: f32x4 = -f32x4::MAX;
let mut best_score = invalid_score;
let extremity_scale = epsilon_scale * f32x4::splat(1e-2f32);
for (i, candidate) in self.value.iter().take(max_count).enumerate() {
let exist =
Self::candidate_exists(candidate.depth, minimum_depth, mask_contact_count, i);
let extremity_score =
(candidate.x * f32x4::splat(0.51) + candidate.y * f32x4::splat(0.6)).absf();
let extremity_score = extremity_score * extremity_scale;
let extremity_score =
extremity_score.select(candidate.depth.ge(f32x4::ZERO), f32x4::ZERO);
let score = candidate.depth + extremity_score;
let is_highest_score = exist & score.gt(best_score);
contacts[0] = conditional_select(is_highest_score, candidate, &contacts[0]);
best_score = score.select(is_highest_score, best_score);
}
contact_exists[0] = best_score.ne(invalid_score);
let mut max_distance_squared = f32x4::ZERO;
for (i, candidate) in self.value.iter().take(max_count).enumerate() {
let offset_x = candidate.x - contacts[0].x;
let offset_y = candidate.y - contacts[0].y;
let distance_squared = offset_x * offset_x + offset_y * offset_y;
let exist =
Self::candidate_exists(candidate.depth, minimum_depth, mask_contact_count, i);
let candidate_is_most_distant = distance_squared.gt(max_distance_squared) & exist;
max_distance_squared =
distance_squared.select(candidate_is_most_distant, max_distance_squared);
contacts[1] =
conditional_select(candidate_is_most_distant, candidate, &contacts[1]);
}
contact_exists[1] =
max_distance_squared.gt(epsilon_scale * epsilon_scale * f32x4::splat(1e-6f32));
let edge_offset_x = contacts[1].x - contacts[0].x;
let edge_offset_y = contacts[1].y - contacts[0].y;
let mut min_signed_area = f32x4::ZERO;
let mut max_signed_area = f32x4::ZERO;
for (i, candidate) in self.value.iter().take(max_count).enumerate() {
let candidate_offset_x = candidate.x - contacts[0].x;
let candidate_offset_y = candidate.y - contacts[0].y;
let mut signed_area =
candidate_offset_x * edge_offset_y - candidate_offset_y * edge_offset_x;
signed_area = (signed_area * f32x4::splat(0.2f32))
.select(candidate.depth.lt(f32x4::ZERO), signed_area);
let exist =
Self::candidate_exists(candidate.depth, minimum_depth, mask_contact_count, i);
let is_min_area = signed_area.lt(min_signed_area) & exist;
min_signed_area = signed_area.select(is_min_area, min_signed_area);
contacts[2] = conditional_select(is_min_area, candidate, &contacts[2]);
let is_max_area = signed_area.gt(max_signed_area) & exist;
max_signed_area = signed_area.select(is_max_area, max_signed_area);
contacts[3] = conditional_select(is_max_area, candidate, &contacts[3]);
}
let epsilon = max_distance_squared * max_distance_squared * f32x4::splat(1e-6f32);
contact_exists[2] = (min_signed_area * min_signed_area).gt(epsilon);
contact_exists[3] = (max_signed_area * max_signed_area).gt(epsilon);
contacts.iter().enumerate().for_each(
#[inline]
|(i, &candidate)| {
self.value[i] = candidate;
},
);
}
#[inline]
fn candidate_exists(
candidate_depth: f32x4,
minimum_depth: f32x4,
raw_contact_count: u32x4,
i: usize,
) -> bool32x4 {
candidate_depth.gt(minimum_depth) & u32x4::splat(i as u32).lt(raw_contact_count)
}
#[inline]
pub(super) fn masked_contact_count(raw_contact_count: u32x4, pair_count: usize) -> u32x4 {
let mut result = raw_contact_count;
let max_wide_len = i32x4::lanes();
for i in pair_count..max_wide_len {
result.replace(i, 0);
}
result
}
#[inline]
pub(super) fn max_count(max_candidate_count: usize, masked_contact_count: u32x4) -> usize {
#[allow(clippy::range_plus_one)]
if let Some(index) = (0..(max_candidate_count + 1)).rposition(
#[inline]
|x| masked_contact_count.eq((x as u32).into()).any(),
) {
return index;
};
max_candidate_count
}
#[inline]
pub fn calculate_depths_and_get_max_count(
&mut self,
context: &ReduceContext,
contact_normal: &UnitVec3x4,
minimum_depth: f32x4,
pair_count: usize,
) -> (usize, u32x4) {
let mut max_count = self.value.len();
let masked_contact_count = Self::masked_contact_count(self.count, pair_count);
max_count = Self::max_count(max_count, masked_contact_count);
let inverse_face_a_normal_dot_contact_normal =
context.face_a_normal.dot(contact_normal).recip();
let axis = context.face_a_normal * inverse_face_a_normal_dot_contact_normal;
let neg_center_dot = context.b_center_to_a_center.dot(axis);
let x_dot = context.face_b_tangent_x.dot(axis);
let y_dot = context.face_b_tangent_y.dot(axis);
for candidate in self.value.iter_mut().take(max_count) {
candidate.depth = x_dot * candidate.x + y_dot * candidate.y - neg_center_dot;
}
if let Some(i) = self.value.iter().enumerate().rposition(
#[inline]
|(i, candidate)| {
Self::candidate_exists(candidate.depth, minimum_depth, masked_contact_count, i)
.any()
},
) {
return (i + 1, masked_contact_count);
}
(max_count, masked_contact_count)
}
#[inline]
pub fn add(
&mut self,
candidate: &ManifoldCandidateWide,
new_contact_exists: bool32x4,
pair_count: usize,
) {
let new_contact_exists_array: [bool; 4] = new_contact_exists.into();
let candidate_x_array: [f32; 4] = candidate.x.into();
let candidate_y_array: [f32; 4] = candidate.y.into();
let candidate_feature_id_array: [u32; 4] = candidate.feature_id.into();
let count_array: [u32; 4] = (self.count).into();
for index in (0..pair_count).filter(
#[inline]
|&x| new_contact_exists_array[x],
) {
let target_index = count_array[index] as usize;
let c = &mut self.value[target_index];
c.x.replace(index, candidate_x_array[index]);
c.y.replace(index, candidate_y_array[index]);
c.feature_id
.replace(index, candidate_feature_id_array[index]);
}
self.count = (self.count + u32x4::ONE).select(new_contact_exists, self.count);
}
}
pub struct EdgeCoordinate {
pub min: f32x4,
pub max: f32x4,
}
impl EdgeCoordinate {
#[inline]
pub fn flip(&self) -> Self {
let flipped_min = -self.max;
let flipped_max = -self.min;
Self {
min: flipped_min,
max: flipped_max,
}
}
}
pub struct PointEdgeCoordinates {
pub x_axis: EdgeCoordinate,
pub y_axis: EdgeCoordinate,
}
impl PointEdgeCoordinates {
#[inline]
pub fn new(min_t_x: f32x4, max_t_x: f32x4, min_t_y: f32x4, max_t_y: f32x4) -> Self {
Self {
x_axis: EdgeCoordinate {
min: min_t_x,
max: max_t_x,
},
y_axis: EdgeCoordinate {
min: min_t_y,
max: max_t_y,
},
}
}
#[inline]
pub fn clamp(&self, half_length: f32x4, neg_half_length: f32x4) -> EdgeCoordinate {
let min_t = neg_half_length.max(self.x_axis.min).max(self.y_axis.min);
let max_t = half_length.min(self.x_axis.max).min(self.y_axis.max);
EdgeCoordinate {
min: min_t,
max: max_t,
}
}
}
pub struct FaceVertices {
pub v00: Vec3x4,
pub v01: Vec3x4,
pub v11: Vec3x4,
pub v10: Vec3x4,
pub edge_dir_00_01: Vec3x4,
pub edge_dir_01_11: Vec3x4,
pub edge_dir_11_10: Vec3x4,
pub edge_dir_10_00: Vec3x4,
}
pub struct CuboidFaceInitConfig {
pub use_x_as_normal_tangent_x_y: [Axis; 2],
pub use_y_as_normal_tangent_x_y: [Axis; 2],
pub use_z_as_normal_tangent_x_y: [Axis; 2],
}
impl CuboidFaceInitConfig {
pub fn get_half_span_xyz(
&self,
cuboid_half_length: Vec3x4,
use_x_as_face_normal: bool32x4,
use_y_as_face_normal: bool32x4,
) -> Vec3x4 {
let use_x_half_spans = self.use_x_half_spans(cuboid_half_length);
let use_y_half_spans = self.use_y_half_spans(cuboid_half_length);
let use_z_half_spans = self.use_z_half_spans(cuboid_half_length);
let result =
Vec3x4::lane_select(use_x_as_face_normal, use_x_half_spans, use_z_half_spans);
Vec3x4::lane_select(use_y_as_face_normal, use_y_half_spans, result)
}
fn use_x_half_spans(&self, cuboid_half_length: Vec3x4) -> Vec3x4 {
let index_x: usize = self.use_x_as_normal_tangent_x_y[0] as usize;
let index_y: usize = self.use_x_as_normal_tangent_x_y[1] as usize;
let x = cuboid_half_length.index(index_x);
let y = cuboid_half_length.index(index_y);
let z = cuboid_half_length.index(0);
Vec3x4::new(*x, *y, *z)
}
fn use_y_half_spans(&self, cuboid_half_length: Vec3x4) -> Vec3x4 {
let index_x: usize = self.use_y_as_normal_tangent_x_y[0] as usize;
let index_y: usize = self.use_y_as_normal_tangent_x_y[1] as usize;
let x = cuboid_half_length.index(index_x);
let y = cuboid_half_length.index(index_y);
let z = cuboid_half_length.index(1);
Vec3x4::new(*x, *y, *z)
}
fn use_z_half_spans(&self, cuboid_half_length: Vec3x4) -> Vec3x4 {
let index_x: usize = self.use_z_as_normal_tangent_x_y[0] as usize;
let index_y: usize = self.use_z_as_normal_tangent_x_y[1] as usize;
let x = cuboid_half_length.index(index_x);
let y = cuboid_half_length.index(index_y);
let z = cuboid_half_length.index(2);
Vec3x4::new(*x, *y, *z)
}
pub fn get_x_y_tangent(
&self,
transform: &impl AxisInfo,
use_x_as_face_normal: bool32x4,
use_y_as_face_normal: bool32x4,
) -> [Vec3x4; 2] {
let use_x_tangent_xy = self.use_x_tangent_xy(transform);
let use_y_tangent_xy = self.use_y_tangent_xy(transform);
let use_z_tangent_xy = self.use_z_tangent_xy(transform);
let tangent_x = Vec3x4::lane_select(
use_x_as_face_normal,
use_x_tangent_xy[0],
Vec3x4::lane_select(
use_y_as_face_normal,
use_y_tangent_xy[0],
use_z_tangent_xy[0],
),
);
let tangent_y = Vec3x4::lane_select(
use_x_as_face_normal,
use_x_tangent_xy[1],
Vec3x4::lane_select(
use_y_as_face_normal,
use_y_tangent_xy[1],
use_z_tangent_xy[1],
),
);
[tangent_x, tangent_y]
}
fn use_x_tangent_xy(&self, transform: &impl AxisInfo) -> [Vec3x4; 2] {
let tangent_x = transform.axis(self.use_x_as_normal_tangent_x_y[0]);
let tangent_y = transform.axis(self.use_x_as_normal_tangent_x_y[1]);
[tangent_x, tangent_y]
}
fn use_y_tangent_xy(&self, transform: &impl AxisInfo) -> [Vec3x4; 2] {
let tangent_x = transform.axis(self.use_y_as_normal_tangent_x_y[0]);
let tangent_y = transform.axis(self.use_y_as_normal_tangent_x_y[1]);
[tangent_x, tangent_y]
}
fn use_z_tangent_xy(&self, transform: &impl AxisInfo) -> [Vec3x4; 2] {
let tangent_x = transform.axis(self.use_z_as_normal_tangent_x_y[0]);
let tangent_y = transform.axis(self.use_z_as_normal_tangent_x_y[1]);
[tangent_x, tangent_y]
}
}
impl Default for CuboidFaceInitConfig {
fn default() -> Self {
Self {
use_x_as_normal_tangent_x_y: [Axis::Z, Axis::Y],
use_y_as_normal_tangent_x_y: [Axis::X, Axis::Z],
use_z_as_normal_tangent_x_y: [Axis::Y, Axis::X],
}
}
}
}
pub(super) use structure::{
AxisDepthInfo, CuboidFaceInitConfig, EdgeClipContext, VertexClipContext,
};
trait Vec3x4ElementAccess {
fn x(&self) -> f32x4;
fn y(&self) -> f32x4;
fn z(&self) -> f32x4;
fn abs(&self) -> Self;
}
trait MultipliedByMatrixWide {
type ValueType;
fn multiplied_by(&self, other: &impl AxisInfo) -> Self::ValueType;
}
trait DotProductWide {
type ValueType;
fn dot(&self, other: &impl Vec3x4ElementAccess) -> Self::ValueType;
}
pub(super) fn calculate_separating_axis(
a: &CuboidWide,
b: &CuboidWide,
b_2_a_transform: &(impl TransformativeWide + AxisInfo),
) -> AxisDepthInfo {
let cross_edge_test = {
#[inline]
|i: Axis| {
edge_cross_shape_a_axis_test(
&a.half_length,
&b.half_length,
b_2_a_transform.axis(i),
b_2_a_transform,
)
}
};
let mut check_result: AxisDepthInfo = cross_edge_test(Axis::X);
check_result.update(&cross_edge_test(Axis::Y));
check_result.update(&cross_edge_test(Axis::Z));
ab_edge_test(
&a.half_length,
&b.half_length,
b_2_a_transform,
&mut check_result,
);
check_result
}
fn edge_cross_shape_a_axis_test(
half_length_a: &Vec3x4,
half_length_b: &Vec3x4,
edge_b: Vec3x4,
b_2_a_transform: &(impl TransformativeWide + AxisInfo),
) -> AxisDepthInfo {
let mut result = AxisDepthInfo::default();
let edge_b = Vec3x4WithLengthInfo::new(edge_b);
{
let length = (edge_b.y_square + edge_b.z_square).sqrtf();
result.normal = (edge_b.value.cross(Vec3x4::X) * length.recip()).as_unit_vec3x4_unchecked();
let normal_ignore_some = result.normal_by_ignore::<0>();
let half_length_a_dot_normal = normal_ignore_some.abs().dot(half_length_a);
let half_length_b_dot_normal = normal_ignore_some
.multiplied_by(b_2_a_transform)
.abs()
.dot(*half_length_b);
let depth = half_length_a_dot_normal + half_length_b_dot_normal
- normal_ignore_some.dot(&b_2_a_transform.offset()).absf();
result.depth = f32x4::MAX.select(f32x4::lt(length, f32x4::EPSILON), depth);
}
{
let mut result_now = AxisDepthInfo::default();
let length = (edge_b.x_square + edge_b.z_square).sqrtf();
result_now.normal =
(edge_b.value.cross(Vec3x4::Y) * length.recip()).as_unit_vec3x4_unchecked();
let normal_ignore_some = result_now.normal_by_ignore::<1>();
let half_length_a_dot_normal = normal_ignore_some.abs().dot(half_length_a);
let half_length_b_dot_normal = normal_ignore_some
.multiplied_by(b_2_a_transform)
.abs()
.dot(*half_length_b);
result_now.depth = half_length_a_dot_normal + half_length_b_dot_normal
- normal_ignore_some.dot(&b_2_a_transform.offset()).absf();
result_now.depth = f32x4::MAX.select(f32x4::lt(length, f32x4::EPSILON), result_now.depth);
result.update(&result_now);
}
{
let mut result_now = AxisDepthInfo::default();
let length = (edge_b.x_square + edge_b.y_square).sqrtf();
result_now.normal =
(edge_b.value.cross(Vec3x4::Z) * length.recip()).as_unit_vec3x4_unchecked();
let normal_ignore_some = result_now.normal_by_ignore::<2>();
let half_length_a_dot_normal = normal_ignore_some.abs().dot(half_length_a);
let half_length_b_dot_normal = normal_ignore_some
.multiplied_by(b_2_a_transform)
.abs()
.dot(*half_length_b);
result_now.depth = half_length_a_dot_normal + half_length_b_dot_normal
- normal_ignore_some.dot(&b_2_a_transform.offset()).absf();
result_now.depth = f32x4::MAX.select(f32x4::lt(length, f32x4::EPSILON), result_now.depth);
result.update(&result_now);
}
result
}
fn ab_edge_test(
half_length_a: &Vec3x4,
half_length_b: &Vec3x4,
b_2_a_transform: &(impl TransformativeWide + AxisInfo),
result: &mut AxisDepthInfo,
) {
let abs_rbx = b_2_a_transform.x_axis().abs();
let abs_rby = b_2_a_transform.y_axis().abs();
let abs_rbz = b_2_a_transform.z_axis().abs();
{
let normal_dot_axis_a = half_length_a.dot(Vec3x4::X);
let normal_dot_axis_b =
half_length_b.x * abs_rbx.x + half_length_b.y * abs_rby.x + half_length_b.z * abs_rbz.x;
let depth = normal_dot_axis_a + normal_dot_axis_b - b_2_a_transform.offset().x.absf();
let result_now = AxisDepthInfo::new(UnitVec3x4::X, depth);
result.update(&result_now);
}
{
let normal_dot_axis_a = half_length_a.dot(Vec3x4::Y);
let normal_dot_axis_b =
half_length_b.x * abs_rbx.y + half_length_b.y * abs_rby.y + half_length_b.z * abs_rbz.y;
let depth = normal_dot_axis_a + normal_dot_axis_b - b_2_a_transform.offset().y.absf();
let result_now = AxisDepthInfo::new(UnitVec3x4::Y, depth);
result.update(&result_now);
}
{
let normal_dot_axis_a = half_length_a.dot(Vec3x4::Z);
let normal_dot_axis_b =
half_length_b.x * abs_rbx.z + half_length_b.y * abs_rby.z + half_length_b.z * abs_rbz.z;
let depth = normal_dot_axis_a + normal_dot_axis_b - b_2_a_transform.offset().z.absf();
let result_now = AxisDepthInfo::new(UnitVec3x4::Z, depth);
result.update(&result_now);
}
let offset_b_in_local_space_b = b_2_a_transform
.orientation()
.inverse_mul_vec3(b_2_a_transform.offset());
{
let normal_dot_axis_b = half_length_b.x;
let normal_dot_axis_a =
half_length_a.x * abs_rbx.x + half_length_a.y * abs_rbx.y + half_length_a.z * abs_rbx.z;
let depth = normal_dot_axis_a + normal_dot_axis_b - offset_b_in_local_space_b.x.absf();
let result_now =
AxisDepthInfo::new(b_2_a_transform.x_axis().as_unit_vec3x4_unchecked(), depth);
result.update(&result_now);
}
{
let normal_dot_axis_b = half_length_b.y;
let normal_dot_axis_a =
half_length_a.x * abs_rby.x + half_length_a.y * abs_rby.y + half_length_a.z * abs_rby.z;
let depth = normal_dot_axis_a + normal_dot_axis_b - offset_b_in_local_space_b.y.absf();
let result_now =
AxisDepthInfo::new(b_2_a_transform.y_axis().as_unit_vec3x4_unchecked(), depth);
result.update(&result_now);
}
{
let normal_dot_axis_b = half_length_b.z;
let normal_dot_axis_a =
half_length_a.x * abs_rbz.x + half_length_a.y * abs_rbz.y + half_length_a.z * abs_rbz.z;
let depth = normal_dot_axis_a + normal_dot_axis_b - offset_b_in_local_space_b.z.absf();
let result_now =
AxisDepthInfo::new(b_2_a_transform.z_axis().as_unit_vec3x4_unchecked(), depth);
result.update(&result_now);
}
}
pub struct CuboidFace {
pub center: Vec3x4,
pub normal: Vec3x4,
pub tangent_x: Vec3x4,
pub tangent_y: Vec3x4,
pub half_span_x: f32x4,
pub half_span_y: f32x4,
pub half_span_z: f32x4,
pub use_x_as_face_normal: bool32x4,
pub use_y_as_face_normal: bool32x4,
pub is_neg_normal: bool32x4,
}
impl CuboidFace {
#[inline]
pub fn new(
manifold_normal: UnitVec3x4,
transform: &impl AxisInfo,
half_length: &Vec3x4,
should_flip: bool,
config: &CuboidFaceInitConfig,
) -> Self {
let abs_ax_dot = manifold_normal.dot(transform.x_axis()).absf();
let abs_ay_dot = manifold_normal.dot(transform.y_axis()).absf();
let abs_az_dot = manifold_normal.dot(transform.z_axis()).absf();
let max_a_dot = abs_ax_dot.max(abs_ay_dot.max(abs_az_dot));
let use_x_as_face_normal = max_a_dot.eq(abs_ax_dot);
let use_y_as_face_normal = max_a_dot.eq(abs_ay_dot) & !use_x_as_face_normal;
let mut normal = Vec3x4::lane_select(
use_x_as_face_normal,
transform.x_axis(),
Vec3x4::lane_select(use_y_as_face_normal, transform.y_axis(), transform.z_axis()),
);
let calibration_dot = normal.dot(manifold_normal);
let should_negate_normal = if should_flip {
calibration_dot.lt(f32x4::ZERO)
} else {
calibration_dot.gt(f32x4::ZERO)
};
normal = Vec3x4::lane_select(should_negate_normal, -normal, normal);
let [mut tangent_x, tangent_y] =
config.get_x_y_tangent(transform, use_x_as_face_normal, use_y_as_face_normal);
tangent_x = Vec3x4::lane_select(should_negate_normal, -tangent_x, tangent_x);
let [half_span_x, half_span_y, half_span_z] = config
.get_half_span_xyz(*half_length, use_x_as_face_normal, use_y_as_face_normal)
.to_array();
Self {
center: normal * half_span_z,
normal,
tangent_x,
tangent_y,
half_span_x,
half_span_y,
half_span_z,
use_x_as_face_normal,
use_y_as_face_normal,
is_neg_normal: should_negate_normal,
}
}
#[inline]
pub fn vertices(&self) -> FaceVertices {
let half_offset_x = self.tangent_x * self.half_span_x;
let half_offset_y = self.tangent_y * self.half_span_y;
let v00 = self.center - half_offset_x - half_offset_y;
let v01 = self.center + half_offset_x - half_offset_y;
let v10 = self.center - half_offset_x + half_offset_y;
let v11 = self.center + half_offset_x + half_offset_y;
FaceVertices {
v00,
v10,
v01,
v11,
edge_dir_00_01: self.tangent_x,
edge_dir_01_11: self.tangent_y,
edge_dir_11_10: -self.tangent_x,
edge_dir_10_00: -self.tangent_y,
}
}
#[inline]
pub fn half_vertices_of_edge_x(&self) -> [Vec3x4; 2] {
let half_offset_y = self.tangent_y * self.half_span_y;
let v1 = self.center + half_offset_y;
let v0 = self.center - half_offset_y;
[v0, v1]
}
#[inline]
pub fn half_vertices_of_edge_y(&self) -> [Vec3x4; 2] {
let half_offset_x = self.tangent_x * self.half_span_x;
let v0 = self.center - half_offset_x;
let v1 = self.center + half_offset_x;
[v0, v1]
}
#[inline]
pub fn max_half_span(&self) -> f32x4 {
self.half_span_x.max(self.half_span_y.max(self.half_span_z))
}
}
pub struct FacePairInfo<'a> {
pub a: &'a CuboidFace,
pub b: &'a CuboidFace,
pub contact_normal: &'a UnitVec3x4,
}
pub struct ReduceContext<'a> {
pub face_a_normal: &'a Vec3x4,
pub b_center_to_a_center: &'a Vec3x4,
pub face_b_tangent_x: &'a Vec3x4,
pub face_b_tangent_y: &'a Vec3x4,
}
pub(crate) struct AddVertexContext<'a> {
pub face_pair_info: &'a FacePairInfo<'a>,
pub candidate: &'a mut ManifoldCandidateWide,
pub candidates: &'a mut Candidates<8>,
}
pub(crate) struct ManifoldCandidatePair {
pub min: ManifoldCandidateWide,
pub max: ManifoldCandidateWide,
}
pub(super) fn edges_of_face_b_clip_face_a(
face_pair_info: &FacePairInfo,
vertices_of_a: &FaceVertices,
context: &EdgeClipContext,
candidates: &mut Candidates<8>,
) {
let [edge_x0_mid, edge_x1_mid] = face_pair_info.b.half_vertices_of_edge_x();
let [edge_y0_mid, edge_y1_mid] = face_pair_info.b.half_vertices_of_edge_y();
let edge_a_x_in_manifold_space = face_pair_info
.a
.tangent_y
.cross(face_pair_info.contact_normal);
let edge_a_y_in_manifold_space = face_pair_info
.a
.tangent_x
.cross(face_pair_info.contact_normal);
let velocity_a_x_b_x = edge_a_x_in_manifold_space.dot(face_pair_info.b.tangent_x);
let inverse_velocity_a_x_b_x = velocity_a_x_b_x.recip();
let velocity_a_x_b_y = edge_a_x_in_manifold_space.dot(face_pair_info.b.tangent_y);
let inverse_velocity_a_x_b_y = velocity_a_x_b_y.recip();
let velocity_a_y_b_x = edge_a_y_in_manifold_space.dot(face_pair_info.b.tangent_x);
let inverse_velocity_a_y_b_x = velocity_a_y_b_x.recip();
let velocity_a_y_b_y = edge_a_y_in_manifold_space.dot(face_pair_info.b.tangent_y);
let inverse_velocity_a_y_b_y = velocity_a_y_b_y.recip();
let invalid_epsilon = f32x4::splat(1e-5);
let velocity_a_x_b_x_abs = velocity_a_x_b_x.absf();
let velocity_a_y_b_x_abs = velocity_a_y_b_x.absf();
let edge_clip_face_context = EdgeClipFaceContext {
face_v00: vertices_of_a.v00,
face_v11: vertices_of_a.v11,
edge_a_x_in_manifold_space,
edge_a_y_in_manifold_space,
};
let x0_coordinates = edge_clip_face(
edge_x0_mid,
&edge_clip_face_context,
velocity_a_x_b_x_abs.gt(invalid_epsilon),
velocity_a_y_b_x_abs.gt(invalid_epsilon),
inverse_velocity_a_x_b_x,
inverse_velocity_a_y_b_x,
);
let neg_half_span_x = -face_pair_info.b.half_span_x;
let x0_edge_coordinate = x0_coordinates.clamp(face_pair_info.b.half_span_x, neg_half_span_x);
let x1_coordinates = edge_clip_face(
edge_x1_mid,
&edge_clip_face_context,
velocity_a_x_b_x_abs.gt(invalid_epsilon),
velocity_a_y_b_x_abs.gt(invalid_epsilon),
inverse_velocity_a_x_b_x,
inverse_velocity_a_y_b_x,
);
let x1_edge_coordinate = x1_coordinates.clamp(face_pair_info.b.half_span_x, neg_half_span_x);
let velocity_a_x_b_y_abs = velocity_a_x_b_y.absf();
let velocity_a_y_b_y_abs = velocity_a_y_b_y.absf();
let y0_coordinates = edge_clip_face(
edge_y0_mid,
&edge_clip_face_context,
velocity_a_x_b_y_abs.gt(invalid_epsilon),
velocity_a_y_b_y_abs.gt(invalid_epsilon),
inverse_velocity_a_x_b_y,
inverse_velocity_a_y_b_y,
);
let neg_half_span_y = -face_pair_info.b.half_span_y;
let y0_edge_coordinate = y0_coordinates.clamp(face_pair_info.b.half_span_y, neg_half_span_y);
let y1_coordinates = edge_clip_face(
edge_y1_mid,
&edge_clip_face_context,
velocity_a_x_b_y_abs.gt(invalid_epsilon),
velocity_a_y_b_y_abs.gt(invalid_epsilon),
inverse_velocity_a_x_b_y,
inverse_velocity_a_y_b_y,
);
let y1_edge_coordinate = y1_coordinates.clamp(face_pair_info.b.half_span_y, neg_half_span_y);
let x1_edge_coordinate_flipped = x1_edge_coordinate.flip();
let y0_edge_coordinate_flipped = y0_edge_coordinate.flip();
let epsilon = context.epsilon_scale * f32x4::const_splat(1e-5f32);
let mut candidate_pair = ManifoldCandidatePair {
min: ManifoldCandidateWide::default(),
max: ManifoldCandidateWide::default(),
};
candidate_pair.min.feature_id = context.feature_id_x0;
candidate_pair.min.x = x0_edge_coordinate.min;
candidate_pair.min.y = neg_half_span_y;
candidate_pair.max.feature_id = context.feature_id_x0 + EDGE_FEATURE_ID_OFFSET;
candidate_pair.max.x = x0_edge_coordinate.max;
candidate_pair.max.y = neg_half_span_y;
add_contacts_for_edge(
&x0_edge_coordinate,
&candidate_pair,
face_pair_info.b.half_span_x,
epsilon,
candidates,
context.allow_contacts,
context.pair_count,
);
candidate_pair.min.feature_id = context.feature_id_x1;
candidate_pair.min.x = x1_edge_coordinate.max;
candidate_pair.min.y = face_pair_info.b.half_span_y;
candidate_pair.max.feature_id = context.feature_id_x1 + EDGE_FEATURE_ID_OFFSET;
candidate_pair.max.x = x1_edge_coordinate.min;
candidate_pair.max.y = face_pair_info.b.half_span_y;
add_contacts_for_edge(
&x1_edge_coordinate_flipped,
&candidate_pair,
face_pair_info.b.half_span_x,
epsilon,
candidates,
context.allow_contacts,
context.pair_count,
);
candidate_pair.min.feature_id = context.feature_id_y0;
candidate_pair.min.x = neg_half_span_x;
candidate_pair.min.y = y0_edge_coordinate.max;
candidate_pair.max.feature_id = context.feature_id_y0 + EDGE_FEATURE_ID_OFFSET;
candidate_pair.max.x = candidate_pair.min.x;
candidate_pair.max.y = y0_edge_coordinate.min;
add_contacts_for_edge(
&y0_edge_coordinate_flipped,
&candidate_pair,
face_pair_info.b.half_span_y,
epsilon,
candidates,
context.allow_contacts,
context.pair_count,
);
candidate_pair.min.feature_id = context.feature_id_y1;
candidate_pair.min.x = face_pair_info.b.half_span_x;
candidate_pair.min.y = y1_edge_coordinate.min;
candidate_pair.max.feature_id = context.feature_id_y1 + EDGE_FEATURE_ID_OFFSET;
candidate_pair.max.x = face_pair_info.b.half_span_x;
candidate_pair.max.y = y1_edge_coordinate.max;
add_contacts_for_edge(
&y1_edge_coordinate,
&candidate_pair,
face_pair_info.b.half_span_y,
epsilon,
candidates,
context.allow_contacts,
context.pair_count,
);
}
#[inline]
pub(super) fn add_contacts_for_edge(
edge_coordinate: &EdgeCoordinate,
candidate_min_max: &ManifoldCandidatePair,
half_span_b: f32x4,
epsilon: f32x4,
candidates: &mut Candidates<8>,
allow_contacts: bool32x4,
pair_count: usize,
) {
let min_exists = allow_contacts
& (edge_coordinate.max - edge_coordinate.min).gt(epsilon)
& (edge_coordinate.min).absf().lt(half_span_b);
candidates.add(&candidate_min_max.min, min_exists, pair_count);
let max_exists_right =
(edge_coordinate.max).ge(edge_coordinate.min) & edge_coordinate.max.absf().le(half_span_b);
let max_exists = allow_contacts & max_exists_right;
candidates.add(&candidate_min_max.max, max_exists, pair_count);
}
struct EdgeClipFaceContext {
pub face_v00: Vec3x4,
pub face_v11: Vec3x4,
pub edge_a_x_in_manifold_space: Vec3x4,
pub edge_a_y_in_manifold_space: Vec3x4,
}
fn edge_clip_face(
mid_point_of_edge: Vec3x4,
context: &EdgeClipFaceContext,
velocity_valid_ax: bool32x4,
velocity_valid_ay: bool32x4,
inverse_velocity_ax: f32x4,
inverse_velocity_ay: f32x4,
) -> PointEdgeCoordinates {
let b = mid_point_of_edge;
let b_v00 = context.face_v00 - b;
let b_v11 = context.face_v11 - b;
let b_v00_dot_x = b_v00.dot(context.edge_a_x_in_manifold_space);
let b_v11_dot_x = b_v11.dot(context.edge_a_x_in_manifold_space);
let b_v00_dot_y = b_v00.dot(context.edge_a_y_in_manifold_space);
let b_v11_dot_y = b_v11.dot(context.edge_a_y_in_manifold_space);
let mid_point_is_inside_x = (b_v00_dot_x * b_v11_dot_x).le(f32x4::ZERO);
let mid_point_is_inside_y = (b_v00_dot_y * b_v11_dot_y).le(f32x4::ZERO);
let mut t0_x = b_v00_dot_x * inverse_velocity_ax;
let mut t1_x = b_v11_dot_x * inverse_velocity_ax;
let mut t0_y = b_v00_dot_y * inverse_velocity_ay;
let mut t1_y = b_v11_dot_y * inverse_velocity_ay;
t_value_check(
&mut t0_x,
&mut t1_x,
mid_point_is_inside_x,
velocity_valid_ax,
);
t_value_check(
&mut t0_y,
&mut t1_y,
mid_point_is_inside_y,
velocity_valid_ay,
);
PointEdgeCoordinates::new(t0_x, t1_x, t0_y, t1_y)
}
fn t_value_check(
t0: &mut f32x4,
t1: &mut f32x4,
is_inside: bool32x4,
dont_use_fall_back: bool32x4,
) {
let large_negative = -f32x4::MAX;
let large_positive = f32x4::MAX;
let min = t0.min(*t1).select(
dont_use_fall_back,
large_negative.select(is_inside, large_positive),
);
let max = t0.max(*t1).select(
dont_use_fall_back,
large_positive.select(is_inside, large_negative),
);
*t0 = min;
*t1 = max;
}
pub(super) fn vertices_of_face_a_clip_face_b(
vertices_of_a: &FaceVertices,
face_pair_info: &FacePairInfo,
context: &VertexClipContext,
candidates: &mut Candidates<8>,
) {
let normal_dot = (face_pair_info.b.normal).dot(face_pair_info.contact_normal);
let inverse_contact_normal_dot_face_normal_b = normal_dot
.recip()
.select(normal_dot.absf().gt(f32x4::splat(1e-10)), f32x4::MAX);
let mut candidate = ManifoldCandidateWide::default();
let mut add_vertex_context = AddVertexContext {
face_pair_info,
candidate: &mut candidate,
candidates,
};
add_vertex(
&vertices_of_a.v00,
&mut add_vertex_context,
inverse_contact_normal_dot_face_normal_b,
context.feature_id_v00,
context.pair_count,
context.allow_contacts,
);
add_vertex(
&vertices_of_a.v01,
&mut add_vertex_context,
inverse_contact_normal_dot_face_normal_b,
context.feature_id_v01,
context.pair_count,
context.allow_contacts,
);
add_vertex(
&vertices_of_a.v10,
&mut add_vertex_context,
inverse_contact_normal_dot_face_normal_b,
context.feature_id_v10,
context.pair_count,
context.allow_contacts,
);
add_vertex(
&vertices_of_a.v11,
&mut add_vertex_context,
inverse_contact_normal_dot_face_normal_b,
context.feature_id_v11,
context.pair_count,
context.allow_contacts,
);
}
#[inline]
pub(super) fn add_vertex(
vertex: &Vec3x4,
context: &mut AddVertexContext,
inverse_contact_normal_dot_face_normal_b: f32x4,
feature_id: u32x4,
pair_count: usize,
allow_contacts: bool32x4,
) {
let b_center_to_vertex = *vertex - context.face_pair_info.b.center;
let plane_distance = (context.face_pair_info.b.normal).dot(b_center_to_vertex);
let offset: Vec3x4 = (*context.face_pair_info.contact_normal)
* (plane_distance * inverse_contact_normal_dot_face_normal_b);
let vertex_on_b_face = *vertex - offset;
let vertex_offset_on_b_face = vertex_on_b_face - context.face_pair_info.b.center;
context.candidate.x = vertex_offset_on_b_face.dot(context.face_pair_info.b.tangent_x);
context.candidate.y = vertex_offset_on_b_face.dot(context.face_pair_info.b.tangent_y);
context.candidate.feature_id = feature_id;
let contained = context
.candidate
.x
.absf()
.le(context.face_pair_info.b.half_span_x)
& context
.candidate
.y
.absf()
.le(context.face_pair_info.b.half_span_y);
let below_buffer_capacity = context.candidates.count.lt(EIGHT_U32X4);
let contact_exists = allow_contacts & contained & below_buffer_capacity;
context
.candidates
.add(context.candidate, contact_exists, pair_count);
}
#[inline]
pub(super) fn transform_candidate_to_manifold(
row_contact: &ManifoldCandidateWide,
face_b: &CuboidFace,
manifold_offset_a: &mut Vec3x4,
manifold_depth: &mut f32x4,
manifold_feature_id: &mut u32x4,
) {
*manifold_offset_a = (face_b.tangent_x) * (row_contact.x);
let y = (face_b.tangent_y) * (row_contact.y);
*manifold_offset_a += y;
*manifold_offset_a += face_b.center;
*manifold_depth = row_contact.depth;
*manifold_feature_id = row_contact.feature_id;
}