Skip to content

Commit

Permalink
Add support for bevy_xpbd (#22)
Browse files Browse the repository at this point in the history
This PR adds support for `bevy_xpbd` as an alternative physics backend.

## Changes

- Added `xpbd_collisions` feature
- Added `rapier` and `xpbd` sub-modules for `collisions`
- Added `xpbd_collision` example; a clone of `rapier_example`, but
modified for `bevy_xpbd`
- Updated `README.md` and `src/lib.rs` to have separate sections for
`bevy_rapier` and `bevy_xpbd`. If you'd like it to be changed more, let
me know :)
  • Loading branch information
Jondolf authored Nov 12, 2023
1 parent d1820b1 commit c94e650
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* Bump `bevy` to `0.12.x`
* Bump `bevy_rapier3d` to `0.23.x`
* Bump `bevy_inspector_egui` to `0.21.x`
* [bevy_xpbd](https://github.com/Jondolf/bevy_xpbd) collision support (#22):
* Added `xpbd_collisions` feature
* Added `xpbd_collision` example
* Split `collisions` into `rapier` and `xpbd` sub-modules
* Fixed clippy warnings for rust 1.72.0 (#19)
* Added rustfmt config (#19)
* Added vertex colors in flag example (#19)
Expand Down
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ documentation = "https://docs.rs/bevy_silk"
[features]
default = []
rapier_collisions = ["bevy_rapier3d"]
xpbd_collisions = ["bevy_xpbd_3d"]

[dependencies]
# Error handling
Expand All @@ -31,6 +32,12 @@ optional = true
default-features = false
features = ["dim3", "async-collider"]

[dependencies.bevy_xpbd_3d]
version = "0.3"
optional = true
default-features = false
features = ["3d", "f32", "async-collider"]

[dev-dependencies]
bevy-inspector-egui = "0.21"
bevy_rapier3d = "0.23"
Expand Down Expand Up @@ -71,6 +78,11 @@ name = "rapier_collision"
path = "examples/rapier_collision_example.rs"
required-features = ["rapier_collisions"]

[[example]]
name = "xpbd_collision"
path = "examples/xpbd_collision_example.rs"
required-features = ["xpbd_collisions"]

[[example]]
name = "anchors"
path = "examples/anchors_example.rs"
Expand Down
69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ fn spawn(mut commands: Commands) {
```

Custom anchoring allows to :

- pin vertices to various entities, like skeletal mesh joints
- define custom offsets to customize the distance between the anchored
vertices an the target
Expand Down Expand Up @@ -188,6 +189,7 @@ configuration.

You may add wind forces to the simulation for a more dynamic clothing
effect, for each force you may choose from:

- `Wind::Constant` for constant wind force
- `Wind::SinWave` for a sin wave following wind intensity with custom force
and frequency.
Expand Down Expand Up @@ -219,9 +221,16 @@ fn main() {
## Collisions

Enabling the `rapier_collisions` features enable cloth interaction with
other colliders. Add the `bevy_rapier3d::RapierPhysicsPlugin` to your app
and a `ClothCollider` to your entity to enable collisions:
Both [`bevy_rapier`] and [`bevy_xpbd`] are supported for cloth interactions with colliders.
They can be enabled with the `rapier_collisions` and `xpbd_collisions` features respectively.

> Note: Collision support is still experimental for now and is not suited
> for production use. Feedback is welcome!
### `bevy_rapier`

Add `bevy_rapier3d::RapierPhysicsPlugin` to your app and a `ClothCollider`
to your entity to enable collisions:

```rust
use bevy::prelude::*;
Expand All @@ -231,25 +240,55 @@ fn spawn(mut commands: Commands) {
commands.spawn((
PbrBundle {
// Add your mesh, material and your custom PBR data
..Default::default()
..default()
},
ClothBuilder::new(),
ClothCollider::default(),
));
}
```

Three [`bevy_rapier`](https://github.com/dimforge/bevy_rapier) components will be automatically inserted:
Three `bevy_rapier` components will be automatically inserted:

- a `RigidBody::KinematicPositionBased`
- a `Collider` which will be updated every frame to follow the cloth bounds
(AABB)
- a `SolverGroup` set to 0 (`Group::NONE`) in everything, avoiding default
collision solving.
collision solving.

You can customize what collisions will be checked through a `CollisionGroups` (See the [rapier docs](https://rapier.rs/docs/user_guides/bevy_plugin/colliders#collision-groups-and-solver-groups)).
You can customize what collisions will be checked by specifying `CollisionGroups`.
(See the [`bevy_rapier` docs](https://rapier.rs/docs/user_guides/bevy_plugin/colliders#collision-groups-and-solver-groups)).

> Note: Collision support is still experimental for now and is not suited
> for production use. Feedback is welcome !
### `bevy_xpbd`

Add `bevy_xpbd_3d::PhysicsPlugins` to your app and a `ClothCollider`
to your entity to enable collisions:

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

fn spawn(mut commands: Commands) {
commands.spawn((
PbrBundle {
// Add your mesh, material and your custom PBR data
..default()
},
ClothBuilder::new(),
ClothCollider::default(),
));
}
```

Three `bevy_xpbd` components will be automatically inserted:

- a `RigidBody::Kinematic`
- a `Collider` which will be updated every frame to follow the cloth bounds
(AABB)
- a `Sensor` used for avoiding default collision solving.

You can customize what collisions will be checked by specifying `CollisionLayers`.
(See the [`bevy_xpbd` docs](https://docs.rs/bevy_xpbd_3d/latest/bevy_xpbd_3d/components/struct.CollisionLayers.html)).

## Mesh utils

Expand Down Expand Up @@ -296,13 +335,17 @@ run `cargo run --example balloon`

run `cargo run --example moving`

4. [Rapier] Collision example
4. [`bevy_rapier`] collision example

run `cargo run --example rapier_collision --features rapier_collisions`

5. Anchors example
5. [`bevy_xpbd`] collision example

run `cargo run --example xpbd_collision --features xpbd_collisions`

6. Anchors example

run `cargo run --example anchors`

[Rapier]: https://github.com/dimforge/bevy_rapier
[Heron]: https://github.com/jcornaz/heron
[`bevy_rapier`]: https://github.com/dimforge/bevy_rapier
[`bevy_xpbd`]: https://github.com/Jondolf/bevy_xpbd
164 changes: 164 additions & 0 deletions examples/xpbd_collision_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use std::time::Duration;

use bevy::{prelude::*, time::common_conditions::on_timer};
use bevy_inspector_egui::quick::{ResourceInspectorPlugin, WorldInspectorPlugin};
use bevy_silk::prelude::*;
use bevy_xpbd_3d::prelude::*;
use rand::{thread_rng, Rng};

mod camera_plugin;

#[derive(Debug, Resource)]
struct ClothMovement {
sign: f32,
t: f32,
}

fn main() {
App::new()
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
})
.add_plugins(DefaultPlugins)
.add_plugins(PhysicsPlugins::default())
.add_plugins(ResourceInspectorPlugin::<ClothConfig>::new())
.add_plugins(WorldInspectorPlugin::default())
.add_plugins(ClothPlugin)
.add_plugins(camera_plugin::CameraPlugin)
.insert_resource(ClothMovement { sign: -1.0, t: 0.0 })
.add_systems(Startup, (spawn_cloth, setup))
.add_systems(
Update,
(
shoot_balls.run_if(on_timer(Duration::from_secs(6))),
move_cloth,
),
)
.run();
}

fn setup(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
) {
commands.spawn(DirectionalLightBundle {
transform: Transform::from_rotation(Quat::from_rotation_y(5.0)),
..Default::default()
});
let mesh_handle = meshes.add(shape::Cube::new(2.0).into());
[
(Color::BLUE, [-10.0, 0.0]),
(Color::GREEN, [10.0, 0.0]),
(Color::YELLOW, [0.0, -10.0]),
(Color::RED, [0.0, 10.0]),
]
.map(|(color, [x, z])| {
commands.spawn((
PbrBundle {
mesh: mesh_handle.clone(),
transform: Transform::from_xyz(x, 1.0, z),
material: materials.add(StandardMaterial {
base_color: color,
double_sided: true,
..Default::default()
}),
..Default::default()
},
Collider::cuboid(2.0, 2.0, 2.0),
RigidBody::Static,
));
});
commands.spawn((
PbrBundle {
mesh: meshes.add(shape::Cube { size: 24.0 }.into()),
material: materials.add(Color::WHITE.into()),
transform: Transform::from_xyz(0.0, -12.0, 0.0),
..Default::default()
},
Collider::cuboid(24.0, 24.0, 24.0),
RigidBody::Static,
));
}

fn spawn_cloth(
mut commands: Commands,
mut materials: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
asset_server: Res<AssetServer>,
) {
let flag_texture = asset_server.load("Bevy.png");
let (size_x, size_y) = (60, 40);
let mesh = rectangle_mesh((size_x, size_y), (-Vec3::X * 0.5, -Vec3::Y * 0.5), Vec3::Z);
let cloth = ClothBuilder::new()
.with_pinned_vertex_ids(0..size_x)
.with_stick_generation(StickGeneration::Triangles);
commands.spawn((
PbrBundle {
mesh: meshes.add(mesh),
material: materials.add(StandardMaterial {
base_color_texture: Some(flag_texture),
cull_mode: None, // Option required to render back faces correctly
double_sided: true, // Option required to render back faces correctly
..Default::default()
}),
transform: Transform::from_xyz(15.0, 15.0, 15.0),
..Default::default()
},
cloth,
ClothCollider {
dampen_others: Some(0.02),
..Default::default()
},
Name::new("Cloth"),
));
}

fn move_cloth(
time: Res<Time>,
mut query: Query<&mut Transform, With<ClothBuilder>>,
mut movement: ResMut<ClothMovement>,
) {
let delta_time = time.delta_seconds();
for mut transform in query.iter_mut() {
movement.t += delta_time * 2.0;
transform.translation.z += movement.sign * delta_time * 2.0;
if movement.t > 30.0 {
movement.t = 0.0;
movement.sign = -movement.sign;
}
}
}

fn shoot_balls(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let mut rng = thread_rng();
let radius = rng.gen_range(1.0..3.0);
commands.spawn((
PbrBundle {
mesh: meshes.add(
shape::Icosphere {
radius,
subdivisions: 5,
}
.try_into()
.unwrap(),
),
material: materials.add(Color::WHITE.into()),
transform: Transform::from_xyz(0.0, 0.0, -20.0),
..Default::default()
},
LinearVelocity(Vec3::new(
rng.gen_range(-5.0..5.0),
rng.gen_range(10.0..15.0),
rng.gen_range(20.0..30.0),
)),
RigidBody::Dynamic,
Collider::ball(radius),
Name::new("Ball"),
));
}
2 changes: 1 addition & 1 deletion src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ pub mod cloth_builder;
/// cloth rendering module
pub mod cloth_rendering;
/// collider module
#[cfg(feature = "rapier_collisions")]
#[cfg(any(feature = "rapier_collisions", feature = "xpbd_collisions"))]
pub mod collider;
Loading

0 comments on commit c94e650

Please sign in to comment.