dimforge / rapier

2D and 3D physics engines focused on performance.
https://rapier.rs
Apache License 2.0
3.78k stars 235 forks source link

Crash when combining a MultibodyJoint, a fixed parent object, and a dynamic body colliding with the parent object #400

Open werner291 opened 1 year ago

werner291 commented 1 year ago

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"
werner291 commented 1 year ago

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.
werner291 commented 1 year ago

I can also confirm that using an ImpulseJoint simply works (in the example above).

werner291 commented 1 year ago

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...

werner291 commented 1 year ago

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)

werner291 commented 1 year ago

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

werner291 commented 1 year ago

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.

werner291 commented 1 year ago

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.