rapier
rapier copied to clipboard
Odd cylinder collisions
Putting 4 cylinders into a compound collider has some very odd collision behavior:
https://user-images.githubusercontent.com/2748981/160272665-44df6ac1-6935-4393-b9c2-6520e2230d87.mp4
It also happens in other orientations:
https://user-images.githubusercontent.com/2748981/160272672-b79e51eb-29b1-4690-832d-af3222e2112d.mp4
But not for other shapes:
https://user-images.githubusercontent.com/2748981/160272675-494237e3-81dd-472b-93cb-6d51ae7cbf1b.mp4
I've made an easily reproducible example:
use rapier3d::prelude::*;
use rapier_testbed3d::Testbed;
// This shows a bug when a cylinder is in contact with a very large
// but very thin cuboid. In this case the EPA returns an incorrect
// contact normal, resulting in the cylinder falling through the floor.
pub fn init_world(testbed: &mut Testbed) {
/*
* World
*/
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let impulse_joints = ImpulseJointSet::new();
let multibody_joints = MultibodyJointSet::new();
/*
* Ground
*/
let ground_size = 100.1;
let ground_height = 0.1;
let collider = ColliderBuilder::cuboid(ground_size, ground_height, ground_size).translation(vector![0.0, -ground_height, 0.0]).rotation(vector![0.2, 0.0, 0.0]).friction(0.0);
colliders.insert(collider);
// Build the rigid body.
let rigid_body = RigidBodyBuilder::dynamic().translation(vector![0.0, 3.0, 0.0]);
let handle = bodies.insert(rigid_body);
// Case 1: Bad collisions with cylinders in this orientation
let collider1 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![-1.5, 0.0, 2.0]);
let collider2 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![1.5, 0.0, 2.0]);
let collider3 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![-1.5, 0.0, -2.0]);
let collider4 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![1.5, 0.0, -2.0]);
// Case 2: Also with this orientation
// let collider1 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![-1.5, 0.0, 2.0]).rotation(vector![0.0, 0.0, 90.0_f32.to_radians()]);
// let collider2 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![1.5, 0.0, 2.0]).rotation(vector![0.0, 0.0, 90.0_f32.to_radians()]);
// let collider3 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![-1.5, 0.0, -2.0]).rotation(vector![0.0, 0.0, 90.0_f32.to_radians()]);
// let collider4 = ColliderBuilder::cylinder(0.19, 0.45).translation(vector![1.5, 0.0, -2.0]).rotation(vector![0.0, 0.0, 90.0_f32.to_radians()]);
// Case 3: Same thing doesn't happen with cuboids
// let collider1 = ColliderBuilder::cuboid(0.38, 0.9, 0.9).translation(vector![-1.5, 0.0, 2.0]);
// let collider2 = ColliderBuilder::cuboid(0.38, 0.9, 0.9).translation(vector![1.5, 0.0, 2.0]);
// let collider3 = ColliderBuilder::cuboid(0.38, 0.9, 0.9).translation(vector![-1.5, 0.0, -2.0]);
// let collider4 = ColliderBuilder::cuboid(0.38, 0.9, 0.9).translation(vector![1.5, 0.0, -2.0]);
// Case 4: Or with capsules
// let collider1 = ColliderBuilder::capsule_y(0.19, 0.45).translation(vector![-1.5, 0.0, 2.0]);
// let collider2 = ColliderBuilder::capsule_y(0.19, 0.45).translation(vector![1.5, 0.0, 2.0]);
// let collider3 = ColliderBuilder::capsule_y(0.19, 0.45).translation(vector![-1.5, 0.0, -2.0]);
// let collider4 = ColliderBuilder::capsule_y(0.19, 0.45).translation(vector![1.5, 0.0, -2.0]);
// Case 5: In either orientation
// let collider1 = ColliderBuilder::capsule_x(0.19, 0.45).translation(vector![-1.5, 0.0, 2.0]);
// let collider2 = ColliderBuilder::capsule_x(0.19, 0.45).translation(vector![1.5, 0.0, 2.0]);
// let collider3 = ColliderBuilder::capsule_x(0.19, 0.45).translation(vector![-1.5, 0.0, -2.0]);
// let collider4 = ColliderBuilder::capsule_x(0.19, 0.45).translation(vector![1.5, 0.0, -2.0]);
let shapes = vec![
(collider1.position, collider1.shape),
(collider2.position, collider2.shape),
(collider3.position, collider3.shape),
(collider4.position, collider4.shape),
];
let collider = ColliderBuilder::compound(shapes).restitution(0.2).friction(0.0).build();
colliders.insert_with_parent(collider, handle, &mut bodies);
/*
* Set up the testbed.
*/
testbed.set_world(bodies, colliders, impulse_joints, multibody_joints);
testbed.look_at(point![100.0, 100.0, 100.0], Point::origin());
}
Possible related issue: https://github.com/dimforge/parry/issues/70
I've narrowed this down to the GJK implementation returning the wrong normal here: https://github.com/dimforge/parry/blob/c3f28389e762cf93d8a1a62337fb806450852017/src/query/gjk/gjk.rs#L131. From the examples most collisions don't hit this code. Passing in exact_dir=false
from https://github.com/dimforge/parry/blob/c3f28389e762cf93d8a1a62337fb806450852017/src/query/contact/contact_support_map_support_map.rs#L64 resolves the issue, but I'm not familiar enough with GJK to know what the correct fix is. Maybe it's obvious to @sebcrozet ?