Skip to content

contacts_enabled behaves incorrectly when there's more than 1 joint between 2 rigid bodies #866

@nstoddard

Description

@nstoddard

From the code in NarrowPhase::compute_contacts, I get the impression that if there's more than one joint between two objects, it's supposed to disregard the contact if any of the joints have contacts_enabled set to false. However, that isn't how it behaves in practice; it seems to only check contacts_enabled for the most recently added joint.

My best guess is that something is wrong with ImpulseJointSet::joints_between: even if there's more than one joint connecting two objects, it always returns only 1 element. ImpulseJointSet::attached_joints, however, returns the correct result.

All of this is demonstrated with the repro code below. Since one of the joints has contacts_enabled set to false, I would expect it to ignore the contact.

use rapier2d::prelude::*;

#[derive(Default)]
struct GamePhysics {
    physics: PhysicsPipeline,
    island_manager: IslandManager,
    broad_phase: DefaultBroadPhase,
    narrow_phase: NarrowPhase,
    bodies: RigidBodySet,
    colliders: ColliderSet,
    impulse_joints: ImpulseJointSet,
    multibody_joints: MultibodyJointSet,
    ccd_solver: CCDSolver,
}

impl GamePhysics {
    fn step(&mut self) {
        self.physics.step(&Vector::new(0.0, 9.8), &Default::default(), &mut self.island_manager, &mut self.broad_phase, &mut self.narrow_phase, &mut self.bodies, &mut self.colliders, &mut self.impulse_joints, &mut self.multibody_joints, &mut self.ccd_solver, None, &(), &MyEventHandler);
    }
}

struct MyEventHandler;

impl EventHandler for MyEventHandler {
    fn handle_collision_event(&self, _: &RigidBodySet, _: &ColliderSet, _: CollisionEvent, _: Option<&ContactPair>) {
        println!("Collision");
    }
    
    fn handle_contact_force_event(&self, _: f32, _: &RigidBodySet, _: &ColliderSet, _: &ContactPair, _: f32) {}
}

fn main() {
    let mut physics = GamePhysics::default();
    
    let a = physics.bodies.insert(RigidBodyBuilder::dynamic());
    let b = physics.bodies.insert(RigidBodyBuilder::dynamic());
    physics.colliders.insert_with_parent(ColliderBuilder::ball(1.0)
        .active_events(ActiveEvents::COLLISION_EVENTS), a, &mut physics.bodies);
    physics.colliders.insert_with_parent(ColliderBuilder::ball(1.0)
        .active_events(ActiveEvents::COLLISION_EVENTS), b, &mut physics.bodies);

    // If the following two lines are switched, it correctly doesn't detect a collision
    physics.impulse_joints.insert(a, b, RevoluteJointBuilder::new().contacts_enabled(false), true);
    physics.impulse_joints.insert(a, b, RevoluteJointBuilder::new().contacts_enabled(true), true);

    // Should be 2, but instead is 1
    println!("Joints between a and b: {}", physics.impulse_joints.joints_between(a, b).count());
    // This one is correct
    println!("Joints for a: {}", physics.impulse_joints.attached_joints(a).count());
    
    // This incorrectly detects a collision
    physics.step();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions