use crate::collision::Manifold;
use crate::core::NULL_INDEX;
use crate::distance::SimplexCache;
use crate::math_functions::{Rot, Transform, ROT_IDENTITY, TRANSFORM_IDENTITY};
mod api;
mod lifecycle;
mod update;
pub use api::*;
pub use lifecycle::*;
pub use update::*;
pub mod contact_flags {
pub const TOUCHING: u32 = 0x00000001;
pub const HIT_EVENT: u32 = 0x00000002;
pub const ENABLE_CONTACT_EVENTS: u32 = 0x00000004;
pub const RECYCLE: u32 = 0x00000008;
pub const SIM_TOUCHING: u32 = 0x00010000;
pub const SIM_DISJOINT: u32 = 0x00020000;
pub const SIM_STARTED_TOUCHING: u32 = 0x00040000;
pub const SIM_STOPPED_TOUCHING: u32 = 0x00080000;
pub const SIM_ENABLE_HIT_EVENT: u32 = 0x00100000;
pub const SIM_ENABLE_PRE_SOLVE_EVENTS: u32 = 0x00200000;
pub const SIM_RELATIVE_TRANSFORM_VALID: u32 = 0x00400000;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ContactEdge {
pub body_id: i32,
pub prev_key: i32,
pub next_key: i32,
}
impl Default for ContactEdge {
fn default() -> Self {
ContactEdge {
body_id: NULL_INDEX,
prev_key: NULL_INDEX,
next_key: NULL_INDEX,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Contact {
pub edges: [ContactEdge; 2],
pub island_id: i32,
pub island_index: i32,
pub set_index: i32,
pub color_index: i32,
pub local_index: i32,
pub shape_id_a: i32,
pub shape_id_b: i32,
pub contact_id: i32,
pub flags: u32,
pub generation: u32,
}
impl Default for Contact {
fn default() -> Self {
Contact {
edges: [ContactEdge::default(); 2],
island_id: NULL_INDEX,
island_index: NULL_INDEX,
set_index: NULL_INDEX,
color_index: NULL_INDEX,
local_index: NULL_INDEX,
shape_id_a: NULL_INDEX,
shape_id_b: NULL_INDEX,
contact_id: NULL_INDEX,
flags: 0,
generation: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ContactSim {
pub contact_id: i32,
pub cached_rotation_a: Rot,
pub cached_rotation_b: Rot,
pub cached_relative_pose: Transform,
pub body_id_a: i32,
pub body_id_b: i32,
pub body_sim_index_a: i32,
pub body_sim_index_b: i32,
pub shape_id_a: i32,
pub shape_id_b: i32,
pub inv_mass_a: f32,
pub inv_i_a: f32,
pub inv_mass_b: f32,
pub inv_i_b: f32,
pub manifold: Manifold,
pub friction: f32,
pub restitution: f32,
pub rolling_resistance: f32,
pub tangent_speed: f32,
pub sim_flags: u32,
pub cache: SimplexCache,
}
impl Default for ContactSim {
fn default() -> Self {
ContactSim {
contact_id: NULL_INDEX,
cached_rotation_a: ROT_IDENTITY,
cached_rotation_b: ROT_IDENTITY,
cached_relative_pose: TRANSFORM_IDENTITY,
body_id_a: NULL_INDEX,
body_id_b: NULL_INDEX,
body_sim_index_a: NULL_INDEX,
body_sim_index_b: NULL_INDEX,
shape_id_a: NULL_INDEX,
shape_id_b: NULL_INDEX,
inv_mass_a: 0.0,
inv_i_a: 0.0,
inv_mass_b: 0.0,
inv_i_b: 0.0,
manifold: Manifold::default(),
friction: 0.0,
restitution: 0.0,
rolling_resistance: 0.0,
tangent_speed: 0.0,
sim_flags: 0,
cache: SimplexCache::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::body::{create_body, get_body_full_id, get_body_transform};
use crate::broad_phase::update_broad_phase_pairs;
use crate::collision::ShapeType;
use crate::geometry::make_box;
use crate::math_functions::VEC2_ZERO;
use crate::shape::create_polygon_shape;
use crate::solver_set::AWAKE_SET;
use crate::table::shape_pair_key;
use crate::types::{default_body_def, default_shape_def, default_world_def, BodyType};
use crate::world::World;
#[test]
fn contact_register_pairs() {
use ShapeType::*;
for (a, b) in [
(Circle, Circle),
(Capsule, Circle),
(Capsule, Capsule),
(Polygon, Circle),
(Polygon, Capsule),
(Polygon, Polygon),
(Segment, Circle),
(Segment, Capsule),
(Segment, Polygon),
(ChainSegment, Circle),
(ChainSegment, Capsule),
(ChainSegment, Polygon),
] {
assert!(can_collide(a, b), "{a:?} vs {b:?}");
assert!(can_collide(b, a), "{b:?} vs {a:?}");
}
assert!(!can_collide(Segment, Segment));
assert!(!can_collide(Segment, ChainSegment));
assert!(!can_collide(ChainSegment, ChainSegment));
}
#[test]
fn overlapping_boxes_create_touching_contact() {
let mut world = World::new(&default_world_def());
let mut body_def = default_body_def();
body_def.type_ = BodyType::Dynamic;
let body_a = create_body(&mut world, &body_def);
let body_b = create_body(&mut world, &body_def);
let body_index_a = get_body_full_id(&world, body_a);
let body_index_b = get_body_full_id(&world, body_b);
let box_poly = make_box(0.5, 0.5);
let shape_def = default_shape_def();
let sa = create_polygon_shape(&mut world, body_a, &shape_def, &box_poly);
let sb = create_polygon_shape(&mut world, body_b, &shape_def, &box_poly);
let shape_index_a = sa.index1 - 1;
let shape_index_b = sb.index1 - 1;
assert_eq!(world.broad_phase.move_array.len(), 2);
update_broad_phase_pairs(&mut world);
assert_eq!(world.contact_id_pool.id_count(), 1);
assert_eq!(world.solver_sets[AWAKE_SET as usize].contact_sims.len(), 1);
assert!(world.broad_phase.move_array.is_empty());
assert!(world
.broad_phase
.pair_set
.contains_key(shape_pair_key(shape_index_a, shape_index_b)));
let contact_id = world.solver_sets[AWAKE_SET as usize].contact_sims[0].contact_id;
{
let contact = &world.contacts[contact_id as usize];
assert_eq!(contact.set_index, AWAKE_SET);
assert_eq!(contact.color_index, NULL_INDEX);
assert_eq!(contact.flags & contact_flags::TOUCHING, 0);
assert_eq!(contact.edges[0].body_id, body_index_a);
assert_eq!(contact.edges[1].body_id, body_index_b);
}
assert_eq!(world.bodies[body_index_a as usize].contact_count, 1);
assert_eq!(world.bodies[body_index_b as usize].contact_count, 1);
assert_eq!(
world.bodies[body_index_a as usize].head_contact_key,
contact_id << 1
);
assert_eq!(
world.bodies[body_index_b as usize].head_contact_key,
(contact_id << 1) | 1
);
update_broad_phase_pairs(&mut world);
assert_eq!(world.contact_id_pool.id_count(), 1);
let (shape_id_a, shape_id_b) = {
let contact = &world.contacts[contact_id as usize];
(contact.shape_id_a, contact.shape_id_b)
};
let ctx = ContactUpdateContext::new(&world);
let transform_a = get_body_transform(&world, body_index_a);
let transform_b = get_body_transform(&world, body_index_b);
let shape_a = world.shapes[shape_id_a as usize].clone();
let shape_b = world.shapes[shape_id_b as usize].clone();
let mut contact_sim = world.solver_sets[AWAKE_SET as usize].contact_sims[0];
let touching = update_contact(
&ctx,
&mut contact_sim,
&shape_a,
transform_a,
VEC2_ZERO,
&shape_b,
transform_b,
VEC2_ZERO,
);
assert!(touching);
assert_eq!(contact_sim.manifold.point_count, 2);
assert!(contact_sim.sim_flags & contact_flags::SIM_TOUCHING != 0);
world.solver_sets[AWAKE_SET as usize].contact_sims[0] = contact_sim;
destroy_contact(&mut world, contact_id, false);
assert_eq!(world.contact_id_pool.id_count(), 0);
assert!(world.solver_sets[AWAKE_SET as usize]
.contact_sims
.is_empty());
assert!(!world
.broad_phase
.pair_set
.contains_key(shape_pair_key(shape_index_a, shape_index_b)));
assert_eq!(world.bodies[body_index_a as usize].contact_count, 0);
assert_eq!(world.bodies[body_index_b as usize].contact_count, 0);
assert_eq!(
world.bodies[body_index_a as usize].head_contact_key,
NULL_INDEX
);
world.validate_solver_sets();
}
}