dimforge / ncollide

2 and 3-dimensional collision detection library in Rust.
https://ncollide.org
Apache License 2.0
922 stars 106 forks source link

Compound shapes with "holes" intersect with things in those "holes", only in collision world. #201

Closed Sushisource closed 6 years ago

Sushisource commented 6 years ago

In my app I've got "rooms" represented as a compound shape composed of four (or more) walls. I have created an example that illustrates the bug below. The second "room" fits completely within the first, and a contact query indeed returns no contacts. However, the collision world erroneously thinks they are in contact.

Side note: Contact pairs are not debug printable which would be really nice to have

Thanks for your work on the library! Happy to help out if desired.


  #[test]
  fn ncoll_example() {
    type Cub = Cuboid<f32>;
    type CWorld = nc::world::CollisionWorld<f32, u8>;

    let r1_top_bot = ShapeHandle::new(Cub::new(Vector2::new(10.0, 0.2)));
    let r1_left_right = ShapeHandle::new(Cub::new(Vector2::new(0.2, 10.0)));
    let r1 = Compound::new(vec![
      (Isometry2::new(Vector2::new(0.0, 5.0), na::zero()), r1_top_bot.clone()),
      (Isometry2::new(Vector2::new(0.0, -5.0), na::zero()), r1_top_bot),
      (Isometry2::new(Vector2::new(5.0, 0.0), na::zero()), r1_left_right.clone()),
      (Isometry2::new(Vector2::new(-5.0, 0.0), na::zero()), r1_left_right)
    ]);

    let r2_top_bot = ShapeHandle::new(Cub::new(Vector2::new(2.0, 0.2)));
    let r2_left_right = ShapeHandle::new(Cub::new(Vector2::new(0.2, 2.0)));
    let r2 = Compound::new(vec![
      (Isometry2::new(Vector2::new(0.0, 1.0), na::zero()), r2_top_bot.clone()),
      (Isometry2::new(Vector2::new(0.0, -1.0), na::zero()), r2_top_bot),
      (Isometry2::new(Vector2::new(1.0, 0.0), na::zero()), r2_left_right.clone()),
      (Isometry2::new(Vector2::new(-1.0, 0.0), na::zero()), r2_left_right)
    ]);

    let origin = Isometry2::new(Vector2::new(0.0, 0.0), na::zero());

    let contacts = nc::query::contact(&origin, &r1, &origin, &r2, 0.1);
    assert_eq!(contacts, None);

    let mut world = CWorld::new(0.1);
    let q = nc::world::GeometricQueryType::Contacts(0.0, 0.0);
    world.add(origin, ShapeHandle::new(r1), CollisionGroups::new(), q, 1);
    world.add(origin, ShapeHandle::new(r2), CollisionGroups::new(), q, 2);
    world.update();
    assert!(world.contact_pairs().next().is_none());
  }
sebcrozet commented 6 years ago

Note that world.contact_pairs() returns an iterator on all the pairs detected by the broad phase, i.e., the pairs with intersecting bounding volumes. To check if there is actually a contact you have to call the num_contacts method, i.e. your test should be world.contact_pairs().next().2.num_contacts() == 0.

Sushisource commented 6 years ago

Wow, thanks! That helps a lot. The ContactPairs in general are a little hard to grok - the documentation is pretty minimal there.

I'd be happy to add some docs / maybe the debug derives if that's a contribution that sounds worthwhile.

Thanks!

sebcrozet commented 6 years ago

Of course, any contribution on the documentation is very welcome! Debug derives would be useful as well. I'd prefer that no trait inherit from Debug though.