Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete rewrite for ergonomics improvements + update to Bevy 0.7 and Rapier 0.12 #138

Merged
merged 12 commits into from
Apr 29, 2022

Conversation

sebcrozet
Copy link
Member

@sebcrozet sebcrozet commented Apr 28, 2022

This is the third design of this plugin:

  1. The very first design of this plugin was a very shallow integration of Rapier: we added rapier sets as resources, and the user was expected to create rigid-bodies and colliders in a way very similar to the vanilla Rapier API. This felt very awkward to use with Bevy.
  2. Then, we changed the design to split colliders and rigid-bodies into multiple components, and Rapier was modified to be able to use these components directly through bevy Query. This improved ergonomics significantly but also reduced performances (because of the inefficiency of randomly accessing components through a Bevy Query). This ergonomics gain was significantly hurt by the introduction of the required Component trait implementation in bevy 0.6.
  3. This third design no longer relies on Query to store the components used by Rapier itself. Instead, we define our own components that get synchronized with Rapier at each frame (somewhat similar to the way the heron plugin is designed).

This new design has many advantages:

  • No conversion between nalgebra and glam is needed any more. Users can use glam everywhere and the plugin takes care of the conversion when synchronizing with Rapier’s state. Note however that users have access to the original Rapier objects, accessing to these raw objects will require using nalgebra (but you shouldn’t need to access them unless for very advanced stuffs).
  • The Reflect trait can be auto-implemented for many components (not all of them yet).
  • All components are optional. With the previous version, removing any component would result in a panic of the physics-engine.

Colliders

Colliders can be created easily by inserting one of two components: Collider or AsyncCollider. The AsyncCollider is only available in 3D right now: it allows you to create a collider from a Bevy Handle<Mesh>. If that mesh isn’t loaded yet (i.e. if you are using asynchronous asset loading), the corresponding Collider will automatically be created once the Mesh becomes available. Some examples:

commands.spawn()
    .insert(Collider::cuboid(1.0, 2.0, 3.0));

let handle: Handle<Mesh> = server.load("bunny.obj");
commands.spawn()
    .insert(AsyncCollider::Mesh(handle)); // The collider will be created once the mesh finish loading.

When the mesh doesn’t originate from asset loading, a mesh collider can be created directly with Collider::bevy_mesh(mesh) where mesh is just a bevy::render::Mesh.

Just like before, colliders are attached to rigid-bodies by adding the component to the same entity, or to its children.

Other optional components can be added to customize the collider’s behavior: Friction, Restitution, CollisionGroups, etc. The collider position can be controlled with a bevy Transform. The scaling part of that transform is automatically taken into account to scale the collider accordingly (unless the ColliderScale component is added to control the scaling manually).

Rigid-bodies

Rigid-bodies can be created easily by inserting the RigidBodyComponent:

commands.spawn()
    .insert(RigidBody::KinematicVelocityBased);

All the other components are optional to customize the rigid-body’s behavior: Velocity, LockedAxes, ExternalForce, etc. The rigid-body’s position can be controlled with a bevy Transform. The scaling part of that transform has no effect on the rigid-body.

Joints

Impulse joints and multibody joints are now supported as components. To attach a joint to rigid-body users only need to add the joint to the rigid-body’s entity. Impulse joints can also be attached to one of the rigid-body entity’s its children (because it is possible to have multiple impulse joint attached to the same rigid-body).

Scene queries

All the scene queries are made through the RapierContext object. These queries have been fully wrapped to only expose glam types. Querying the contact graph on intersection graph has also been wrapped to only expose glam types.

Pixel scaling in 2D

One common source of confusion is the fact that 2D Bevy uses pixels as its default unit to place sprites, whereas Rapier use meters. Therefore a common mistake is to choose 1meter = 1pixel which results in less accuracy and slow-motion simulation (unless one adjust the gravity). The solution we introduced previously was the RapierConfiguration::scale that would affect the way rigid-body positions were synchronized with the bevy Transform. However, the user had to be careful to take that scale into account everywhere else (when initializing colliders, modifying velocities, etc.)

With this new design, it is possible to set a scale at the plugin-level, and that scale will be applied transparently throughout the engine. So basically, you can say "1 meter = 100 pixels" and then work with Rapier as if pixel was its distance unit (so having a collider sized as Collider::cuboid(500.0, 100.0) is perfectly fine). The plugin will deal with the conversion between pixels and meters transparently when it calls into Rapier itself.

app.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0)) // 100 pixel per meter.

Debug-rendering

Debug rendering has been completely changed. Rapier 0.12 itself has its own official backend-agnostic debug-renderer (dimforge/rapier#315). This renderer is simple, and based on lines for better visibility when superimposed with your own graphics representation of your scene objects.

This renderer is able to render all the default shapes supported by Rapier (including round shapes), and will draw lines for joints.
It is completely non-instrusive (it doesn’t insert any component to any of the user’s entities) and automatic (it will render everything automatically, without the need to add components to the objects that need rendering). This renderer will render exactly what Rapier itself sees.

image

Debug-snapshots

Since this new design uses Rapier’s ColliderSet and RigidBodySet under the hood (instead of bevy Query), it is now possible to serialize (with serde) the RapierContext. This can help debug issues that are hard to reproduce (by simply sending snapshots of the simulation shortly before the bug occurs).

@CleanCut
Copy link
Contributor

CleanCut commented May 6, 2022

❤️ You are making all of my wildest dreams come true.

quackercrumbs added a commit to quackercrumbs/tower-defense that referenced this pull request May 15, 2022
Rapier seems to have caught up with bevy so i had to do alot of
refactors for rapier colliders as well.

This included restructuring how the entities are built. and moving away
from using nalgebra.

More information about these update can be found here:
- dimforge/bevy_rapier#138
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants