Skip to content

Commit

Permalink
Async colliders (#190)
Browse files Browse the repository at this point in the history
# 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`
  • Loading branch information
Jondolf authored Oct 27, 2023
1 parent 93fb46e commit dea0d24
Show file tree
Hide file tree
Showing 11 changed files with 564 additions and 63 deletions.
3 changes: 1 addition & 2 deletions crates/bevy_xpbd_2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keywords = ["gamedev", "physics", "simulation", "xpbd", "bevy"]
categories = ["game-development", "science", "simulation"]

[features]
default = ["2d", "f32", "collider-from-mesh", "parallel"]
default = ["2d", "f32", "parallel"]
2d = []
f32 = ["dep:parry2d"]
f64 = ["dep:parry2d-f64"]
Expand All @@ -24,7 +24,6 @@ enhanced-determinism = [
"parry2d-f64?/enhanced-determinism",
"glam/libm",
]
collider-from-mesh = ["bevy/bevy_render"]

[lib]
name = "bevy_xpbd_2d"
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_xpbd_3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keywords = ["gamedev", "physics", "simulation", "xpbd", "bevy"]
categories = ["game-development", "science", "simulation"]

[features]
default = ["3d", "f32", "collider-from-mesh", "parallel"]
default = ["3d", "f32", "async-collider", "parallel"]
3d = []
f32 = ["dep:parry3d"]
f64 = ["dep:parry3d-f64"]
Expand All @@ -25,6 +25,7 @@ enhanced-determinism = [
"glam/libm",
]
collider-from-mesh = ["bevy/bevy_render"]
async-collider = ["bevy/bevy_scene", "bevy/bevy_gltf", "collider-from-mesh"]

[lib]
name = "bevy_xpbd_3d"
Expand Down Expand Up @@ -90,6 +91,10 @@ required-features = ["3d"]
name = "trimesh_shapes_3d"
required-features = ["3d"]

[[example]]
name = "async_colliders"
required-features = ["3d", "async-collider"]

[[bench]]
name = "cubes"
harness = false
Binary file added crates/bevy_xpbd_3d/assets/ferris.glb
Binary file not shown.
69 changes: 69 additions & 0 deletions crates/bevy_xpbd_3d/examples/async_colliders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! An example showcasing how to create colliders for meshes and scenes
//! using `AsyncCollider` and `AsyncSceneCollider` respectively.
use bevy::prelude::*;
use bevy_xpbd_3d::prelude::*;
use examples_common_3d::XpbdExamplePlugin;

fn main() {
App::new()
.add_plugins((DefaultPlugins, XpbdExamplePlugin))
.add_systems(Startup, setup)
.run();
}

fn setup(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
assets: ResMut<AssetServer>,
) {
// Spawn ground and generate a collider for the mesh using AsyncCollider
commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane::from_size(8.0))),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default()
},
AsyncCollider(ComputedCollider::TriMesh),
RigidBody::Static,
));

// Spawn Ferris the crab and generate colliders for the scene using AsyncSceneCollider
commands.spawn((
SceneBundle {
// The model was made by RayMarch, licenced under CC0-1.0, and can be found here:
// https://github.com/RayMarch/ferris3d
scene: assets.load("ferris.glb#Scene0"),
transform: Transform::from_xyz(0.0, 2.0, 0.0).with_scale(Vec3::splat(2.0)),
..default()
},
// Create colliders using convex decomposition.
// This takes longer than creating a trimesh or convex hull collider,
// but is more performant for collision detection.
AsyncSceneCollider::new(Some(ComputedCollider::ConvexDecomposition(
VHACDParameters::default(),
)))
// Make the arms heavier to make it easier to stand upright
.with_density_for_name("armL_mesh", 5.0)
.with_density_for_name("armR_mesh", 5.0),
RigidBody::Dynamic,
));

// Light
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 3000.0,
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(2.0, 8.0, 2.0),
..default()
});

// Camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-5.0, 3.5, 5.5).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
2 changes: 1 addition & 1 deletion crates/bevy_xpbd_3d/examples/trimesh_shapes_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn setup(
for (i, shape) in shapes.into_iter().enumerate() {
commands.spawn((
RigidBody::Dynamic,
Collider::trimesh_from_bevy_mesh(&shape).unwrap(),
Collider::trimesh_from_mesh(&shape).unwrap(),
Position(Vector::new(
-14.5 / 2.0 + i as Scalar / (num_shapes - 1) as Scalar * 14.5,
2.0,
Expand Down
Loading

0 comments on commit dea0d24

Please sign in to comment.