dimforge / parry

2D and 3D collision-detection library for Rust.
https://parry.rs
Apache License 2.0
557 stars 97 forks source link

MassProperties::from_compound produces NaN values if total mass is 0 #20

Closed Demindiro closed 3 years ago

Demindiro commented 3 years ago

Due to a division by 0 in Sum<MassProperties> it is possible to get NaN values.

https://github.com/dimforge/parry/blob/8241547b85e963d975d9e5b4024b84dec6cba7f1/src/mass_properties/mass_properties.rs#L386

This can happen when e.g. trying to compute the MassProperties of a HeighField.

I believe the sum function should either panic or return as soon as it detects the total mass is 0.

Reproduction project:

// src/main.rs

use rapier3d::pipeline::*;
use rapier3d::dynamics::*;
use rapier3d::geometry::*;
use rapier3d::na::*;

fn main() {

    let mut pipeline = PhysicsPipeline::new();
    let gravity = Vector3::new(0.0, -9.81, 0.0);
    let integration_parameters = IntegrationParameters::default();
    let mut broad_phase = BroadPhase::new();
    let mut narrow_phase = NarrowPhase::new();
    let mut bodies = RigidBodySet::new();
    let mut colliders = ColliderSet::new();
    let mut joints = JointSet::new();
    let physics_hooks = ();
    let event_handler = ();

    let body = RigidBodyBuilder::new_dynamic().translation(0.0, 1.01, 0.0).build();
    let handle = bodies.insert(body);
    let collider = ColliderBuilder::ball(1.0).build();
    colliders.insert(collider, handle, &mut bodies);

    let shape = SharedShape::heightfield(DMatrix::zeros(10, 10), Vector3::new(9.0, 1.0, 9.0));
    let collider = ColliderBuilder::new(shape.clone()).build();
    let mut body = RigidBodyBuilder::new_static().build();
    let position = *body.position();
    let mp = MassProperties::from_compound(1.0, &vec![(position, shape)][..]);
    dbg!(collider.mass_properties()); // This is fine
    dbg!(mp); // This has NaN center of mass
    body.set_mass_properties(mp, true);
    let handle = bodies.insert(body);
    colliders.insert(collider, handle, &mut bodies);

    loop {
        pipeline.step(
            &gravity,
            &integration_parameters,
            &mut broad_phase,
            &mut narrow_phase,
            &mut bodies,
            &mut colliders,
            &mut joints,
            &physics_hooks,
            &event_handler,
        );
        for (_, b) in bodies.iter() {
            if b.is_dynamic() {
                // The position of the ball also becomes NaN due to this
                println!("{}", b.position());
                assert!(!b.position().translation.x.is_nan());
            }
        }
    }
}
# Cargo.toml

[package]
name = "rapier3d_heightfield_nan"
version = "0.1.0"
authors = ["David Hoppenbrouwers <david@salt-inc.org>"]
edition = "2018"

[dependencies]
rapier3d = "*"