use oxijolt::*;
use super::{cube_shape, step};
pub fn add_locked_cube(world: &mut PhysicsWorld, position: RVec3, dofs: AllowedDofs) -> BodyId {
world
.create_body(
&cube_shape(),
&BodySettings::new_dynamic()
.position(position)
.allowed_dofs(dofs),
)
.unwrap()
}
pub fn motion(world: &PhysicsWorld, id: BodyId) -> (RVec3, Quat, Vec3, Vec3) {
let body = world.body(id).unwrap();
(
body.position(),
body.rotation(),
body.linear_velocity(),
body.angular_velocity(),
)
}
pub fn add_static_sensor(world: &mut PhysicsWorld, half: f32, position: RVec3) -> BodyId {
world
.create_body(
&Shape::new_box(Vec3::new(half, half, half)).unwrap(),
&BodySettings::new_static().position(position).sensor(true),
)
.unwrap()
}
pub fn is_between(event: &ContactEvent, a: BodyId, b: BodyId) -> bool {
let pair = event.pair();
[pair.body1, pair.body2] == [a, b] || [pair.body1, pair.body2] == [b, a]
}
pub fn sensor_contacts(
events: &[ContactEvent],
a: BodyId,
b: BodyId,
) -> Vec<(char, Option<ContactSettings>)> {
events
.iter()
.filter(|event| is_between(event, a, b))
.map(|event| match event {
ContactEvent::Added { settings, .. } => ('a', Some(*settings)),
ContactEvent::Persisted { settings, .. } => ('p', Some(*settings)),
ContactEvent::Removed(_) => ('r', None),
})
.collect()
}
pub fn contact_kinds(events: &[ContactEvent], a: BodyId, b: BodyId) -> String {
sensor_contacts(events, a, b)
.iter()
.map(|(kind, _)| kind)
.collect()
}
pub fn contact_events(world: &mut PhysicsWorld, ticks: usize) -> Vec<ContactEvent> {
let mut events = Vec::new();
for _ in 0..ticks {
step(world, 1);
events.extend(world.take_events().contacts);
}
events
}
pub fn fall_asleep(world: &mut PhysicsWorld, body: BodyId) {
for _ in 0..600 {
if world.body(body).unwrap().is_sleeping() {
return;
}
step(world, 1);
}
panic!("the body never fell asleep");
}
pub fn invalid<T: std::fmt::Debug>(result: Result<T, BodyError>) -> bool {
matches!(result, Err(BodyError::InvalidValue(_)))
}
pub fn largest_accepted(high: f32, mut accepts: impl FnMut(f32) -> bool) -> f32 {
let (mut low, mut high) = (0_u32, high.to_bits());
assert!(accepts(0.0));
while low < high {
let middle = low + (high - low).div_ceil(2);
if accepts(f32::from_bits(middle)) {
low = middle;
} else {
high = middle - 1;
}
}
f32::from_bits(low)
}
pub fn assert_finite(world: &PhysicsWorld, id: BodyId) {
let (p, q, v, w) = motion(world, id);
let p: [Real; 3] = p.into();
let values = [q.x, q.y, q.z, q.w, v.x, v.y, v.z, w.x, w.y, w.z];
assert!(p.iter().all(|c| c.is_finite()) && values.iter().all(|c| c.is_finite()));
}
pub fn add_sleeping(world: &mut PhysicsWorld, shape: &Shape, mass: f32, position: RVec3) -> BodyId {
world
.create_body(
shape,
&BodySettings::new_dynamic()
.position(position)
.mass(mass)
.activation(Activation::DontActivate),
)
.unwrap()
}
pub fn add_kinematic(
world: &mut PhysicsWorld,
shape: &Shape,
position: RVec3,
activation: Activation,
) -> BodyId {
world
.create_body(
shape,
&BodySettings::new_kinematic()
.position(position)
.activation(activation),
)
.unwrap()
}
pub fn angle_between(a: Quat, b: Quat) -> f32 {
let dot = (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w)
.abs()
.min(1.0);
2.0 * dot.acos()
}
pub fn distance(a: RVec3, b: RVec3) -> f64 {
let d = [a.x - b.x, a.y - b.y, a.z - b.z].map(f64::from);
(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt()
}