dimforge / rapier

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

Free fall distance calculation doesn't meet expectaion. #525

Closed ElderJoy closed 8 months ago

ElderJoy commented 1 year ago

I'm learning rapier engine and run first example from rust guide: https://rapier.rs/docs/user_guides/rust/getting_started, but without ground and with 0 start height to check how it is calculates free fall height. Results are different than I expected. Next log illustrates each step of calculation of 1/60 second each:

Ball altitude: 0 0
Ball altitude: 1 -0.0027250003
Ball altitude: 2 -0.008175001
Ball altitude: 3 -0.016350001
...
Ball altitude: 60 -4.986749

According to free-fall calculator (https://www.omnicalculator.com/physics/free-fall) for 9.81 gravity they should be as next:

0: 0
1: -0.0013625
2: -0.00545
3: -0.0122625
..
60: -4.905

There is difference with expected calculations:

1: -0.0013625
2: -0.002725
3: -0.0040875
..
60: -0.081749

For some reason rapier adding (with "-" sign) 0.0013625 m. for each step. It is exactly the distance, that body should fall for the first 1/60 sec. Why is that? Am I doing something wrong? Code, that I'm using:

use rapier2d::prelude::*;

fn main() {
    let mut rigid_body_set = RigidBodySet::new();
    let mut collider_set = ColliderSet::new();

    /* Create the bouncing ball. */
    let rigid_body = RigidBodyBuilder::dynamic()
        .translation(vector![0.0, 0.0])
        .build();
    let collider = ColliderBuilder::ball(0.001).restitution(0.7).build();
    let ball_body_handle = rigid_body_set.insert(rigid_body);
    collider_set.insert_with_parent(collider, ball_body_handle, &mut rigid_body_set);

    /* Create other structures necessary for the simulation. */
    let gravity = vector![0.0, -9.81];
    let integration_parameters = IntegrationParameters::default();
    let mut physics_pipeline = PhysicsPipeline::new();
    let mut island_manager = IslandManager::new();
    let mut broad_phase = BroadPhase::new();
    let mut narrow_phase = NarrowPhase::new();
    let mut impulse_joint_set = ImpulseJointSet::new();
    let mut multibody_joint_set = MultibodyJointSet::new();
    let mut ccd_solver = CCDSolver::new();
    let physics_hooks = ();
    let event_handler = ();

    let ball_body = &rigid_body_set[ball_body_handle];
    println!("Ball altitude: 0 {}", ball_body.translation().y);

    /* Run the game loop, stepping the simulation once per frame. */
    for i in 1..61 {
        physics_pipeline.step(
            &gravity,
            &integration_parameters,
            &mut island_manager,
            &mut broad_phase,
            &mut narrow_phase,
            &mut rigid_body_set,
            &mut collider_set,
            &mut impulse_joint_set,
            &mut multibody_joint_set,
            &mut ccd_solver,
            None,
            &physics_hooks,
            &event_handler,
        );

        let ball_body = &rigid_body_set[ball_body_handle];
        println!("Ball altitude: {} {}", i, ball_body.translation().y);
    }
}
sebcrozet commented 8 months ago

The accuracy of the free-fall calculation will depend on the integration scheme. Rapier, like most physics engines, uses simplectic Euler which is a first-order integration scheme, so it won’t capture exactly the effect of gravity on position (which is quadratically dependent on the timestep). I suspect the omnicalculator is relying on a more accurate (or analytic) scheme.