nphysics icon indicating copy to clipboard operation
nphysics copied to clipboard

Can't raycast static bodies

Open n0uk opened this issue 7 years ago • 4 comments

Hi!

I miss something, or ray doesn't intersect static rigidbodies?

Sample code:

extern crate nalgebra;
extern crate ncollide;
extern crate nphysics2d;

use nphysics2d::object::RigidBody;
use nphysics2d::world::World;
use ncollide::shape::Ball;
use ncollide::world::CollisionGroups;
use ncollide::query::Ray;
use nalgebra::{Vector2, Point2, Translation2};

fn create_static_body() -> RigidBody<f32> {
    RigidBody::new_static(Ball::new(100.0), 1.0, 0.3)
}

fn create_dynamic_body() -> RigidBody<f32> {
    RigidBody::new_dynamic(Ball::new(100.0), 1.0, 0.3, 1.0)
}

fn main() {
    // Create world
    let mut physics_world = World::new();
    physics_world.set_gravity(Vector2::new(0.0, 0.0));

    // Create static body at left
    let mut rb = create_static_body();
    rb.append_translation(&Translation2::new(-1000.0, 0.0));
    physics_world.add_rigid_body(rb);

    // Create dynamic body at right
    let mut rb = create_dynamic_body();
    rb.append_translation(&Translation2::new(1000.0, 0.0));
    physics_world.add_rigid_body(rb);

    let all_groups = &CollisionGroups::new();

    // Raycast over static body
    let ray = Ray::new(Point2::new(-1000.0, 1000.0), Vector2::new(0.0, -1.0));
    println!("Static: {}",
             physics_world
                 .collision_world()
                 .interferences_with_ray(&ray, all_groups)
                 .count());

    // Raycast over dynamic body
    let ray = Ray::new(Point2::new(1000.0, 1000.0), Vector2::new(0.0, -1.0));
    println!("Dynamic: {}",
             physics_world
                 .collision_world()
                 .interferences_with_ray(&ray, all_groups)
                 .count());

    // Raycast from left to right over static and dynamic bodies
    let ray = Ray::new(Point2::new(-2000.0, 0.0), Vector2::new(1.0, 0.0));
    println!("Dynamic + Static: {}",
             physics_world
                 .collision_world()
                 .interferences_with_ray(&ray, all_groups)
                 .count());
}

Output:

Static: 0 Dynamic: 1 Dynamic + Static: 1

n0uk avatar May 02 '17 07:05 n0uk

This looks like a bug with how collision groups are handled.

sebcrozet avatar May 02 '17 17:05 sebcrozet

Yes, it's collision groups, because: RigidBodyCollisionGroups::new_static(); return group with blacklist: 0b110000000000000000000000000000 And all_groups have membership: 0b111111111111111111111111111111 and

189: ncollide_pipeline/world/collision_groups.rs

self.membership  & other.blacklist == 0 &&
other.membership & self.blacklist  == 0 &&

is false.

So, workaround for me is exclude {STATIC_GROUP_ID, SENSOR_GROUP_ID} from membership of ray collision groups.

n0uk avatar May 03 '17 07:05 n0uk

Thanks for digging into this. I'm not sure yet what is the best way to deal with this. The main solution I see would be to add a ray-casting function to the physical world (not the collision world because ncollide has no knowledge about the two special groups STATIC_GROUP_ID and SENSOR_GROUP_ID) that would adjust the memberships.

sebcrozet avatar May 03 '17 21:05 sebcrozet

I'm very sorry for my English.

I'm not sure, but It's looks like main problem here is "blacklist" field. I think it is redundant field, any case may be covered using "membership" and "whitelist" masks.

May be better solution - keep "blacklist" field on static rigidbodies as zero, and use something like:

Static Rigidbody Sensor Rigidbody Dynamic Rigidbody
Membership 0b100 0b010 0b111
Whitelist 0b001 0b001 0b111

This way fix all issues with raycast/aabb_query.

Most of physic engines use 2 masks for handling collisions, for example:

p2.js

if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
    // The shapes can collide
}

Box2D

  bool collide =
          (filterA.maskBits & filterB.categoryBits) != 0 &&
          (filterA.categoryBits & filterB.maskBits) != 0;

Bullet Physics

	bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
	collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);

n0uk avatar May 04 '17 06:05 n0uk