bevy_xpbd
bevy_xpbd copied to clipboard
Unstable collision with cubes joined by fixed joints
I ran into this while experimenting with joints:
- When two cube colliders
- joined with fixed joints
- are hitting ground collider
- face down
- at the same time, the solver goes into an unstable state.
This is an edge case, and easy to work around it, but I wanted to open an issue in case it's useful. I'll try to look into this over the weekend.
use bevy::prelude::*;
use bevy_xpbd_3d::{math::*, prelude::*};
use examples_common_3d::XpbdExamplePlugin;
fn main() {
App::new()
.add_plugins((DefaultPlugins, XpbdExamplePlugin))
.insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1)))
.insert_resource(Msaa::Sample4)
.insert_resource(SubstepCount(50))
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
) {
let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 }));
let cube_collider = Collider::cuboid(1.0, 1.0, 1.0);
let cube_material_1 = materials.add(Color::BEIGE.into());
let cube_material_2 = materials.add(Color::LIME_GREEN.into());
// Kinematic rotating "anchor" object
let anchor = commands
.spawn((
PbrBundle {
mesh: cube_mesh.clone(),
material: cube_material_1,
transform: Transform::from_xyz(0.0, 3.5, 0.0),
..default()
},
MassPropertiesBundle::new_computed(&cube_collider, 1.0),
cube_collider.clone(),
// Collider::ball(0.5), // Using ball colliders fixes the issue
RigidBody::Dynamic,
))
.id();
// Dynamic object rotating around anchor
let object = commands
.spawn((
PbrBundle {
mesh: cube_mesh,
material: cube_material_2,
transform: Transform::from_xyz(4.0, 3.5, 0.0)
// .with_rotation(Quat::from_rotation_x(0.01)) // Uncomment to fix the unstable collision issue
,
..default()
},
RigidBody::Dynamic,
MassPropertiesBundle::new_computed(&cube_collider, 1.0),
cube_collider,
// Collider::ball(0.5)
))
.id();
// Connect anchor and dynamic object
commands.spawn(
// RevoluteJoint::new(anchor, object) // Using revolute joints fixes the issue
FixedJoint::new(anchor, object)
.with_local_anchor_1(Vector::X * 2.0)
.with_local_anchor_2(Vector::X * -2.0),
);
// Ground plane
commands.spawn((
RigidBody::Static,
Collider::cuboid(50.0, 0.1, 50.0),
PbrBundle {
mesh: meshes.add(shape::Plane::from_size(50.0).into()),
material: materials.add(Color::SILVER.into()),
transform: Transform::from_xyz(0.0, -1.0, 0.0),
..default()
},
));
// Directional light
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: 20_000.0,
shadows_enabled: true,
..default()
},
transform: Transform::default().looking_at(Vec3::new(-1.0, -2.5, -1.5), Vec3::Y),
..default()
});
// Camera
commands.spawn(Camera3dBundle {
transform: Transform::from_translation(Vec3::Z * 10.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
https://github.com/Jondolf/bevy_xpbd/assets/2298371/a43cf99c-e53d-4e78-a2a3-a6154cd29c4b
Adding a minimal rotation fixes the issue
https://github.com/Jondolf/bevy_xpbd/assets/2298371/e3411d54-21cb-4c7f-a26f-93e962972714
I'm still trying to understand a lot of things about XPBD, but i'm not sure that this is a bug. I don't think the issue is removable without making some joint specific workarounds in the solver. I didn't find any XPBD implementation with fixed joint constrains to compare the behaviours.
The above scenario can be stabilized by adding a non-zero compilance to the joint:
Compilance=0.001 Substeps=50
compilance_0_001_substepcount_50.webm
Compilance=0.01 Substeps=50
compilance_0_01_substepcount_50.webm
Compilance=0.001 Substeps=1 compilance_0_001_substepcount_1.webm
Compilance=0.01 Substeps=1 compilance_0_01_substepcount_1.webm