Skip to content

Commit

Permalink
Merge pull request #316 from dollisgame/rework-async-colliders
Browse files Browse the repository at this point in the history
Improve ergonomics of all async colliders
  • Loading branch information
sebcrozet authored Feb 4, 2023
2 parents 3899db0 + 127bbab commit 0df92f3
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 59 deletions.
34 changes: 14 additions & 20 deletions src/geometry/collider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,13 @@ pub struct RapierColliderHandle(pub ColliderHandle);

/// A component which will be replaced by the specified collider type after the referenced mesh become available.
#[cfg(all(feature = "dim3", feature = "async-collider"))]
#[derive(Component, Debug, Clone)]
pub struct AsyncCollider {
/// Mesh handle to use for collider generation.
pub handle: Handle<Mesh>,
/// Collider type that will be generated.
pub shape: ComputedColliderShape,
}

#[cfg(all(feature = "dim3", feature = "async-collider"))]
impl Default for AsyncCollider {
fn default() -> Self {
Self {
handle: Default::default(),
shape: ComputedColliderShape::TriMesh,
}
}
}
#[derive(Component, Debug, Clone, Default)]
pub struct AsyncCollider(pub ComputedColliderShape);

/// A component which will be replaced the specified collider types on children with meshes after the referenced scene become available.
#[cfg(all(feature = "dim3", feature = "async-collider"))]
#[derive(Component, Debug, Clone)]
pub struct AsyncSceneCollider {
/// Scene handle to use for colliders generation.
pub handle: Handle<Scene>,
/// Collider type for each scene mesh not included in [`named_shapes`]. If [`None`], then all
/// shapes will be skipped for processing except [`named_shapes`].
pub shape: Option<ComputedColliderShape>,
Expand All @@ -51,11 +34,22 @@ pub struct AsyncSceneCollider {
pub named_shapes: HashMap<String, Option<ComputedColliderShape>>,
}

#[cfg(all(feature = "dim3", feature = "async-collider"))]
impl Default for AsyncSceneCollider {
fn default() -> Self {
Self {
shape: Some(ComputedColliderShape::TriMesh),
named_shapes: Default::default(),
}
}
}

/// Shape type based on a Bevy mesh asset.
#[cfg(all(feature = "dim3", feature = "async-collider"))]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub enum ComputedColliderShape {
/// Triangle-mesh.
#[default]
TriMesh,
/// Convex decomposition.
ConvexDecomposition(VHACDParameters),
Expand Down
64 changes: 25 additions & 39 deletions src/plugin/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ use rapier::prelude::*;
use std::collections::HashMap;

#[cfg(all(feature = "dim3", feature = "async-collider"))]
use crate::prelude::{AsyncCollider, AsyncSceneCollider};
use {
crate::prelude::{AsyncCollider, AsyncSceneCollider},
bevy::scene::SceneInstance,
};

use crate::control::CharacterCollision;
#[cfg(feature = "dim2")]
Expand Down Expand Up @@ -680,11 +683,11 @@ pub fn init_async_colliders() {}
pub fn init_async_colliders(
mut commands: Commands,
meshes: Res<Assets<Mesh>>,
async_colliders: Query<(Entity, &AsyncCollider)>,
async_colliders: Query<(Entity, &Handle<Mesh>, &AsyncCollider)>,
) {
for (entity, async_collider) in async_colliders.iter() {
if let Some(mesh) = meshes.get(&async_collider.handle) {
match Collider::from_bevy_mesh(mesh, &async_collider.shape) {
for (entity, mesh_handle, async_collider) in async_colliders.iter() {
if let Some(mesh) = meshes.get(mesh_handle) {
match Collider::from_bevy_mesh(mesh, &async_collider.0) {
Some(collider) => {
commands
.entity(entity)
Expand All @@ -703,15 +706,15 @@ pub fn init_async_colliders(
pub fn init_async_scene_colliders(
mut commands: Commands,
meshes: Res<Assets<Mesh>>,
scenes: Res<Assets<Scene>>,
async_colliders: Query<(Entity, &AsyncSceneCollider)>,
scene_spawner: Res<SceneSpawner>,
async_colliders: Query<(Entity, &SceneInstance, &AsyncSceneCollider)>,
children: Query<&Children>,
mesh_handles: Query<(&Name, &Handle<Mesh>)>,
) {
for (entity, async_collider) in async_colliders.iter() {
if scenes.get(&async_collider.handle).is_some() {
traverse_descendants(entity, &children, &mut |child| {
if let Ok((name, handle)) = mesh_handles.get(child) {
for (scene_entity, scene_instance, async_collider) in async_colliders.iter() {
if scene_spawner.instance_is_ready(**scene_instance) {
for child_entity in children.iter_descendants(scene_entity) {
if let Ok((name, handle)) = mesh_handles.get(child_entity) {
let shape = async_collider
.named_shapes
.get(name.as_str())
Expand All @@ -720,7 +723,7 @@ pub fn init_async_scene_colliders(
let mesh = meshes.get(handle).unwrap(); // NOTE: Mesh is already loaded
match Collider::from_bevy_mesh(mesh, shape) {
Some(collider) => {
commands.entity(child).insert(collider);
commands.entity(child_entity).insert(collider);
}
None => error!(
"Unable to generate collider from mesh {:?} with name {}",
Expand All @@ -729,20 +732,9 @@ pub fn init_async_scene_colliders(
}
}
}
});
}

commands.entity(entity).remove::<AsyncSceneCollider>();
}
}
}

/// Iterates over all descendants of the `entity` and applies `f`.
#[cfg(all(feature = "dim3", feature = "async-collider"))]
fn traverse_descendants(entity: Entity, children: &Query<&Children>, f: &mut impl FnMut(Entity)) {
if let Ok(entity_children) = children.get(entity) {
for child in entity_children.iter().copied() {
f(child);
traverse_descendants(child, children, f);
commands.entity(scene_entity).remove::<AsyncSceneCollider>();
}
}
}
Expand Down Expand Up @@ -1428,8 +1420,6 @@ mod tests {

use super::*;
use crate::plugin::{NoUserData, RapierPhysicsPlugin};
#[cfg(all(feature = "dim3", feature = "async-collider"))]
use crate::prelude::ComputedColliderShape;

#[test]
fn colliding_entities_updates() {
Expand Down Expand Up @@ -1527,13 +1517,7 @@ mod tests {
let mut meshes = app.world.resource_mut::<Assets<Mesh>>();
let cube = meshes.add(Cube::default().into());

let entity = app
.world
.spawn(AsyncCollider {
handle: cube,
shape: ComputedColliderShape::TriMesh,
})
.id();
let entity = app.world.spawn((cube, AsyncCollider::default())).id();

app.update();

Expand Down Expand Up @@ -1568,11 +1552,13 @@ mod tests {
named_shapes.insert("Capsule".to_string(), None);
let parent = app
.world
.spawn(AsyncSceneCollider {
handle: scene,
shape: Some(ComputedColliderShape::TriMesh),
named_shapes,
})
.spawn((
scene,
AsyncSceneCollider {
named_shapes,
..Default::default()
},
))
.push_children(&[cube, capsule])
.id();

Expand Down

0 comments on commit 0df92f3

Please sign in to comment.