Giter Site home page Giter Site logo

rapier's Introduction

crates.io

Build status crates.io crates.io

Website | Documentation


2D and 3D physics engines for the Rust programming language.


What is Rapier?

Rapier is a set of 2D and 3D physics engines for games, animation, and robotics. These crates are rapier2d, rapier3d, rapier2d-f64, and rapier3d-f64. They are written with the Rust programming language, by the Dimforge organization. It is forever free and open-source!

Roadmap

We update our roadmap at the beginning of each year. Our 2021 roadmap can be seen there. We regularly give updates about our progress on our blog.

Getting started

The easiest way to get started with Rapier is to:

  1. Read the user-guides.
  2. Play with the examples: cargo run --release --bin all_examples2 and cargo run --release --bin all_examples3. Their source code are available on the examples2d/ and examples3d/ directory.
  3. Don't hesitate to ask for help on Discord, or by opening an issue on GitHub.

Resources and discussions

  • Dimforge: See all the open-source projects we are working on! Follow our announcements on our blog.
  • User guide: Learn to use Rapier in your project by reading the official User Guides.
  • Discord: Come chat with us, get help, suggest features, on Discord!
  • NPM packages: Check out our NPM packages for Rapier, if you need to use it with JavaScript/Typescript.

Please make sure to familiarize yourself with our Code of Conduct and our Contribution Guidelines before contributing or participating in discussions with the community.

rapier's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rapier's Issues

Support rounded corners in cuboid colliders

Unity colliders support small rounded corners, which Box2D/Planck-JS do not support, and that are important when making 2-D games when a character moves over several platforms. Without that, numerical errors might spuriously block the character even if platforms are at the same altitude. If the platforms are static, they can be pre-processed into a single shape, but if platforms are moving, they cannot, yet in some configuration they can touch and the character should be able to move smoothly from one to another. The edge radius of Unity box colliders solves that.

As far as I can see, currently Rapier's cuboid colliders do not support that. It would be nice to support that.

Panic indexing empty mj_lambdas in VelocityGroundConstraint::warmstart

I consistently get this panic after switching an existing kinematic rigid body to dynamic:

'index out of bounds: the len is 0 but the index is 0'

core::panicking::panic_bounds_check()
rapier2d::dynamics::solver::velocity_ground_constraint::VelocityGroundConstraint::warmstart(mut slice<rapier2d::dynamics::solver::delta_vel::DeltaVel<f32>> self) Line 227 (rapier2d-0.5.0\src\dynamics\solver\velocity_ground_constraint.rs:227)
rapier2d::dynamics::solver::velocity_constraint::AnyVelocityConstraint::warmstart(mut slice<rapier2d::dynamics::solver::delta_vel::DeltaVel<f32>> self) Line 44 (rapier2d-0.5.0\src\dynamics\solver\velocity_constraint.rs:44)
rapier2d::dynamics::solver::velocity_solver::VelocitySolver::solve(unsigned __int64 self, rapier2d::dynamics::integration_parameters::IntegrationParameters * island_id, rapier2d::dynamics::rigid_body_set::RigidBodySet * params, mut slice<mut parry2d::query::contact_manifolds::contact_manifold::ContactManifold<rapier2d::geometry::contact_pair::ContactManifoldData, rapier2d::geometry::contact_pair::ContactData>*> bodies, mut slice<rapier2d::data::graph::Edge<rapier2d::dynamics::joint::joint::Joint>> manifolds_all, mut slice<rapier2d::dynamics::solver::velocity_constraint::AnyVelocityConstraint> joints_all, mut slice<rapier2d::dynamics::solver::joint_constraint::joint_constraint::AnyJointVelocityConstraint> contact_constraints) Line 42 (rapier2d-0.5.0\src\dynamics\solver\velocity_solver.rs:42)
rapier2d::dynamics::solver::island_solver::IslandSolver::solve_island(unsigned __int64 self, rapier2d::counters::Counters * island_id, rapier2d::dynamics::integration_parameters::IntegrationParameters * counters, rapier2d::dynamics::rigid_body_set::RigidBodySet * params, mut slice<mut parry2d::query::contact_manifolds::contact_manifold::ContactManifold<rapier2d::geometry::contact_pair::ContactManifoldData, rapier2d::geometry::contact_pair::ContactData>*> bodies, slice<usize> manifolds, mut slice<rapier2d::data::graph::Edge<rapier2d::dynamics::joint::joint::Joint>> manifold_indices, slice<usize> joints) Line 47 (rapier2d-0.5.0\src\dynamics\solver\island_solver.rs:47)
rapier2d::pipeline::physics_pipeline::PhysicsPipeline::step(nalgebra::base::matrix::Matrix<f32, nalgebra::base::dimension::U2, nalgebra::base::dimension::U1, nalgebra::base::array_storage::ArrayStorage<f32, nalgebra::base::dimension::U2, nalgebra::base::dimension::U1>> * self, rapier2d::dynamics::integration_parameters::IntegrationParameters * gravity, rapier2d::geometry::broad_phase_multi_sap::BroadPhase * integration_parameters, rapier2d::geometry::narrow_phase::NarrowPhase * broad_phase, rapier2d::dynamics::rigid_body_set::RigidBodySet * narrow_phase, rapier2d::geometry::collider_set::ColliderSet * bodies, rapier2d::dynamics::joint::joint_set::JointSet * colliders, core::option::Option<ContactPairFilter*> joints, core::option::Option<IntersectionPairFilter*> contact_pair_filter, rapier2d::pipeline::event_handler::EventHandler* proximity_pair_filter) Line 173 (rapier2d-0.5.0\src\pipeline\physics_pipeline.rs:173)

2d ColliderBuilder Too obvious docs for some fields

/// A structure responsible for building a new collider.
#[derive(Clone)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct ColliderBuilder {
    /// The shape of the collider to be built.
    pub shape: Shape,
    /// The density of the collider to be built.
    pub density: f32,
    /// The friction coefficient of the collider to be built.
    pub friction: f32,
    /// The restitution coefficient of the collider to be built.
    pub restitution: f32,
    /// The position of this collider relative to the local frame of the rigid-body it is attached to.
    pub delta: Isometry<f32>,
    /// Is this collider a sensor?
    pub is_sensor: bool,
}

This is just a suggestion that "The __ of the collider to be built" kind of documentations could be changed to something that helps understandability. They are too much obvious. As a person that has little knowledge about physics, i do not understand what "restitution", "density" means. And as a newcomer to this library, I do not understand what a sensor is. So those docs could be changed to one sentence explanation of what they are. Or something else that would fit better.
Thanks.

Best way to simulate 2d top down fluid environment ?

Hi, thank you for this lib,

In the example at basic-setup I see there's an easy way to specify gravity.

I'm interested in simulating a top down 2d world, with a water like environment. Something a bit like this: youtube example.

What would be the best way to do that ?

Options :

  • use an existing global drag parameter, similar to how gravity works ;
  • add a new drag parameter through an PR, and then use it ;
    where should I start looking if I want to add such a feature ?
  • manually add a drag force on each body, as drag is not planned in this lib ;
  • use another lib compatible with rapier that can handle this ;
  • something else ?

thanks for your time

Clean up warnings

There are half a dozen or so warnings generated when building rapier3d (unused imports and unused functions). Ignoring these warnings when compiling may lead to us ignoring more serious warnings.

It would be good to forbid warnings in CI and/or by adding this to lib.rs:

#![cfg_attr(not(debug_assertions), deny(warnings))] // Forbid warnings in release builds

applyForce doesn't work at all, rigid body doesn't move

I'm using the JS Bindings of this library and I'm trying to move a rigid body inside the world by applying a force to it.

For some reason rigidBody.applyForce(new RAPIER.Vector(10,0,0),true) should move the rigidBody along x axis but it doesn't.

I'm calling applyForce inside my game loop so definitely a continuous force, I tried waking up the rigidBody manually, I'm applying the force before world.step, should it come after world.step ? I don't think so.

oh rigidBody.setTranslation works but I don't want that since I want to apply force on mouse move so there is a condition here and when that condition is false aka when mouse stops moving the rigidBody returns to its initial position, I'm not sure why

Is this expected behaviour ? what should I do in my case then ?

Add the ability to query rapier for force effects

Sometimes it's important to be able to predict what effect applying a force or impulse to a body will have before actually applying it. It would be nice to to have a dry run version of apply_impulse_at_point which returns the torque the body will experience. A slightly more involved version might be to have a way to create a dry run version of an existing RigidBody which you can then apply many forces to and at the end query it for the total force and torque it experienced.

A motivating example: Say I have a player designed space ship with arbitrarily placed engines. In order to plan how to fire the engines to produce a desired motion I have to be able to predict what effect the engines would have before I actually apply the forces. Currently I have to build a (admittedly not very complicated) model of the body and forces outside of rapier in order to do that planning which adds an opportunity for error and some friction.

Rigid body with mass and no collider contribution behaves strangely

Below is code to compare the behavior of a dynamic cuboid falling on the floor (a static cuboid). The falling cuboid is tested with mass 0.1, 1, 10, 100, 1000. The mass is either set through the collider's density or directly on the rigid body.
In the collider density case every test behaves in exactly the same manner and completes in 205 steps. However, in the rigid body mass case the behavior is different depending on the mass. In the 100 and 1000 mass cases the rigid body moves in a buggy fashion, sliding and bouncing on the floor and eventually tunneling through the floor.

/*
kiss3d = "0.27"
rapier3d = "0.4"
*/

use kiss3d::nalgebra::Point3;
use rapier3d::{
    dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodyHandle, RigidBodySet},
    geometry::{BroadPhase, ColliderBuilder, ColliderHandle, ColliderSet, NarrowPhase},
    math::Vector,
    pipeline::PhysicsPipeline,
};

struct World {
    pipeline: PhysicsPipeline,
    gravity: Vector<f32>,
    integration_parameters: IntegrationParameters,
    broad_phase: BroadPhase,
    narrow_phase: NarrowPhase,
    bodies: RigidBodySet,
    colliders: ColliderSet,
    joints: JointSet,
    event_handler: (),
}

#[derive(Debug)]
enum Mass {
    BodyMass(f32),
    ColliderDensity(f32),
}

impl World {
    fn new() -> Self {
        Self {
            pipeline: PhysicsPipeline::new(),
            gravity: Vector::new(0.0, -9.81, 0.0),
            integration_parameters: IntegrationParameters::default(),
            broad_phase: BroadPhase::new(),
            narrow_phase: NarrowPhase::new(),
            bodies: RigidBodySet::new(),
            colliders: ColliderSet::new(),
            joints: JointSet::new(),
            event_handler: (),
        }
    }

    fn create_ground(&mut self, hx: f32, hy: f32, hz: f32) -> (RigidBodyHandle, ColliderHandle) {
        let body = self.bodies.insert(
            RigidBodyBuilder::new_static()
                .translation(0.0, -hy, 0.0)
                .build(),
        );
        let collider = self.colliders.insert(
            ColliderBuilder::cuboid(hx, hy, hz)
                .friction(0.5)
                .restitution(0.0)
                .build(),
            body,
            &mut self.bodies,
        );
        (body, collider)
    }

    fn create_cuboid(
        &mut self,
        hx: f32,
        hy: f32,
        hz: f32,
        mass: &Mass,
    ) -> (RigidBodyHandle, ColliderHandle) {
        let (body_mass, collider_contribution, collider_density) = match mass {
            Mass::BodyMass(mass) => (*mass, false, None),
            Mass::ColliderDensity(density) => (0.0, true, Some(*density)),
        };
        let body = self.bodies.insert(
            RigidBodyBuilder::new_dynamic()
                .translation(0.0, hy * 2.0, 0.0)
                .rotation(Vector::new(0.0, 0.0, std::f32::consts::FRAC_PI_4 * 0.9))
                .mass(body_mass, collider_contribution)
                .build(),
        );
        let mut collider = ColliderBuilder::cuboid(hx, hz, hz)
            .friction(0.5)
            .restitution(0.0);
        if let Some(density) = collider_density {
            collider = collider.density(density);
        };
        let collider = self
            .colliders
            .insert(collider.build(), body, &mut self.bodies);
        (body, collider)
    }

    fn step(&mut self) {
        self.pipeline.step(
            &self.gravity,
            &self.integration_parameters,
            &mut self.broad_phase,
            &mut self.narrow_phase,
            &mut self.bodies,
            &mut self.colliders,
            &mut self.joints,
            None,
            None,
            &self.event_handler,
        );
    }
}

fn main() {
    let (floor_hx, floor_hy, floor_hz) = (5.0, 0.5, 5.0);
    let (cube_hx, cube_hy, cube_hz) = (0.5, 0.5, 0.5);

    let mut window = kiss3d::window::Window::new("");
    window.set_light(kiss3d::light::Light::StickToCamera);
    let mut camera =
        kiss3d::camera::FirstPerson::new(Point3::new(0.0, 1.0, -5.0), Point3::new(0.0, 1.0, 0.0));
    let mut floor = window.add_cube(floor_hx * 2.0, floor_hy * 2.0, floor_hz * 2.0);
    floor.set_color(0.5, 0.0, 0.0);
    let mut cuboid = window.add_cube(cube_hx * 2.0, cube_hy * 2.0, cube_hz * 2.0);
    cuboid.set_color(0.0, 0.5, 0.0);

    for mass in vec![
        Mass::ColliderDensity(0.1),
        Mass::BodyMass(0.1),
        Mass::ColliderDensity(1.0),
        Mass::BodyMass(1.0),
        Mass::ColliderDensity(10.0),
        Mass::BodyMass(10.0),
        Mass::ColliderDensity(100.0),
        Mass::BodyMass(100.0),
        Mass::ColliderDensity(1000.0),
        Mass::BodyMass(1000.0),
    ] {
        println!("starting test with mass {:?}", mass);

        let mut world = World::new();
        let (floor_body, _) = world.create_ground(floor_hx, floor_hy, floor_hz);
        let (cuboid_body, _) = world.create_cuboid(cube_hx, cube_hy, cube_hz, &mass);

        let mut step = 0;
        loop {
            world.step();

            window.render_with_camera(&mut camera);
            floor.set_local_transformation(*world.bodies.get(floor_body).unwrap().position());
            cuboid.set_local_transformation(*world.bodies.get(cuboid_body).unwrap().position());

            step += 1;
            let body = world.bodies.get(cuboid_body).unwrap();
            if !body.is_moving() {
                println!("object is resting after {} steps\n", step);
                break;
            }
            if step >= 1000 {
                println!("cancelling because still moving after {} steps\n", step);
                break;
            }
        }
    }
}

Initial pipeline step is extremely slow.

If I spawn multiple cones at identical location then initial PhysicsPipeline::step takes much more time that it should.

In debug mode with 10 cones first step takes 15 seconds, 15 cones - 30 seconds, 20 cones - 60 seconds.
In release it's much faster, but still way too slow: 100 cones - 12 seconds, 200 cones - 47 seconds.
All this time one core is fully utilized by the process.

It only happens if cones all are at same location.
I also tried other simple colliders like ball or cuboid, and everything works smooth with them.

[BUG] Sphere-sphere collision response

Here are two balls, radius=0.5 at [0.0, 0.5, 0.0] and [0.1, 1.5, 0.0] on a floor at y=0. friction=1, restitution=0 (balls and floor). Everything else default.

Kapture 2021-01-25 at 13 35 31

The red dots are the reported contact points, drawn like this:

for contact_pair in self.narrow_phase.contact_graph().interactions() {
    for manifold in &contact_pair.manifolds {
        if let Some(collider1) = self.colliders.get(manifold.pair.collider1) {
            if let Some(collider2) = self.colliders.get(manifold.pair.collider2) {
                for contact in manifold.active_contacts() {
                    let world_p1 = collider1.position() * contact.local_p1;
                    let world_p2 = collider2.position() * contact.local_p2;
                    draw_red_sphere(world_p1);
                    draw_red_sphere(world_p2);
                }
            }
        }
    }
}

It looks like the contact is computed with the wrong rotation somewhere (it seems to rotate in the opposite direction of the top sphere).

2D Trimesh seems broken

Here's a quick and dirty reproduction repo for the bug I described yesterday on Discord: https://github.com/msvbg/rapier-trimesh-problem-reproduction. cargo run is enough to run it. The little cube is controlled with WASD, and if you try to hit the static piece of geometry, which is defined by a Rapier trimesh, the collision response is a little bit weird. Turning on simd-stable causes a panic.

Apologies for the messy code, but even getting it down to this state was a fair bit of work...

Consider allowing contacts between static/kinematic bodies.

Currently, the detection of collisions between two static bodies, two kinematic bodies, or one static and one kinematic bodies, will be ignored (the contact points won't even be computed).

It may be useful to have a way to tell Rapier that we do want some contacts between static/kinematic bodies to be computed by the narrow-phase, but ignored by the constraints solver. Maybe that could be useful for character controllers?

I would like to open the discussion on whether this can be useful and why. Please comment or add a 👍 bellow if you are interested in this feature, or 👎 if you think this is completely useless.

Unbounded memory usage when an object falls indefinitely

If an object falls indefinitely, the memory usage of Rapier will increase steadily. This is likely caused by the broad-phase allocating empty grid cells traversed by the falling object.

We should see if there is a way to automatically free the empty broad-phase grid cells without any performance impact.

cargo test fails in build/rapier2d & build/rapier3d

cargo test fails if run from within the build/rapier{2d,3d} directories.

running cargo test --workspace however doesnt.

I'm not sure if this is a real issue, or not, but I'm putting it up here just in case.

Far away object causes panic in broad phase

thread 'main' panicked at 'assertion failed: proxy.aabb.maxs[dim] >= self.min_bound', .cargo/registry/src/github.com-1ecc6299db9ec823/rapier3d-0.4.2/src/geometry/broad_phase_multi_sap.rs:172:13
stack backtrace:
0: std::panicking::begin_panic
1: rapier3d::geometry::broad_phase_multi_sap::SAPAxis::batch_insert
2: rapier3d::geometry::broad_phase_multi_sap::BroadPhase::update_regions
3: rapier3d::geometry::broad_phase_multi_sap::BroadPhase::find_pairs
4: rapier3d::pipeline::physics_pipeline::PhysicsPipeline::step
5: bug_tests::main

This can happen when an object is free falling without a floor.

If the panic is intentional it should be documented under what conditions it can occur and what users can do to prevent it.

Inconsistency

Hey,
I am currently going through the "tutorial" and there are a few things I find inconsistent which in my opinion could be improved.

First there is some inconsistency between the shorthand methods on the builders.

On the RigidBodyBuilder for example we call RigidBodyBuilder::new_static() while on the ColliderBuilder it's ColliderBuilder::ball(0.5)

It would make more sense to stay consistent and name the calls either

RigidBodyBuilder::static(); or ColliderBuilder::new_ball(0.5) respectively

Something similar with linvel(mut self, x: f32, y: f32, z: f32) and angvel(mut self, angvel: AngVector<f32>).
Why use separate values for one and a vector for the other.
It would make more sense to either add linvel(mut self, linvel: LinVector<f32>) and angvel(mut self, x: f32, y: f32, z: f32) as well or just allow for construction via seperate values or vector only.

Another thing that's inconsistent is the creation of joints which unlike RigidBodies and Colliders does not use the builder pattern. Why?

Lastly JointSet::insert<J>( &mut self, bodies: &mut RigidBodySet, body1: RigidBodyHandle, body2: RigidBodyHandle, joint_params: J, ) should be JointSet::insert<J>( &mut self, joint_params: J, body1: RigidBodyHandle, body2: RigidBodyHandle, bodies: &mut RigidBodySet, ) to be consistent with ColliderSet::insert( &mut self, mut coll: Collider, parent_handle: RigidBodyHandle, bodies: &mut RigidBodySet, )

PS: Awesome project. Thank you for your effort.

GPU Support?

Are there any plans for GPU collision detection?

MassProperties for 3D triangle mesh

I'd like to have a 3D physics simulation using dynamic triangle meshes. I think (not sure?) that the main missing component in rapier is that MassProperties has not been implemented for Trimesh, shown here: https://github.com/dimforge/rapier/blob/master/src/geometry/collider.rs#L202

I'd be happy to take a stab at implementing this feature, but I don't understand the engine design or the involved math well enough (e.g. what is principal inertia, how to compute it, so on). If anyone has a good reference, I can take a look.

No proximity or contact detected between moving Kinematic and Static

I'm moving a Kinematic object and expect to get proximity or contact events when the Kinematic intersects a Static object.

I investigated this more, and found that the results are different depending on whether the moving object is a sensor. But in both cases, no intersection between a Kinematic and a Static is reported.

Here are the results. Note that when a moving Kinematic intersects a Static, the result is None (no contact).

Moving object is not a sensor:
Moving Dynamic vs Dynamic => Contact
Moving Static vs Dynamic => None
Moving Kinematic vs Dynamic => None
Moving Dynamic vs Static => Contact
Moving Static vs Static => None
Moving Kinematic vs Static => None
Moving Dynamic vs Kinematic => Contact
Moving Static vs Kinematic => None
Moving Kinematic vs Kinematic => None

Moving object is sensor:
Moving Dynamic vs Dynamic => Proximity
Moving Static vs Dynamic => None
Moving Kinematic vs Dynamic => Proximity
Moving Dynamic vs Static => Proximity
Moving Static vs Static => None
Moving Kinematic vs Static => None
Moving Dynamic vs Kinematic => Proximity
Moving Static vs Kinematic => None
Moving Kinematic vs Kinematic => None

Test program here: https://github.com/vilcans/rapier-intersection-test

Please let me know if I have misunderstood how detection is supposed to work.

Region processing is O(n) complexity

In BroadPhase.update_aabbs, within complete_removals that is called if there is any out of bounds proxy, or in BroadPhase.find_pairs, the code iterates over all regions in an O(n) complexity (where n is the number of regions). So if there is a big world with a lot of regions with fully asleep proxies, which in principle would not need to be processed as their state does not change, they are all touched. It is probably a future optimisation, but I am wondering whether it would be helpful at some point to create a tree structure over the region hash map and track in each node whether that subtree needs any processing.

Originally discussed in #30 (comment)

bevy_rapier documentation incorrect

When I use the main.rs copied from the documentation it doesn't compile.

This is because I had to replace mut commands: Commands with `commands: &mut Commands) as the former isn't compatible with the latest bevy.

After that it compiles, but when I run this I get the following crash:

thread 'TaskPool (22)' panicked at 'Resource does not exist bevy_core::time::time::Time.',

I can trigger the same error if I reference `time: Res<Time>` myself, so it appears that's the underlying cause; the physics plugin assumes a resource is there that isn't there. This clued me in that it depends on the existence of the bevy default plugins.

So the documentation should also be updated to include:

.add_plugins(DefaultPlugins)


After that it compiles and runs.

The pleural of "axis" is "axes", and not "axii"

Sorry if I'm being too pedantic, but this was really starting to get on my nerves while reading the code. The fix should be as simple as doing a find-and-replace. Unless there's some other obscure reason for using the term "axii"?

Simple pendelum explodes

Hello!

I tried setting up capsule as a pendulum like so:

let world_body = bodies.insert(RigidBodyBuilder::new_static().build());
let pendelum_body = bodies.insert(RigidBodyBuilder::new_dynamic().build());

// let half_length = 0.5; // this is fine!
let half_length = 1.0; // this explodes!
let capsule = ColliderBuilder::capsule_x(half_length, 0.5).build();
colliders.insert(capsule, pendelum_body, &mut bodies);

let ball_joint = BallJoint::new(
    [half_length, 0.0, 0.0].into(),
    [half_length, 0.0, 0.0].into(),
);
let joint = JointParams::BallJoint(ball_joint);
joints.insert(&mut bodies, world_body, body, joint);

As the code explains it works great when the pendulum is short, but as soon as it gets longer it explodes almost immediately (i.e. diverges to infinity).

I am using the latest rapier3d = "0.4" with default everything.

Example integration with Bevy

Hi. I'm interested in using the bevy_rapier2d plugin in a new project. I know the integration is early days, but it would be nice to see a simple example of a full integration.

The place where I'm stuck at the moment is how to query for, and update, my RigidBody using a bevy query. Specifically, I'd like to add velocity to my player character while the player presses an arrow key. Something like driving a cube around the screen would be a nice tutorial here.

Octrees

Will Rapier support octrees? I have one project in which octrees will be very important for efficient computation of collisions and I want to know whether I should make my own engine for that or not.

Add a function returning the version of Rapier.

We need a function or constant that returns the version of Rapier. This can be useful for networked applications to check that they all use the same version of the crate. Ideally we would want this version information to include the enabled features of the crate too.

Add ability to cancel events or at least react before the collision physics occurs.

Box2D and its derivative have a preSolve event that is fired when two colliders or sensors are about to get into contact, giving the user code the possibility to cancel the contact. This is for example useful in two cases:

  • for platforms that can be climbed from the bottom, but are solid in the top, as in Super Mario,
  • when one of the object is supposed to be destroyed by the user logic when it touches the other, before the collision occurs.

As far as I've read in the documentation, Rapier currently only provides post world step events.

If proximity events Proximity::WithinMargin would be fired for collider-collider interactions, possibly these could be used for hacking around this limitation, but as the documentation says, due to the speed of movement they might be skipped, so that does not seem like a solution.

The implementation of Clone for RigidBody is not idiomatic

Currently, the implementation of Clone for RigidBody does some unexpected stuffs: it clears the collider set of the clone, and zero the various internal IDs. The rationale behind this is that cloning a rigid-body and adding the clone to a RigidBodySet would result in invalid internal IDs if there were not cleared. But this also means that any code cloning the whole physics state will break.

I think it would make more sense to switch back to the default behavior of Clone (by cloning everything), and simply clear all the internal IDs when calling RigidBodySet::insert.

RigidBody with Trimesh Collider panics on collision

I'm having an issue with this rigidbody with a trimesh collider on it. As soon as it hits ground, the program panics with the following error:

thread 'main' panicked at 'Vector cross product dimension mismatch: must be (3, 1) or (1, 3) but found (2, 1).', /home/elnu/.cargo/registry/src/github.com-1ecc6299db9ec823/nalgebra-0.23.0/src/base/matrix.rs:1674:9

trimesh-bug

The only thing I'm doing to the RigidBody after initialization is settings its mass_properties.inv_mass. Otherwise, it wouldn't fall at all, but get no errors (even on collisions). Any thoughts on why this is happening? Is it a bug?

DeformableBodies

Hello Sébastien,

From what I understand, rapier's goal is at term to replace nphysics, right?

I want to use softbodies (to simulate fibers, like growing hair), should I use nphysics for that as this feature is not yet ported to rapier or am I missing something?

Best,
~Nico

Typo in documentation

There is a small typo in documentation here. In sentence, This will allow use to get a reference to this body later if needed:, the use should be us. I've been briefly looking for the source of the documentation to correct it myself, but did not find it.

rfc: Harness updates and callbacks

After using the harness code for a couple of days, there are some pain points:

the disparity between callbacks on the testbed, and callbacks on the harness.

I'll need to have a proper look at the code again, but I'm thinking of something like:

  • Rename HarnessState to RunState
  • expose callbacks from the testbed, which expose the graphics stuff, which get run on the testbed via the harness callback
  • have the testbed add a callback to the harness which enables the above
  • Make the harness available via the testbed (testbed.run_harness, or harness .. ?)

Regression in performance?

image

This is the chart on the website. It seems that Rapier has lost 5ms over where it was at 2020-09-13, and now it is slower than PhysX.

Internal edges in tri-mesh generate strange contacts

This is a follow-up to dimforge/bevy_rapier#51

It appears that internal edges in tri-mesh colliders can sometimes generate contacts that don't faithfully reflect the geometry of the mesh. Quoting @sebcrozet from the linked PR:

The problem you are experiencing here is likely caused by the common physics-engine problem called the "internal edges problem". Basically, your ball hits the edges between two triangles, and this edge contact ends up having a normal that is not parallel to the adjacent triangles faces' normals. So this deviates the ball from its trajectory since it is as if the floor isn't actually flat.

This problem can be demonstrated by tweaking an example from the bevy_rapier repository: https://github.com/dimforge/bevy_rapier/blob/master/bevy_rapier3d/examples/static_trimesh3.rs

Here is a video of the intended behaviour:

trimesh3d.mp4

If you reduce the number of ramp segments to 3, you'll see this instead:

broken-ramp.mp4

Each of the balls appears to get "stuck" on an internal edge of the ramp mesh and roll along it instead of straight down the ramp, even though each pair of triangles that makes each segment is a "perfectly" (as much as they can be with floats) flat plane.

Feature request: joint drives

Joint drives (or joint motors) allows one to simulate e.g. a motor turning a wheel, a muscle bending an arm, or a pushing piston.

In particular, I'd like to be able to set up angular forces for ball joints and revolute joints in an "angular spring" fashion, i.e.:

torque = stiffness * (target_angle - relative_angle)
       + damping * (target_angular_velocity - relative_angular_velocity)

(the word damping really only makes sense for when target_angular_velocity=0 - some more clever nomenclature may be warranted).

In addition, it would be desireable to clamp this to a given maximum absolute torque.

For a revolve joint this would then be five parameters (target_angle, target_angular_velocity, stiffness, damping, max_torque). One could then use target_angle and stiffness to move the hands of a clock, and target_velocity + damping to turn the wheels of a car.

For ball joints we have three DOFs, so target_angle would need to be a quaternion and target_angular_velocity a vector. For the siffness, damping and max_torque I think one dimension is enough for most use cases (i.e. use the same strength for all three DOFs).

Related to this is a generalization of all joints as one 6DOF joint. If one can set a target angle and velocity as well as stiffness and damping for all the six degrees of freedom independently, then one actually has a general joint constraint. Basically one would lock a degree of freedom by setting a very high stiffness and damping to one dimension (and zero target velocity). This may simplify the inner workings of rapier in the long run, but probably be more work in the short run.


Of course, all this can be approximated using RigidBody::apply_torque and friends, but it really ought to be part of the constraint solver, as forces and torques tend to "lose" against real constraints (after all, one can approximate all the constraints using forces and torques).

Overlap when a ball collider rolls off of another.

I adapted the bevy_rapier2d example and used a ball collider and notice this strange overlap as one ball rolls off of the other where it kind of falls into the other ball’s geometry for a moment:

bevy_rapier2d_short

Expand to see code for reproduction...

use bevy::prelude::*;
use bevy::render::pass::ClearColor;
use bevy_rapier2d::{
    physics::{RapierConfiguration, RapierPhysicsPlugin},
    rapier::{dynamics::RigidBodyBuilder, geometry::ColliderBuilder, math::Vector},
};

const PIXELS_PER_METER: f32 = 60.0;

fn main() {
    App::build()
        .add_resource(ClearColor(Color::rgb(
            0xF9 as f32 / 255.0,
            0xF9 as f32 / 255.0,
            0xFF as f32 / 255.0,
        )))
        .add_plugins(DefaultPlugins)
        .add_plugin(RapierPhysicsPlugin)
        .add_startup_system(setup_graphics.system())
        .add_startup_system(setup_physics.system())
        .run();
}

fn setup_graphics(mut commands: Commands, mut configuration: ResMut<RapierConfiguration>) {
    configuration.scale = 1.0;
    configuration.gravity = Vector::y() * -9.8 * PIXELS_PER_METER;

    commands
        .spawn(LightComponents {
            transform: Transform::from_translation(Vec3::new(1000.0, 100.0, 2000.0)),
            ..Default::default()
        })
        .spawn(Camera2dComponents {
            transform: Transform::from_translation(Vec3::new(0.0, 200.0, 0.0)),
            ..Camera2dComponents::default()
        });
}

fn ground(w: f32, h: f32, materials: &mut ResMut<Assets<ColorMaterial>>) -> SpriteComponents {
    SpriteComponents {
        sprite: Sprite {
            size: Vec2::new(w, h),
            resize_mode: SpriteResizeMode::Automatic,
        },
        material: materials.add(Color::rgb(0.3, 0.3, 0.3).into()),
        ..Default::default()
    }
}

pub fn setup_physics(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    /*
     * Ground
     */
    let ground_size = PIXELS_PER_METER;
    let w = ground_size * 10.0;
    let h = ground_size;

    let rigid_body = RigidBodyBuilder::new_static();
    let collider = ColliderBuilder::cuboid(w / 2.0, h / 2.0);
    commands
        .spawn((rigid_body, collider))
        .with_bundle(ground(w, h, &mut materials));

    let rigid_body = RigidBodyBuilder::new_static()
        .rotation(std::f32::consts::FRAC_PI_2)
        .translation(-w / 2.0 - h / 2.0, w / 2.0 + h / 2.0);
    let collider = ColliderBuilder::cuboid(w / 2.0, h / 2.0);
    commands
        .spawn((rigid_body, collider))
        .with_bundle(ground(w, h, &mut materials));

    let rigid_body = RigidBodyBuilder::new_static()
        .rotation(std::f32::consts::FRAC_PI_2)
        .translation(w / 2.0 + h / 2.0, w / 2.0 + h / 2.0);
    let collider = ColliderBuilder::cuboid(w / 2.0, h / 2.0);
    commands
        .spawn((rigid_body, collider))
        .with_bundle(ground(w, h, &mut materials));

    let num = 1;
    let rad = ground_size / 2.0;

    let shift = rad * 2.0 + 5.0;
    let centerx = shift * (num as f32) / 2.0;
    let centery = shift / 2.0;

    // Bevy balls
    for i in 0..num {
        for j in 0usize..num * 2 {
            let x = i as f32 * shift + ((j % 5) as f32) - centerx;
            let y = j as f32 * shift + centery + 200.0;

            // Build the rigid body.
            let body = RigidBodyBuilder::new_dynamic().translation(x, y);
            let collider = ColliderBuilder::ball(rad).density(1.0);
            let texture_handle = asset_server.load("branding/icon.png");
            let sprite = SpriteComponents {
                sprite: Sprite {
                    size: Vec2::new(rad * 2.0, rad * 2.0),
                    resize_mode: SpriteResizeMode::Manual,
                },
                material: materials.add(texture_handle.into()),
                ..Default::default()
            };
            commands.spawn((body, collider)).with_bundle(sprite);
        }
    }
}

Add Collision Layers and Masks.

Right now all the colliders collide with each other, there is no way to tell which colliders can collide.
One way to do this are collision layers and masks.

A collision layer is where the given collider leaves and the mask is which layers can the collider collide.
Layer and mask are just a 32 bit integer and this makes the comparisson very fast.
Godot Game engine uses this technic
An example of setting individual bits can been seen here :
https://github.com/godotengine/godot/blob/00949f0c5fcc6a4f8382a4a97d5591fd9ec380f8/scene/2d/area_2d.cpp#L483

And the comparison here:
https://github.com/godotengine/godot/blob/8e8699e36b81f04fb7acb403bb567648f576867f/servers/physics_2d/collision_object_2d_sw.h#L193

Make shapes more extensible / Add support for user-defined shapes

Currently it appears that the set of possible shapes that we can deal with are present in a single Shape enum. Perhaps the extensibility of this design can be improved. There appears to be some thoughts of this for the future: there is a ContactDispatcher trait, but its one implementation, DefaultContactDispatcher, is hard-coded into the narrow-phase.

I'm wondering what the goals are for user defined shapes and what the plan is? I would guess since we are not using ncollide in full, we want to avoid dynamic dispatch for performance reasons. We could look into options using traits instead. I would like to have this feature for the game I am working on, and I would love to help add it into Rapier. Having this feature would probably also help us make progress faster on issues like #29 and #27.

GPU Support?

Is rapier planned to get GPU collision detection at some time?

Async support

I'm opening this issue to get a discussion on async support in rapier, namely, does it even make sense?

Without looking at the design of rapier, I don't believe there's too much opportunity for work-stealing when computing the physics simulation, and if that's the case async might not make much sense.

The only thing that comes to mind is sharing a thread pool in a project that already has an asynchronous executor defined (i.e. tokio, smol, async-std). If your project is performing concurrent operations, and some of your dependencies are all trying to spawn threads to do their own work, maybe it could be beneficial if they all shared the same thread pool.

But again, maybe this doesn't make sense and doesn't warrant the effort! I at least want to have the conversation and see what folks have to say.

[BUG] Energy is not conserved for perfectly elastic collisions

The test: ball dropped on a static box, both with restitution=1.
Expected: the ball bounce back up to the same height.
Actual: the ball bounces higher and higher.

The good news is I have a fix that works locally. Just need to clean it up a bit :)


Latest master:

Screen Shot 2021-01-27 at 10 12 52

With my changes:

Screen Shot 2021-01-27 at 10 11 46

Energy is conserved to within 1e-5 per bounce.

Switch to 64-bit handles

I noticed that arena::Index is implemented as usize + u64. This is in contrast with e.g. slotmap which uses 64-bit keys (u32 + u32).

Switching to a smaller index would make for faster lookups, less memory usage etc, but would limit the number of simultaneous bodies/joints/colliders to 2^32 (which like a reasonable limitation). But is there perhaps some subtlety with regard to determinism that I'm missing (e.g. with regards to generation wrapping around after cycling through 2^32 elements)?

Allow access to contact information

Right now, accessing to contact information (contact normals, forces, etc.) between colliders is nearly impossible. While the narrow-phase does give access to the contact graph, this graph cannot be queried because it requires some internal indices given to each colliders.

We should make it possible to retrieve contacts between colliders, using the collider handles instead of these internal indices.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.