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

Child colliders #154

Merged
merged 45 commits into from
Oct 21, 2023
Merged

Child colliders #154

merged 45 commits into from
Oct 21, 2023

Conversation

Jondolf
Copy link
Owner

@Jondolf Jondolf commented Sep 16, 2023

Objective

A very common and important physics engine feature is to be able to attach multiple colliders to a body and position them relative to that body.

This is already possible via Collider::compound, but it's highly limiting, as you can not specify separate material properties, densities, collision layers or whether a collider is a sensor, or add other components like meshes to the separate colliders. It can also be useful to have separate collision events for the individual colliders instead of for just the shared rigid body entity.

Users expect it to be possible to have colliders be child entities that move with the parent and act as separate colliders, while still affecting the body like a normal compound collider would. It should be possible to nest the colliders in arbitrarily deep hierarchies.

Solution

This PR makes it possible to attach colliders as child entities:

fn setup(mut commands: Commands) {
    commands
        .spawn((RigidBody::Dynamic, Collider::ball(0.5)))
        .with_children(|commands| {
            commands.spawn((
                Transform::from_xyz(3.0, 0.0, 0.0),
                Collider::ball(0.5),
            ));
        });
}

You should be able to nest colliders as much as you want, and they will all be positioned relative to each other using Transform. Each collider has its own AABB, collision events, collision layers, material properties and so on.

For knowing which colliders affect which rigid bodies, we use a ColliderParent component that stores the Entity of the rigid body. The component is automatically updated based on entity hierarchies.

For positioning the colliders correctly and computing contact positions and centers of mass relative to the rigid body, we can use an automatically updated ColliderTransform component that stores the local positional and rotational offset and scale relative to the rigid body (not necessarily the collider's direct parent!). To avoid precision issues and numerical drift, I try to avoid using GlobalTransform and instead use Position and Rotation wherever possible.

The mass properties of bodies are also automatically updated whenever any of the colliders are transformed or removed.

Todo

  • Document child colliders in Collider
  • Wake up bodies when child colliders are transformed
  • Wake up bodies and update their mass properties when their colliders are removed
  • Remove ColliderParent and reset ColliderOffset when the collider's rigid body is removed
  • Verify that arbitrary nesting with translations and rotations works correctly

Follow-up work

  • Collider scale based on transforms Collider scale #189
  • Async colliders Async colliders #190
  • Colliders and AABBs should be debug rendering the sleeping status of the rigid body even as children
  • Find a way to remove ColliderStorageMap
  • Fix remaining contact issues with child colliders
    • Child collider collisions can sometimes be jittery, maybe the position isn't exactly right?
    • The circular motion of offset child colliders rotating around bodies with angular velocity should be taken into account better when expanding AABBs
    • The center of mass seems to affect collisions a bit weirdly; this appears to be a constraint issue rather than a child collider issue though

@Jondolf Jondolf added C-Enhancement New feature or request A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality labels Sep 16, 2023
@Jondolf Jondolf marked this pull request as ready for review October 20, 2023 13:34
This was referenced Oct 20, 2023
@Jondolf Jondolf merged commit 53609b4 into main Oct 21, 2023
4 checks passed
@Jondolf Jondolf deleted the child-colliders branch October 21, 2023 22:37
Jondolf added a commit that referenced this pull request Oct 21, 2023
# Objective

Depends on #154, which needs to be merged first.

Currently, modifying the transform scale of an entity has no effect on its collider. This means that people need to set the collider size to match the size of the scaled mesh, which is often inconvenient. Colliders should get scaled by their transforms like in other game engines such as Godot, Unity and so on.

## Solution

Extend `Collider` to support scaling:

```rust
pub struct Collider {
    /// The raw unscaled collider shape.
    shape: SharedShape,
    /// The scaled version of the collider shape.
    ///
    /// If the scale is `Vector::ONE`, this will be `None` and `unscaled_shape`
    /// will be used instead.
    scaled_shape: SharedShape,
    /// The global scale used for the collider shape.
    scale: Vector,
}
```

Transform scale works in hierarchies the same way as it does for meshes, so scale is propagated to children.

---

## Migration Guide

This:

```rust
let mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 }));
commands.spawn((
    PbrBundle {
        mesh,
        transform: Transform::from_scale(Vec3::new(10.0, 1.0, 10.0)),
        ..default()
    },
    Collider::cuboid(10.0, 1.0, 10.0),
    RigidBody::Static,
));
```

becomes this: (collider size matches unscaled mesh size)

```rust
let mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 }));
commands.spawn((
    PbrBundle {
        mesh,
        transform: Transform::from_scale(Vec3::new(10.0, 1.0, 10.0)),
        ..default()
    },
    Collider::cuboid(1.0, 1.0, 1.0),
    RigidBody::Static,
));
```
@Jondolf Jondolf mentioned this pull request Oct 22, 2023
Jondolf added a commit that referenced this pull request Oct 27, 2023
# Objective

Fixes #104. Depends on #154 and #189.

Currently, there's no easy way to add colliders to meshes loaded from scenes (glTFs and so on). A very common request is to have a way to generate the colliders automatically.

## Solution

Add `AsyncCollider` and `AsyncSceneCollider`, which are essentially equivalents to bevy_rapier's components. Also add a `ComputedCollider` enum that determines if the colliders should be trimeshes or convex hulls. Because these rely on some Bevy features, there is a new `async-collider` feature that is enabled by default, but only in 3D because colliders can't be created from meshes in 2D currently.

### `AsyncCollider` example:

```rust
use bevy::prelude::*;
use bevy_xpbd_3d::prelude::*;

fn setup(mut commands: Commands, mut assets: ResMut<AssetServer>) {
    // Spawn a cube with a convex hull collider generated from the mesh
    commands.spawn((
        AsyncCollider(ComputedCollider::ConvexHull),
        PbrBundle {
            mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
            ..default(),
        },
    ));
}
```

### `AsyncSceneCollider` example:

```rust
use bevy::prelude::*;
use bevy_xpbd_3d::prelude::*;

fn setup(mut commands: Commands, mut assets: ResMut<AssetServer>) {
    let scene = SceneBundle {
        scene: assets.load("my_model.gltf#Scene0"),
        ..default()
    };

    // Spawn the scene and automatically generate triangle mesh colliders
    commands.spawn((
        scene.clone(),
        AsyncSceneCollider::new(Some(ComputedCollider::TriMesh)),
    ));

    // Specify configuration for specific meshes by name
    commands.spawn((
        scene.clone(),
        AsyncSceneCollider::new(Some(ComputedCollider::TriMesh))
            .with_shape_for_name("Tree", ComputedCollider::ConvexHull)
            .with_layers_for_name("Tree", CollisionLayers::from_bits(0b0010))
            .with_density_for_name("Tree", 2.5),
    ));

    // Only generate colliders for specific meshes by name
    commands.spawn((
        scene.clone(),
        AsyncSceneCollider::new(None)
            .with_shape_for_name("Tree".to_string(), Some(ComputedCollider::ConvexHull)),
    ));

    // Generate colliders for everything except specific meshes by name
    commands.spawn((
        scene,
        AsyncSceneCollider::new(ComputedCollider::TriMesh)
            .without_shape_for_name("Tree"),
    ));
}
```

---

## Changes to methods

I also renamed and added some more methods to make things more consistent and simple. I removed `_bevy` from the names because this crate is already for Bevy so it's unnecessary to have the prefix.

### Renamed

- `trimesh_with_flags` → `trimesh_with_config`
- `convex_decomposition_with_params` → `convex_decomposition_with_config`
- `trimesh_from_bevy_mesh` → `trimesh_from_mesh`
- `trimesh_from_bevy_mesh_with_flags` → `trimesh_from_mesh_with_config`
- `convex_decomposition_from_bevy_mesh` → `convex_decomposition_from_mesh`

### Added

- `convex_decomposition_from_mesh_with_config`
- `convex_hull_from_mesh`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Collision Relates to the broad phase, narrow phase, colliders, or other collision functionality C-Enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Child colliders are not handled properly
1 participant