rapier
rapier copied to clipboard
Crash when combining a MultibodyJoint, a fixed parent object, and a dynamic body colliding with the parent object
Hello,
so, I'm trying to make a pinball game. I have the game board/table as a large, fixed rigid body. The flippers are dynamic rigid bodies, attached through a MultibodyJoint
to the game board/table.
The microsecond the ball (a dynamic rigid body) touches the table, I get a crash.
Here's a minimal example that reproduced the issue on my machine:
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use bevy_asset_loader::prelude::*;
use bevy_prototype_debug_lines::DebugLinesPlugin;
use bevy_rapier3d::prelude::ComputedColliderShape::{ConvexDecomposition, TriMesh};
use bevy_rapier3d::rapier::dynamics::{JointAxis, RigidBodyType};
fn main() {
let rapier_configuration = RapierConfiguration {
gravity: Vec3::new(0.0, -0.1, 0.0),
..Default::default()
};
App::new()
.insert_resource(Msaa::default())
.insert_resource(rapier_configuration)
.add_plugins(DefaultPlugins)
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
.add_plugin(RapierDebugRenderPlugin::default())
.add_startup_system(setup_scene)
.add_startup_system(setup_camera)
.run();
}
fn setup_camera(mut commands: Commands) {
commands.spawn_bundle(Camera3dBundle {
transform: Transform::from_xyz(0.0, 1.0, 0.5).looking_at(Vec3::new(0.0, 0.0, -0.5), Vec3::Y),
..Default::default()
});
}
fn setup_scene(mut commands: Commands) {
let main_body = {
commands
.spawn()
.insert_bundle(TransformBundle {
local: Transform::from_xyz(0.0, 0.0, 0.0),
..Default::default()
})
.insert(Collider::cuboid(1.0, 0.1, 1.0))
.insert(RigidBody::Fixed).id()
};
{
let collider = Collider::cuboid(0.1, 0.1, 0.1);
let revj = RevoluteJointBuilder::new(Vec3::Y)
.local_anchor1(Vec3::new(-0.5, 0.3, -0.5))
.motor_velocity(-1.0, 1.0)
.build();
let mut joint = MultibodyJoint::new(main_body, revj);
commands
.spawn()
.insert_bundle(TransformBundle {
local: Transform::from_xyz(-0.5, 0.3, -0.5),
..Default::default()
})
.insert(collider)
.insert(RigidBody::Dynamic)
.insert(joint)
.insert(Sleeping::disabled());
}
{
commands
.spawn()
.insert_bundle(TransformBundle {
local: Transform::from_xyz(0.0, 1.0, 0.0),
..Default::default()
})
.insert(Sleeping::disabled())
.insert(Collider::ball(0.01))
.insert(Friction {
coefficient: 0.0,
combine_rule: CoefficientCombineRule::Multiply,
})
.insert(RigidBody::Dynamic);
}
}
These are my package versions:
[dependencies]
bevy = { version = "0.8.1" }
bevy_rapier3d = "0.16.2"
Also, the error message itself:
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/dynamics/solver/generic_velocity_ground_constraint.rs:63:14
stack backtrace:
0: rust_begin_unwind
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/panicking.rs:142:14
2: core::panicking::panic
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/panicking.rs:48:5
3: rapier3d::dynamics::solver::generic_velocity_ground_constraint::GenericVelocityGroundConstraint::generate
4: rapier3d::dynamics::solver::solver_constraints::SolverConstraints<rapier3d::dynamics::solver::velocity_constraint::AnyVelocityConstraint>::compute_generic_ground_constraints
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/dynamics/solver/solver_constraints.rs:235:13
5: rapier3d::dynamics::solver::solver_constraints::SolverConstraints<rapier3d::dynamics::solver::velocity_constraint::AnyVelocityConstraint>::init
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/dynamics/solver/solver_constraints.rs:148:9
6: rapier3d::dynamics::solver::island_solver::IslandSolver::init_and_solve
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/dynamics/solver/island_solver.rs:54:9
7: rapier3d::pipeline::physics_pipeline::PhysicsPipeline::build_islands_and_solve_velocity_constraints
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/pipeline/physics_pipeline.rs:223:17
8: rapier3d::pipeline::physics_pipeline::PhysicsPipeline::step
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.14.0/src/pipeline/physics_pipeline.rs:539:13
9: bevy_rapier3d::plugin::context::RapierContext::step_simulation
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_rapier3d-0.16.2/src/plugin/context.rs:237:21
10: bevy_rapier3d::plugin::systems::step_simulation
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_rapier3d-0.16.2/src/plugin/systems.rs:603:9
11: core::ops::function::FnMut::call_mut
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/ops/function.rs:164:5
12: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/ops/function.rs:290:13
13: <Func as bevy_ecs::system::function_system::SystemParamFunction<(),Out,(F0,F1,F2,F3,F4,F5,F6,F7),()>>::run::call_inner
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/system/function_system.rs:564:21
14: <Func as bevy_ecs::system::function_system::SystemParamFunction<(),Out,(F0,F1,F2,F3,F4,F5,F6,F7),()>>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/system/function_system.rs:567:17
15: <bevy_ecs::system::function_system::FunctionSystem<In,Out,Param,Marker,F> as bevy_ecs::system::system::System>::run_unsafe
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/system/function_system.rs:403:19
16: bevy_ecs::schedule::executor_parallel::ParallelExecutor::prepare_systems::{{closure}}
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/schedule/executor_parallel.rs:194:30
17: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/future/mod.rs:91:19
18: async_executor::Executor::spawn::{{closure}}
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/async-executor-1.4.1/src/lib.rs:144:19
19: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/future/mod.rs:91:19
20: async_task::raw::RawTask<F,T,S>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/async-task-4.3.0/src/raw.rs:511:20
21: async_executor::Executor::try_tick
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/async-executor-1.4.1/src/lib.rs:181:17
22: bevy_tasks::task_pool::TaskPool::scope::{{closure}}
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_tasks-0.8.1/src/task_pool.rs:201:21
23: std::thread::local::LocalKey<T>::try_with
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/std/src/thread/local.rs:445:16
24: std::thread::local::LocalKey<T>::with
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/std/src/thread/local.rs:421:9
25: bevy_tasks::task_pool::TaskPool::scope
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_tasks-0.8.1/src/task_pool.rs:148:9
26: <bevy_ecs::schedule::executor_parallel::ParallelExecutor as bevy_ecs::schedule::executor::ParallelSystemExecutor>::run_systems
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/schedule/executor_parallel.rs:126:9
27: <bevy_ecs::schedule::stage::SystemStage as bevy_ecs::schedule::stage::Stage>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/schedule/stage.rs:884:17
28: bevy_ecs::schedule::Schedule::run_once
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.8.1/src/schedule/mod.rs:342:13
29: bevy_app::app::App::update
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_app-0.8.1/src/app.rs:119:9
30: bevy_winit::winit_runner_with::{{closure}}
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_winit-0.8.1/src/lib.rs:618:21
31: winit::platform_impl::platform::sticky_exit_callback
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/mod.rs:753:5
32: winit::platform_impl::platform::x11::EventLoop<T>::run_return
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/x11/mod.rs:293:17
33: winit::platform_impl::platform::x11::EventLoop<T>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/x11/mod.rs:392:9
34: winit::platform_impl::platform::EventLoop<T>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/mod.rs:669:56
35: winit::event_loop::EventLoop<T>::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/event_loop.rs:154:9
36: bevy_winit::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_winit-0.8.1/src/lib.rs:240:5
37: bevy_winit::winit_runner_with
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_winit-0.8.1/src/lib.rs:663:9
38: bevy_winit::winit_runner
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_winit-0.8.1/src/lib.rs:280:5
39: core::ops::function::Fn::call
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/ops/function.rs:77:5
40: <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/alloc/src/boxed.rs:1954:9
41: bevy_app::app::App::run
at /home/werner/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_app-0.8.1/src/app.rs:135:9
42: forest_pinball::main
at ./src/main.rs:16:5
43: core::ops::function::FnOnce::call_once
at /rustc/fa6ee9375242ae784dab1837dfc0b92f43e787ce/library/core/src/ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
I can also confirm that using an ImpulseJoint
simply works (in the example above).
Bit of debugging later, updated to bevy_rapier
0.17, issue still there.
Here's a bit in GenericVelocityGroundConstraint::generate
:
let (mb2, link_id2) = handle2
.and_then(|h| multibodies.rigid_body_link(h))
.map(|m| (&multibodies[m.multibody], m.id))
.unwrap();
let mj_lambda2 = mb2.solver_id;
I believe that the multibodies.rigid_body_link(h)
bit goes wrong, since the debugger shows an entry from rb2m
being requested that's not in there.
Gonna see if I can figure out why it's not being added in there...
It being RigidBody::Fixed definitely plays into it, there's no error when the big box is Dynamic (I visually confirmed there's a collision)
The ground being fixed puts the interaction into SolverConstraints::generic_ground_interactions
, that presumably plays a role in it, otherwise that code path is unreachable
Ok, so in categorize_joints
, there's this bit:
if multibody_joints.rigid_body_link(joint.body1).is_some()
|| multibody_joints.rigid_body_link(joint.body2).is_some()
{
if !rb1.is_dynamic() || !rb2.is_dynamic() {
generic_ground_joints.push(*joint_i);
} else {
generic_nonground_joints.push(*joint_i);
}
}
The sphere is a dynamic body, the ground is a fixed body and also a link in the multibody. I think there's an incorrect assumption being made here that in dynamic-fixed interaction where one body is fixed and the other is part of a multibody, that the multibody part MUST be the dynamic one, which is not the case here.
Possible workaround: attach the dynamic body to a third fixed body (one possibly without a collider), this removes the possibility of a (Fixed multibody part)-Dynamic interaction from the scene.
So in the pinball use case that I'm using this for, just attach the flippers to something other than the pinball table that the ball cannot touch.