use rapier3d::prelude::*;
struct World {
bodies: RigidBodySet,
colliders: ColliderSet,
impulse_joints: ImpulseJointSet,
multibody_joints: MultibodyJointSet,
link1: RigidBodyHandle,
link2: RigidBodyHandle,
}
fn build(use_multibody: bool, closure_anchor1: Vector) -> World {
let mut bodies = RigidBodySet::new();
let colliders = ColliderSet::new();
let mut impulse_joints = ImpulseJointSet::new();
let mut multibody_joints = MultibodyJointSet::new();
let com1 = Vector::new(0.0, 0.5, 0.0);
let com2 = Vector::new(0.0, -0.3, 0.0);
let inertia = Vector::new(0.1, 0.1, 0.1);
let base = bodies.insert(RigidBodyBuilder::fixed());
let link1 = bodies.insert(
RigidBodyBuilder::dynamic()
.translation(Vector::new(1.0, 0.0, 0.0))
.additional_mass_properties(MassProperties::new(com1, 1.0, inertia)),
);
let link2 = bodies.insert(
RigidBodyBuilder::dynamic()
.translation(Vector::new(2.0, 0.0, 0.0))
.additional_mass_properties(MassProperties::new(com2, 1.0, inertia)),
);
let rev1 = RevoluteJointBuilder::new(Vector::Z)
.local_anchor1(Vector::new(0.0, 0.0, 0.0))
.local_anchor2(Vector::new(-1.0, 0.0, 0.0));
let rev2 = RevoluteJointBuilder::new(Vector::Z)
.local_anchor1(Vector::new(1.0, 0.0, 0.0))
.local_anchor2(Vector::new(0.0, 0.0, 0.0));
if use_multibody {
multibody_joints.insert(base, link1, rev1, true).unwrap();
multibody_joints.insert(link1, link2, rev2, true).unwrap();
} else {
impulse_joints.insert(base, link1, rev1, true);
impulse_joints.insert(link1, link2, rev2, true);
}
let anchor2 = Vector::new(1.0, 0.0, 0.0) + closure_anchor1 - Vector::new(2.0, 0.0, 0.0);
let closure = GenericJointBuilder::new(JointAxesMask::LIN_AXES)
.local_frame1(Pose::from_translation(closure_anchor1))
.local_frame2(Pose::from_translation(anchor2))
.build();
impulse_joints.insert(link1, link2, closure, true);
World {
bodies,
colliders,
impulse_joints,
multibody_joints,
link1,
link2,
}
}
fn simulate(w: &mut World, closure_anchor1: Vector) -> (Real, Real) {
let anchor2 = Vector::new(1.0, 0.0, 0.0) + closure_anchor1 - Vector::new(2.0, 0.0, 0.0);
let gravity = Vector::new(0.0, -9.81, 0.0);
let integration_parameters = IntegrationParameters::default();
let mut pipeline = PhysicsPipeline::new();
let mut islands = IslandManager::new();
let mut broad_phase = DefaultBroadPhase::new();
let mut narrow_phase = NarrowPhase::new();
let mut ccd = CCDSolver::new();
let mut max_vel: Real = 0.0;
let mut max_anchor_err: Real = 0.0;
for _ in 0..100 {
pipeline.step(
gravity,
&integration_parameters,
&mut islands,
&mut broad_phase,
&mut narrow_phase,
&mut w.bodies,
&mut w.colliders,
&mut w.impulse_joints,
&mut w.multibody_joints,
&mut ccd,
&(),
&(),
);
for h in [w.link1, w.link2] {
max_vel = max_vel.max(w.bodies[h].linvel().length());
}
let p1 = w.bodies[w.link1].position() * closure_anchor1;
let p2 = w.bodies[w.link2].position() * anchor2;
max_anchor_err = max_anchor_err.max((p2 - p1).length());
}
(max_vel, max_anchor_err)
}
#[test]
fn redundant_loop_closure_on_multibody_links() {
let anchor = Vector::new(1.0, 0.0, 0.0); let mut w = build(true, anchor);
let (max_vel, _) = simulate(&mut w, anchor);
println!("multibody, redundant closure: max |linvel| = {max_vel}");
assert!(max_vel < 50.0, "system exploded: {max_vel}");
assert!(max_vel > 1.0, "system is frozen: {max_vel}");
}
#[test]
fn welding_loop_closure_on_multibody_links() {
let anchor = Vector::new(2.0, 0.0, 0.0); let mut w = build(true, anchor);
let (max_vel, max_err) = simulate(&mut w, anchor);
println!("multibody, welding closure: max |linvel| = {max_vel}, max anchor err = {max_err}");
assert!(max_vel < 50.0, "system exploded: {max_vel}");
assert!(max_vel > 1.0, "system is frozen: {max_vel}");
assert!(max_err < 1.0e-2, "loop closure not enforced: {max_err}");
}
#[test]
fn welding_loop_closure_on_rigid_bodies() {
let anchor = Vector::new(2.0, 0.0, 0.0);
let mut w = build(false, anchor);
let (max_vel, max_err) = simulate(&mut w, anchor);
println!("rigid bodies, welding closure: max |linvel| = {max_vel}, max anchor err = {max_err}");
assert!(max_vel < 50.0, "system exploded: {max_vel}");
assert!(max_vel > 1.0, "system is frozen: {max_vel}");
assert!(max_err < 1.0e-2, "loop closure not enforced: {max_err}");
}