use rapier3d::pipeline::PhysicsWorld;
use rapier3d::prelude::*;
const GRAVITY: Real = 9.81;
const MASS: Real = 1.0;
fn resting_impulse(warmstart_coefficient: Real, multibody: bool, cuboid: bool) -> Real {
let mut world = PhysicsWorld::new();
world.integration_parameters.warmstart_coefficient = warmstart_coefficient;
world.insert_collider(
ColliderBuilder::cuboid(10.0, 0.5, 10.0).translation(Vector::new(0.0, -0.5, 0.0)),
None,
);
let shape = if cuboid {
ColliderBuilder::cuboid(0.5, 0.5, 0.5)
} else {
ColliderBuilder::ball(0.5)
};
let (body, _) = world.insert(
RigidBodyBuilder::dynamic()
.translation(Vector::new(0.0, 0.5, 0.0))
.additional_mass(MASS)
.can_sleep(false),
shape.density(0.0),
);
if multibody {
let anchor =
world.insert_body(RigidBodyBuilder::fixed().translation(Vector::new(0.0, 10.0, 0.0)));
let joint = PrismaticJointBuilder::new(Vector::Y)
.local_anchor1(Vector::ZERO)
.local_anchor2(Vector::ZERO);
world
.multibody_joints
.insert(anchor, body, joint, true)
.unwrap();
}
for _ in 0..300 {
world.step();
}
world
.narrow_phase
.contact_pairs()
.map(|p| p.total_impulse_magnitude())
.sum()
}
#[test]
fn resting_impulse_matches_gravity_for_any_warmstart_coefficient() {
let expected = MASS * GRAVITY / 60.0;
for &multibody in &[false, true] {
for &cuboid in &[false, true] {
for &coeff in &[1.0, 0.5, 0.0] {
let impulse = resting_impulse(coeff, multibody, cuboid);
assert!(
(impulse - expected).abs() <= expected * 1.0e-2,
"warmstart_coefficient = {coeff} (multibody: {multibody}, cuboid: {cuboid}): \
reported {impulse}, expected {expected}"
);
}
}
}
}