From b3e5bb25f5c100f2ac682c953c56a697196850b6 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 5 Jul 2023 17:24:15 -0700 Subject: [PATCH 01/20] ColliderParent component --- src/geometry/collider.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/plugin/plugin.rs | 5 +++++ 2 files changed, 43 insertions(+) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index a7dd2384..f019ae85 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -5,7 +5,12 @@ use {crate::geometry::VHACDParameters, bevy::utils::HashMap}; use bevy::prelude::*; +use bevy::ecs::{ + entity::{EntityMap, MapEntities, MapEntitiesError}, + reflect::ReflectMapEntities, +}; use bevy::utils::HashSet; + use rapier::geometry::Shape; use rapier::prelude::{ColliderHandle, InteractionGroups, SharedShape}; @@ -503,6 +508,39 @@ impl CollidingEntities { #[reflect(Component, PartialEq)] pub struct ColliderDisabled; +/// Rigid body parent of the collider. +#[derive(Component, Debug, Eq, PartialEq, Reflect)] +#[reflect(Component, MapEntities, PartialEq)] +pub struct ColliderParent(pub(crate) Entity); + +impl ColliderParent { + /// Gets the [`Entity`] ID of the rigid-body parent of the collider. + pub fn get(&self) -> Entity { + self.0 + } +} + +impl FromWorld for ColliderParent { + fn from_world(_world: &mut World) -> Self { + Self(Entity::PLACEHOLDER) + } +} + +impl MapEntities for ColliderParent { + fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> { + self.0 = entity_map.get(self.0)?; + Ok(()) + } +} + +impl std::ops::Deref for ColliderParent { + type Target = Entity; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// We restrict the scaling increment to 1.0e-4, to avoid numerical jitter /// due to the extraction of scaling factor from the GlobalTransform matrix. pub fn get_snapped_scale(scale: Vect) -> Vect { diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index fc5aa295..a702b197 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -172,6 +172,10 @@ where .register_type::() .register_type::() .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() @@ -185,6 +189,7 @@ where .register_type::() .register_type::() .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::(); From b7853790dc4ad389b633c0af0f167e2111a36f2a Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 5 Jul 2023 17:31:20 -0700 Subject: [PATCH 02/20] Initial parent setup --- src/plugin/systems.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index aadbf662..040af18e 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -16,7 +16,7 @@ use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode}; use crate::plugin::{RapierConfiguration, RapierContext}; use crate::prelude::{ BevyPhysicsHooks, BevyPhysicsHooksAdapter, CollidingEntities, KinematicCharacterController, - KinematicCharacterControllerOutput, MassModifiedEvent, RigidBodyDisabled, + KinematicCharacterControllerOutput, MassModifiedEvent, RigidBodyDisabled, Vect, ColliderParent, }; use crate::utils; use bevy::ecs::system::{StaticSystemParam, SystemParamItem}; @@ -935,6 +935,8 @@ pub fn init_colliders( context .colliders .insert_with_parent(builder, body_handle, &mut context.bodies); + commands.entity(entity).insert(ColliderParent(body_entity)); + if let Ok(mut mprops) = rigid_body_mprops.get_mut(body_entity) { // Inserting the collider changed the rigid-body’s mass properties. // Read them back from the engine. @@ -956,6 +958,7 @@ pub fn init_colliders( }; commands.entity(entity).insert(RapierColliderHandle(handle)); + context.entity2collider.insert(entity, handle); } } From 8dcd6f79aa566302acfa3b1d2b80773ec40a63c4 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 5 Jul 2023 19:06:17 -0700 Subject: [PATCH 03/20] Add, update, remove collider parent based on hierarchy changes --- src/geometry/collider.rs | 7 +- src/plugin/systems.rs | 225 ++++++++++++++++++++++++++++++++++----- 2 files changed, 200 insertions(+), 32 deletions(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index f019ae85..faf0e866 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -6,7 +6,7 @@ use {crate::geometry::VHACDParameters, bevy::utils::HashMap}; use bevy::prelude::*; use bevy::ecs::{ - entity::{EntityMap, MapEntities, MapEntitiesError}, + entity::{EntityMapper, MapEntities}, reflect::ReflectMapEntities, }; use bevy::utils::HashSet; @@ -527,9 +527,8 @@ impl FromWorld for ColliderParent { } impl MapEntities for ColliderParent { - fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> { - self.0 = entity_map.get(self.0)?; - Ok(()) + fn map_entities(&mut self, entity_map: &mut EntityMapper) { + self.0 = entity_map.get_or_reserve(self.0); } } diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index 040af18e..9dc7e5cf 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -15,11 +15,13 @@ use crate::pipeline::{CollisionEvent, ContactForceEvent}; use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode}; use crate::plugin::{RapierConfiguration, RapierContext}; use crate::prelude::{ - BevyPhysicsHooks, BevyPhysicsHooksAdapter, CollidingEntities, KinematicCharacterController, - KinematicCharacterControllerOutput, MassModifiedEvent, RigidBodyDisabled, Vect, ColliderParent, + BevyPhysicsHooks, BevyPhysicsHooksAdapter, ColliderParent, CollidingEntities, + KinematicCharacterController, KinematicCharacterControllerOutput, MassModifiedEvent, + RigidBodyDisabled, }; use crate::utils; -use bevy::ecs::system::{StaticSystemParam, SystemParamItem}; +use bevy::ecs::system::{StaticSystemParam, SystemParamItem, SystemParam}; +use bevy::hierarchy::HierarchyEvent; use bevy::prelude::*; use rapier::prelude::*; use std::collections::HashMap; @@ -117,47 +119,191 @@ pub fn apply_scale( } } -/// System responsible for applying changes the user made to a collider-related component. -pub fn apply_collider_user_changes( - mut context: ResMut, - config: Res, - (changed_collider_transforms, parent_query, transform_query): ( - Query< - (Entity, &RapierColliderHandle, &GlobalTransform), - (Without, Changed), - >, - Query<&Parent>, - Query<&Transform>, - ), - - changed_shapes: Query<(&RapierColliderHandle, &Collider), Changed>, - changed_active_events: Query<(&RapierColliderHandle, &ActiveEvents), Changed>, - changed_active_hooks: Query<(&RapierColliderHandle, &ActiveHooks), Changed>, +/// System responsible for detecting changes in the hierarchy that would +/// affect the collider's parent rigid body. +pub fn collect_collider_hierarchy_changes( + mut commands: Commands, + + mut hierarchy_events: EventReader, + parents: Query<&Parent>, + childrens: Query<&Children>, + rigid_bodies: Query<&RigidBody>, + colliders: Query<&Collider>, + mut collider_parents: Query<&mut ColliderParent>, +) { + let child_colliders = |entity: Entity| -> Vec { + let mut found = Vec::new(); + let mut possibilities = vec![entity]; + while let Some(entity) = possibilities.pop() { + if rigid_bodies.contains(entity) { + continue; + } + + if colliders.contains(entity) { + found.push(entity); + } + + if let Ok(children) = childrens.get(entity) { + possibilities.extend(children.iter()); + } else { + continue; + }; + } + + found + }; + + let parent_rigid_body = |mut entity: Entity| -> Option { + loop { + if rigid_bodies.contains(entity) { + return Some(entity); + } + + if let Ok(parent) = parents.get(entity) { + entity = parent.get(); + } else { + return None; + } + } + }; + + for event in hierarchy_events.iter() { + match event { + HierarchyEvent::ChildAdded { child, .. } | HierarchyEvent::ChildMoved { child, .. } => { + let colliders = child_colliders(*child); + let Some(rigid_body) = parent_rigid_body(*child) else { continue }; + + for collider in colliders { + let new_collider_parent = ColliderParent(rigid_body); + if let Ok(mut collider_parent) = collider_parents.get_mut(collider) { + *collider_parent = new_collider_parent; + } else { + commands.entity(collider).insert(new_collider_parent); + } + } + } + HierarchyEvent::ChildRemoved { child, .. } => { + let colliders = child_colliders(*child); + for collider in colliders { + if collider_parents.contains(collider) { + commands.entity(collider).remove::(); + } + } + } + } + } +} + +/// Collection of change queries for colliders. +/// +/// Mainly used because bevy doesn't impl more than +/// 16 parameters for a system. +#[derive(SystemParam)] +pub struct ColliderChanges<'w, 's> { + changed_collider_transforms: Query< + 'w, + 's, + ( + Entity, + &'static RapierColliderHandle, + &'static GlobalTransform, + ), + (Without, Changed), + >, + changed_collider_parents: Query< + 'w, + 's, + (&'static RapierColliderHandle, &'static ColliderParent), + Changed, + >, + changed_shapes: + Query<'w, 's, (&'static RapierColliderHandle, &'static Collider), Changed>, + changed_active_events: Query< + 'w, + 's, + (&'static RapierColliderHandle, &'static ActiveEvents), + Changed, + >, + changed_active_hooks: + Query<'w, 's, (&'static RapierColliderHandle, &'static ActiveHooks), Changed>, changed_active_collision_types: Query< - (&RapierColliderHandle, &ActiveCollisionTypes), + 'w, + 's, + (&'static RapierColliderHandle, &'static ActiveCollisionTypes), Changed, >, - changed_friction: Query<(&RapierColliderHandle, &Friction), Changed>, - changed_restitution: Query<(&RapierColliderHandle, &Restitution), Changed>, + changed_friction: + Query<'w, 's, (&'static RapierColliderHandle, &'static Friction), Changed>, + changed_restitution: + Query<'w, 's, (&'static RapierColliderHandle, &'static Restitution), Changed>, changed_collision_groups: Query< - (&RapierColliderHandle, &CollisionGroups), + 'w, + 's, + (&'static RapierColliderHandle, &'static CollisionGroups), Changed, >, - changed_solver_groups: Query<(&RapierColliderHandle, &SolverGroups), Changed>, - changed_sensors: Query<(&RapierColliderHandle, &Sensor), Changed>, - changed_disabled: Query<(&RapierColliderHandle, &ColliderDisabled), Changed>, + changed_solver_groups: Query< + 'w, + 's, + (&'static RapierColliderHandle, &'static SolverGroups), + Changed, + >, + changed_sensors: + Query<'w, 's, (&'static RapierColliderHandle, &'static Sensor), Changed>, + changed_disabled: Query< + 'w, + 's, + (&'static RapierColliderHandle, &'static ColliderDisabled), + Changed, + >, changed_contact_force_threshold: Query< - (&RapierColliderHandle, &ContactForceEventThreshold), + 'w, + 's, + ( + &'static RapierColliderHandle, + &'static ContactForceEventThreshold, + ), Changed, >, changed_collider_mass_props: Query< - (&RapierColliderHandle, &ColliderMassProperties), + 'w, + 's, + ( + &'static RapierColliderHandle, + &'static ColliderMassProperties, + ), Changed, >, +} + +/// System responsible for applying changes the user made to a collider-related component. +pub fn apply_collider_user_changes( + mut context: ResMut, + config: Res, + + collider_changes: ColliderChanges, + parent_query: Query<&Parent>, + transform_query: Query<&Transform>, mut mass_modified: EventWriter, ) { let scale = context.physics_scale; + let ColliderChanges { + changed_collider_transforms, + changed_collider_parents, + changed_shapes, + changed_active_events, + changed_active_hooks, + changed_active_collision_types, + changed_friction, + changed_restitution, + changed_collision_groups, + changed_solver_groups, + changed_sensors, + changed_disabled, + changed_contact_force_threshold, + changed_collider_mass_props, + } = collider_changes; for (entity, handle, transform) in changed_collider_transforms.iter() { if context.collider_parent(entity).is_some() { @@ -175,6 +321,17 @@ pub fn apply_collider_user_changes( } } + for (handle, collider_parent) in changed_collider_parents.iter() { + if let Some(body_handle) = context.entity2body.get(&collider_parent.0).copied() { + let RapierContext { + ref mut colliders, + ref mut bodies, + .. + } = *context; + colliders.set_parent(handle.0, Some(body_handle), bodies); + } + } + for (handle, shape) in changed_shapes.iter() { if let Some(co) = context.colliders.get_mut(handle.0) { let mut scaled_shape = shape.clone(); @@ -1183,6 +1340,7 @@ pub fn sync_removals( mut removed_sensors: RemovedComponents, mut removed_rigid_body_disabled: RemovedComponents, mut removed_colliders_disabled: RemovedComponents, + mut removed_collider_parents: RemovedComponents, mut mass_modified: EventWriter, ) { @@ -1311,6 +1469,17 @@ pub fn sync_removals( } } + for entity in removed_collider_parents.iter() { + if let Some(handle) = context.entity2collider.get(&entity) { + let RapierContext { + ref mut colliders, + ref mut bodies, + .. + } = *context; + colliders.set_parent(*handle, None, bodies); + } + } + // TODO: what about removing forces? } From 11d69d5f15d51e31e4bddff9cb9cfebb3e0224b9 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Sat, 8 Jul 2023 13:47:25 -0700 Subject: [PATCH 04/20] Testing compounds vs child colliders --- bevy_rapier3d/Cargo.toml | 50 ++++++---- bevy_rapier3d/examples/boxes3.rs | 153 ++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 21 deletions(-) diff --git a/bevy_rapier3d/Cargo.toml b/bevy_rapier3d/Cargo.toml index 1082f501..efcba93f 100644 --- a/bevy_rapier3d/Cargo.toml +++ b/bevy_rapier3d/Cargo.toml @@ -7,7 +7,7 @@ documentation = "http://docs.rs/bevy_rapier3d" homepage = "http://rapier.rs" repository = "https://github.com/dimforge/bevy_rapier" readme = "../README.md" -keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] +keywords = ["physics", "dynamics", "rigid", "real-time", "joints"] license = "Apache-2.0" edition = "2021" @@ -15,38 +15,50 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] path = "../src/lib.rs" -required-features = [ "dim3" ] +required-features = ["dim3"] [features] -default = [ "dim3", "async-collider", "debug-render-3d" ] +default = ["dim3", "async-collider", "debug-render-3d"] dim3 = [] -debug-render = [ "debug-render-3d" ] -debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] -debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] -parallel = [ "rapier3d/parallel" ] -simd-stable = [ "rapier3d/simd-stable" ] -simd-nightly = [ "rapier3d/simd-nightly" ] -wasm-bindgen = [ "rapier3d/wasm-bindgen" ] -serde-serialize = [ "rapier3d/serde-serialize", "bevy/serialize", "serde" ] -enhanced-determinism = [ "rapier3d/enhanced-determinism" ] -headless = [ ] -async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] +debug-render = ["debug-render-3d"] +debug-render-2d = [ + "bevy/bevy_core_pipeline", + "bevy/bevy_sprite", + "bevy/bevy_gizmos", + "rapier3d/debug-render", + "bevy/bevy_asset", +] +debug-render-3d = [ + "bevy/bevy_core_pipeline", + "bevy/bevy_pbr", + "bevy/bevy_gizmos", + "rapier3d/debug-render", + "bevy/bevy_asset", +] +parallel = ["rapier3d/parallel"] +simd-stable = ["rapier3d/simd-stable"] +simd-nightly = ["rapier3d/simd-nightly"] +wasm-bindgen = ["rapier3d/wasm-bindgen"] +serde-serialize = ["rapier3d/serde-serialize", "bevy/serialize", "serde"] +enhanced-determinism = ["rapier3d/enhanced-determinism"] +headless = [] +async-collider = ["bevy/bevy_asset", "bevy/bevy_scene"] [dependencies] bevy = { version = "0.11", default-features = false } -nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } +nalgebra = { version = "0.32.3", features = ["convert-glam024"] } # Don't enable the default features because we don't need the ColliderSet/RigidBodySet rapier3d = "0.17.0" bitflags = "1" #bevy_prototype_debug_lines = { version = "0.6", features = ["3d"], optional = true } log = "0.4" -serde = { version = "1", features = [ "derive" ], optional = true} +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] -bevy = { version = "0.11", default-features = false, features = ["x11"]} +bevy = { version = "0.11", default-features = false, features = ["x11"] } approx = "0.5.1" -glam = { version = "0.24", features = [ "approx" ] } +glam = { version = "0.24", features = ["approx"] } [package.metadata.docs.rs] # Enable all the features when building the docs on docs.rs -features = [ "debug-render-3d", "serde-serialize" ] +features = ["debug-render-3d", "serde-serialize"] diff --git a/bevy_rapier3d/examples/boxes3.rs b/bevy_rapier3d/examples/boxes3.rs index 3d510720..b440dc58 100644 --- a/bevy_rapier3d/examples/boxes3.rs +++ b/bevy_rapier3d/examples/boxes3.rs @@ -17,6 +17,155 @@ fn main() { .run(); } +fn setup_compound(mut commands: Commands, mut meshes: ResMut>) { + // Normal compound + // - Scaling works as expected + commands + .spawn(TransformBundle::default()) + .insert(Name::new("Compound")) + .insert(RigidBody::Dynamic) + .insert(ToggleCollider) + .insert(ToggleScale) + .insert(Collider::compound(vec![ + (Vec3::ZERO, Quat::IDENTITY, Collider::cuboid(0.5, 0.5, 0.5)), + ( + Vec3::new(0.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::cuboid(0.5, 0.5, 0.5), + ), + ( + Vec3::new(-1.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::cuboid(0.5, 0.5, 0.5), + ), + ])); + + let mesh = meshes.add(Mesh::from(shape::UVSphere { + radius: 0.2, + ..default() + })); + + commands + .spawn(PbrBundle { + mesh: mesh.clone(), + ..default() + }) + .insert(TransformBundle::from_transform(Transform { + translation: Vec3::new(-4.0, 0.0, 0.0), + ..default() + })) + .insert(Name::new("Standalone collider")) + .insert(ToggleScale) + .insert(ToggleCollider) + .insert(Collider::cuboid(0.5, 0.5, 0.5)); + + commands + .spawn(PbrBundle { + mesh: mesh.clone(), + ..default() + }) + .insert(TransformBundle::from_transform(Transform { + translation: Vec3::new(4.0, 0.0, 0.0), + ..default() + })) + .insert(Name::new("Compound via children")) + .insert(RigidBody::Dynamic) + .insert(ToggleScale) + //.insert(ToggleCollider) + .insert(Collider::cuboid(0.5, 0.5, 0.5)) + .with_children(|children| { + children + .spawn(PbrBundle { + mesh: mesh.clone(), + ..default() + }) + .insert(TransformBundle::from_transform(Transform { + translation: Vec3::new(0.0, 1.0, 0.0), + ..default() + })) + .insert(Name::new("Child collider")) + .insert(Collider::cuboid(0.5, 0.5, 0.5)); + + children + .spawn(PbrBundle { + mesh: mesh.clone(), + ..default() + }) + .insert(TransformBundle::from_transform(Transform { + translation: Vec3::new(-1.0, 1.0, 0.0), + ..default() + })) + .insert(Name::new("Child collider")) + .insert(Collider::cuboid(0.5, 0.5, 0.5)); + }); +} + +#[derive(Component)] +pub struct ToggleCollider; +#[derive(Component)] +pub struct ToggleScale; + +fn toggle_compound( + keycode: Res>, + mut commands: Commands, + mut collider: Query<(Entity, &mut Collider), With>, + mut scale: Query<(Entity, &mut Transform), With>, +) { + let new_collider = if keycode.just_pressed(KeyCode::Key1) { + Some(Collider::compound(vec![ + (Vec3::ZERO, Quat::IDENTITY, Collider::cuboid(0.5, 0.5, 0.5)), + ( + Vec3::new(0.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::cuboid(0.5, 0.5, 0.5), + ), + ( + Vec3::new(-1.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::cuboid(0.5, 0.5, 0.5), + ), + ])) + } else if keycode.just_pressed(KeyCode::Key2) { + Some(Collider::compound(vec![ + (Vec3::ZERO, Quat::IDENTITY, Collider::ball(0.5)), + ( + Vec3::new(0.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::ball(0.5), + ), + ( + Vec3::new(-1.0, 1.0, 0.0), + Quat::IDENTITY, + Collider::ball(0.5), + ), + ])) + } else { + None + }; + + let new_scale_ratio = if keycode.just_pressed(KeyCode::T) { + Some(1.1) + } else if keycode.just_pressed(KeyCode::R) { + Some(0.9) + } else { + None + }; + + if let Some(new_scale_ratio) = new_scale_ratio { + for (entity, mut transform) in &mut scale { + transform.scale *= new_scale_ratio; + transform.scale = transform.scale.max(Vec3::new(0.1, 0.1, 0.1)); + transform.scale = transform.scale.min(Vec3::new(5.0, 5.0, 5.0)); + } + } + + if let Some(new_collider) = new_collider { + for (entity, mut collider) in &mut collider { + *collider = new_collider.clone(); + } + } +} + fn setup_graphics(mut commands: Commands) { commands.spawn(Camera3dBundle { transform: Transform::from_xyz(-30.0, 30.0, 100.0) @@ -40,7 +189,7 @@ pub fn setup_physics(mut commands: Commands) { /* * Create the cubes */ - let num = 8; + let num = 0; let rad = 1.0; let shift = rad * 2.0 + rad; @@ -56,7 +205,7 @@ pub fn setup_physics(mut commands: Commands) { Color::hsl(260.0, 1.0, 0.7), ]; - for j in 0usize..20 { + for j in 0usize..num { for i in 0..num { for k in 0usize..num { let x = i as f32 * shift - centerx + offset; From 408f75f85d40f62a419e73c5e544589e51efc566 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Sun, 23 Jul 2023 19:46:08 -0700 Subject: [PATCH 05/20] Re-add system for updating parent --- src/geometry/collider.rs | 2 +- src/plugin/plugin.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index faf0e866..223e0a26 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -510,7 +510,7 @@ pub struct ColliderDisabled; /// Rigid body parent of the collider. #[derive(Component, Debug, Eq, PartialEq, Reflect)] -#[reflect(Component, MapEntities, PartialEq)] +#[reflect(Component, PartialEq)] pub struct ColliderParent(pub(crate) Entity); impl ColliderParent { diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index a702b197..66acb636 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -95,6 +95,7 @@ where systems::init_colliders, systems::init_joints, systems::sync_removals, + systems::collect_collider_hierarchy_changes, // Run this here so the folowwing systems do not have a 1 frame delay. apply_deferred, systems::apply_scale, From dbb7d6a4ef5154629cef92d7169bf18191f6d477 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Tue, 25 Jul 2023 15:14:31 -0700 Subject: [PATCH 06/20] Clean up warnings --- src/geometry/collider.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 223e0a26..6b164fb0 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -4,7 +4,6 @@ use std::fmt; use {crate::geometry::VHACDParameters, bevy::utils::HashMap}; use bevy::prelude::*; - use bevy::ecs::{ entity::{EntityMapper, MapEntities}, reflect::ReflectMapEntities, From 63f59246341beb085c4e1be29f4c8a160cf9a383 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Tue, 25 Jul 2023 15:17:14 -0700 Subject: [PATCH 07/20] Re-add MapEntities to ColliderParent --- src/geometry/collider.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 6b164fb0..ed18449b 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -3,12 +3,14 @@ use std::fmt; #[cfg(all(feature = "dim3", feature = "async-collider"))] use {crate::geometry::VHACDParameters, bevy::utils::HashMap}; -use bevy::prelude::*; -use bevy::ecs::{ - entity::{EntityMapper, MapEntities}, - reflect::ReflectMapEntities, +use bevy::{ + ecs::{ + entity::{MapEntities, EntityMapper}, + reflect::ReflectMapEntities, + }, + prelude::*, + utils::HashSet, }; -use bevy::utils::HashSet; use rapier::geometry::Shape; use rapier::prelude::{ColliderHandle, InteractionGroups, SharedShape}; @@ -509,7 +511,7 @@ pub struct ColliderDisabled; /// Rigid body parent of the collider. #[derive(Component, Debug, Eq, PartialEq, Reflect)] -#[reflect(Component, PartialEq)] +#[reflect(Component, MapEntities, PartialEq)] pub struct ColliderParent(pub(crate) Entity); impl ColliderParent { @@ -526,8 +528,8 @@ impl FromWorld for ColliderParent { } impl MapEntities for ColliderParent { - fn map_entities(&mut self, entity_map: &mut EntityMapper) { - self.0 = entity_map.get_or_reserve(self.0); + fn map_entities(&mut self, entity_mapper: &mut EntityMapper) { + self.0 = entity_mapper.get_or_reserve(self.0); } } From 941134adf314520e030ed62e52e4d00fe73881d5 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Tue, 25 Jul 2023 15:26:38 -0700 Subject: [PATCH 08/20] Revert cargo toml changes --- bevy_rapier3d/Cargo.toml | 50 +++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/bevy_rapier3d/Cargo.toml b/bevy_rapier3d/Cargo.toml index efcba93f..1082f501 100644 --- a/bevy_rapier3d/Cargo.toml +++ b/bevy_rapier3d/Cargo.toml @@ -7,7 +7,7 @@ documentation = "http://docs.rs/bevy_rapier3d" homepage = "http://rapier.rs" repository = "https://github.com/dimforge/bevy_rapier" readme = "../README.md" -keywords = ["physics", "dynamics", "rigid", "real-time", "joints"] +keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] license = "Apache-2.0" edition = "2021" @@ -15,50 +15,38 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] path = "../src/lib.rs" -required-features = ["dim3"] +required-features = [ "dim3" ] [features] -default = ["dim3", "async-collider", "debug-render-3d"] +default = [ "dim3", "async-collider", "debug-render-3d" ] dim3 = [] -debug-render = ["debug-render-3d"] -debug-render-2d = [ - "bevy/bevy_core_pipeline", - "bevy/bevy_sprite", - "bevy/bevy_gizmos", - "rapier3d/debug-render", - "bevy/bevy_asset", -] -debug-render-3d = [ - "bevy/bevy_core_pipeline", - "bevy/bevy_pbr", - "bevy/bevy_gizmos", - "rapier3d/debug-render", - "bevy/bevy_asset", -] -parallel = ["rapier3d/parallel"] -simd-stable = ["rapier3d/simd-stable"] -simd-nightly = ["rapier3d/simd-nightly"] -wasm-bindgen = ["rapier3d/wasm-bindgen"] -serde-serialize = ["rapier3d/serde-serialize", "bevy/serialize", "serde"] -enhanced-determinism = ["rapier3d/enhanced-determinism"] -headless = [] -async-collider = ["bevy/bevy_asset", "bevy/bevy_scene"] +debug-render = [ "debug-render-3d" ] +debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] +debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] +parallel = [ "rapier3d/parallel" ] +simd-stable = [ "rapier3d/simd-stable" ] +simd-nightly = [ "rapier3d/simd-nightly" ] +wasm-bindgen = [ "rapier3d/wasm-bindgen" ] +serde-serialize = [ "rapier3d/serde-serialize", "bevy/serialize", "serde" ] +enhanced-determinism = [ "rapier3d/enhanced-determinism" ] +headless = [ ] +async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] [dependencies] bevy = { version = "0.11", default-features = false } -nalgebra = { version = "0.32.3", features = ["convert-glam024"] } +nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } # Don't enable the default features because we don't need the ColliderSet/RigidBodySet rapier3d = "0.17.0" bitflags = "1" #bevy_prototype_debug_lines = { version = "0.6", features = ["3d"], optional = true } log = "0.4" -serde = { version = "1", features = ["derive"], optional = true } +serde = { version = "1", features = [ "derive" ], optional = true} [dev-dependencies] -bevy = { version = "0.11", default-features = false, features = ["x11"] } +bevy = { version = "0.11", default-features = false, features = ["x11"]} approx = "0.5.1" -glam = { version = "0.24", features = ["approx"] } +glam = { version = "0.24", features = [ "approx" ] } [package.metadata.docs.rs] # Enable all the features when building the docs on docs.rs -features = ["debug-render-3d", "serde-serialize"] +features = [ "debug-render-3d", "serde-serialize" ] From ddec5f21ddb2729502ea5929995ea41cba354955 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Tue, 25 Jul 2023 21:55:30 -0700 Subject: [PATCH 09/20] Revert boxes3 example --- bevy_rapier3d/examples/boxes3.rs | 153 +------------------------------ 1 file changed, 2 insertions(+), 151 deletions(-) diff --git a/bevy_rapier3d/examples/boxes3.rs b/bevy_rapier3d/examples/boxes3.rs index b440dc58..3d510720 100644 --- a/bevy_rapier3d/examples/boxes3.rs +++ b/bevy_rapier3d/examples/boxes3.rs @@ -17,155 +17,6 @@ fn main() { .run(); } -fn setup_compound(mut commands: Commands, mut meshes: ResMut>) { - // Normal compound - // - Scaling works as expected - commands - .spawn(TransformBundle::default()) - .insert(Name::new("Compound")) - .insert(RigidBody::Dynamic) - .insert(ToggleCollider) - .insert(ToggleScale) - .insert(Collider::compound(vec![ - (Vec3::ZERO, Quat::IDENTITY, Collider::cuboid(0.5, 0.5, 0.5)), - ( - Vec3::new(0.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::cuboid(0.5, 0.5, 0.5), - ), - ( - Vec3::new(-1.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::cuboid(0.5, 0.5, 0.5), - ), - ])); - - let mesh = meshes.add(Mesh::from(shape::UVSphere { - radius: 0.2, - ..default() - })); - - commands - .spawn(PbrBundle { - mesh: mesh.clone(), - ..default() - }) - .insert(TransformBundle::from_transform(Transform { - translation: Vec3::new(-4.0, 0.0, 0.0), - ..default() - })) - .insert(Name::new("Standalone collider")) - .insert(ToggleScale) - .insert(ToggleCollider) - .insert(Collider::cuboid(0.5, 0.5, 0.5)); - - commands - .spawn(PbrBundle { - mesh: mesh.clone(), - ..default() - }) - .insert(TransformBundle::from_transform(Transform { - translation: Vec3::new(4.0, 0.0, 0.0), - ..default() - })) - .insert(Name::new("Compound via children")) - .insert(RigidBody::Dynamic) - .insert(ToggleScale) - //.insert(ToggleCollider) - .insert(Collider::cuboid(0.5, 0.5, 0.5)) - .with_children(|children| { - children - .spawn(PbrBundle { - mesh: mesh.clone(), - ..default() - }) - .insert(TransformBundle::from_transform(Transform { - translation: Vec3::new(0.0, 1.0, 0.0), - ..default() - })) - .insert(Name::new("Child collider")) - .insert(Collider::cuboid(0.5, 0.5, 0.5)); - - children - .spawn(PbrBundle { - mesh: mesh.clone(), - ..default() - }) - .insert(TransformBundle::from_transform(Transform { - translation: Vec3::new(-1.0, 1.0, 0.0), - ..default() - })) - .insert(Name::new("Child collider")) - .insert(Collider::cuboid(0.5, 0.5, 0.5)); - }); -} - -#[derive(Component)] -pub struct ToggleCollider; -#[derive(Component)] -pub struct ToggleScale; - -fn toggle_compound( - keycode: Res>, - mut commands: Commands, - mut collider: Query<(Entity, &mut Collider), With>, - mut scale: Query<(Entity, &mut Transform), With>, -) { - let new_collider = if keycode.just_pressed(KeyCode::Key1) { - Some(Collider::compound(vec![ - (Vec3::ZERO, Quat::IDENTITY, Collider::cuboid(0.5, 0.5, 0.5)), - ( - Vec3::new(0.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::cuboid(0.5, 0.5, 0.5), - ), - ( - Vec3::new(-1.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::cuboid(0.5, 0.5, 0.5), - ), - ])) - } else if keycode.just_pressed(KeyCode::Key2) { - Some(Collider::compound(vec![ - (Vec3::ZERO, Quat::IDENTITY, Collider::ball(0.5)), - ( - Vec3::new(0.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::ball(0.5), - ), - ( - Vec3::new(-1.0, 1.0, 0.0), - Quat::IDENTITY, - Collider::ball(0.5), - ), - ])) - } else { - None - }; - - let new_scale_ratio = if keycode.just_pressed(KeyCode::T) { - Some(1.1) - } else if keycode.just_pressed(KeyCode::R) { - Some(0.9) - } else { - None - }; - - if let Some(new_scale_ratio) = new_scale_ratio { - for (entity, mut transform) in &mut scale { - transform.scale *= new_scale_ratio; - transform.scale = transform.scale.max(Vec3::new(0.1, 0.1, 0.1)); - transform.scale = transform.scale.min(Vec3::new(5.0, 5.0, 5.0)); - } - } - - if let Some(new_collider) = new_collider { - for (entity, mut collider) in &mut collider { - *collider = new_collider.clone(); - } - } -} - fn setup_graphics(mut commands: Commands) { commands.spawn(Camera3dBundle { transform: Transform::from_xyz(-30.0, 30.0, 100.0) @@ -189,7 +40,7 @@ pub fn setup_physics(mut commands: Commands) { /* * Create the cubes */ - let num = 0; + let num = 8; let rad = 1.0; let shift = rad * 2.0 + rad; @@ -205,7 +56,7 @@ pub fn setup_physics(mut commands: Commands) { Color::hsl(260.0, 1.0, 0.7), ]; - for j in 0usize..num { + for j in 0usize..20 { for i in 0..num { for k in 0usize..num { let x = i as f32 * shift - centerx + offset; From e8b8a2a14bb132c07da51f56746f883579bbea2a Mon Sep 17 00:00:00 2001 From: Aceeri Date: Tue, 25 Jul 2023 21:57:42 -0700 Subject: [PATCH 10/20] Formatting --- src/geometry/collider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index ed18449b..c88b5bcd 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -5,7 +5,7 @@ use {crate::geometry::VHACDParameters, bevy::utils::HashMap}; use bevy::{ ecs::{ - entity::{MapEntities, EntityMapper}, + entity::{EntityMapper, MapEntities}, reflect::ReflectMapEntities, }, prelude::*, From 2e075968559d57b7989b9b2c7d89ea4abb8e5834 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 26 Jul 2023 03:35:52 -0700 Subject: [PATCH 11/20] Collider parent unit tests --- bevy_rapier3d/examples/ray_casting3.rs | 8 ++- src/geometry/collider_impl.rs | 4 +- src/plugin/systems.rs | 89 +++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/bevy_rapier3d/examples/ray_casting3.rs b/bevy_rapier3d/examples/ray_casting3.rs index 50a27a4c..c6068117 100644 --- a/bevy_rapier3d/examples/ray_casting3.rs +++ b/bevy_rapier3d/examples/ray_casting3.rs @@ -80,12 +80,16 @@ fn cast_ray( ) { let window = windows.single(); - let Some(cursor_position) = window.cursor_position() else { return; }; + let Some(cursor_position) = window.cursor_position() else { + return; + }; // We will color in read the colliders hovered by the mouse. for (camera, camera_transform) in &cameras { // First, compute a ray from the mouse position. - let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else { return; }; + let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else { + return; + }; // Then cast the ray. let hit = rapier_context.cast_ray( diff --git a/src/geometry/collider_impl.rs b/src/geometry/collider_impl.rs index f526ecd8..212a77f7 100644 --- a/src/geometry/collider_impl.rs +++ b/src/geometry/collider_impl.rs @@ -172,7 +172,9 @@ impl Collider { /// Returns `None` if the index buffer or vertex buffer of the mesh are in an incompatible format. #[cfg(all(feature = "dim3", feature = "async-collider"))] pub fn from_bevy_mesh(mesh: &Mesh, collider_shape: &ComputedColliderShape) -> Option { - let Some((vtx, idx)) = extract_mesh_vertices_indices(mesh) else { return None; }; + let Some((vtx, idx)) = extract_mesh_vertices_indices(mesh) else { + return None; + }; match collider_shape { ComputedColliderShape::TriMesh => Some( SharedShape::trimesh_with_flags(vtx, idx, TriMeshFlags::MERGE_DUPLICATE_VERTICES) diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index 9dc7e5cf..d74a4fec 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -171,7 +171,9 @@ pub fn collect_collider_hierarchy_changes( match event { HierarchyEvent::ChildAdded { child, .. } | HierarchyEvent::ChildMoved { child, .. } => { let colliders = child_colliders(*child); - let Some(rigid_body) = parent_rigid_body(*child) else { continue }; + let Some(rigid_body) = parent_rigid_body(*child) else { + continue; + }; for collider in colliders { let new_collider_parent = ColliderParent(rigid_body); @@ -1987,6 +1989,91 @@ mod tests { } } + #[test] + fn collider_parent() { + let mut app = App::new(); + app.add_plugins(( + HeadlessRenderPlugin, + TransformPlugin, + HierarchyPlugin, + TimePlugin, + RapierPhysicsPlugin::::default(), + )); + + fn verify_collider_parent( + ctx: Res, + colliders: Query<(Entity, Option<&ColliderParent>), With>, + parents: Query<&Parent>, + bodies: Query<(), With>, + ) { + for (collider_entity, collider_parent) in &colliders { + let rapier_parent = ctx.collider_parent(collider_entity); + let collider_parent = collider_parent.map(|parent| parent.get()); + + let mut entity = collider_entity; + let mut hierarchal_parent = None; + loop { + if bodies.contains(entity) { + hierarchal_parent = Some(entity); + break; + } + + let Ok(parent) = parents.get(entity) else { break }; + entity = parent.get(); + } + + println!( + "{:?} == {:?} == {:?}", + rapier_parent, collider_parent, hierarchal_parent + ); + assert_eq!(rapier_parent, collider_parent); + assert_eq!(rapier_parent, hierarchal_parent); + } + } + + app.add_systems(Last, verify_collider_parent); + + let parent1 = app + .world + .spawn((TransformBundle::default(), RigidBody::Dynamic)) + .id(); + + let parent2 = app + .world + .spawn((TransformBundle::default(), RigidBody::Dynamic)) + .id(); + + let inbetween = app.world.spawn((TransformBundle::default(),)).id(); + + let child = app + .world + .spawn((TransformBundle::default(), Collider::default())) + .id(); + + // Unnested + app.update(); + app.world.entity_mut(child).set_parent(parent1); + app.update(); + app.world.entity_mut(child).set_parent(parent2); + app.update(); + app.world.entity_mut(child).remove_parent(); + app.update(); + + // Nested + app.world.entity_mut(child).set_parent(inbetween); + app.update(); + app.world.entity_mut(inbetween).set_parent(parent1); + app.update(); + app.world.entity_mut(inbetween).set_parent(parent2); + app.update(); + app.world.entity_mut(inbetween).remove_parent(); + app.update(); + + app.world.entity_mut(inbetween).set_parent(parent1); + app.world.entity_mut(child).remove_parent(); + app.update(); + } + // Allows run tests for systems containing rendering related things without GPU struct HeadlessRenderPlugin; From bf29e18cddf4b8bec0db97471c820dbbf3a6afea Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 26 Jul 2023 03:38:56 -0700 Subject: [PATCH 12/20] Add HierarchyPlugin for transform propagation --- src/plugin/systems.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index d74a4fec..1b25f274 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -1858,6 +1858,7 @@ mod tests { app.add_plugins(( HeadlessRenderPlugin, TransformPlugin, + HierarchyPlugin, TimePlugin, RapierPhysicsPlugin::::default(), )); @@ -1915,6 +1916,7 @@ mod tests { app.add_plugins(( HeadlessRenderPlugin, TransformPlugin, + HierarchyPlugin, TimePlugin, RapierPhysicsPlugin::::default(), )); From bcc503cc769aa147073282646728f985b58c7ad9 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 26 Jul 2023 06:03:47 -0700 Subject: [PATCH 13/20] Some messing around to figure out 1 frame delays --- src/plugin/systems.rs | 58 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index 1b25f274..d607f649 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -168,18 +168,25 @@ pub fn collect_collider_hierarchy_changes( }; for event in hierarchy_events.iter() { + println!("event: {:?}", event); match event { HierarchyEvent::ChildAdded { child, .. } | HierarchyEvent::ChildMoved { child, .. } => { + println!("child add/moved"); let colliders = child_colliders(*child); let Some(rigid_body) = parent_rigid_body(*child) else { + println!("no rigid body parent"); continue; }; + println!("- updating colliders"); for collider in colliders { + println!("- collider: {:?}", collider); let new_collider_parent = ColliderParent(rigid_body); if let Ok(mut collider_parent) = collider_parents.get_mut(collider) { + println!(" - update existing"); *collider_parent = new_collider_parent; } else { + println!(" - insert new"); commands.entity(collider).insert(new_collider_parent); } } @@ -323,14 +330,23 @@ pub fn apply_collider_user_changes( } } - for (handle, collider_parent) in changed_collider_parents.iter() { + + for entity in &test_query { + println!("entity {:?} has a cparent", entity); + } + + println!("checking changes"); + for (handle, collider_parent, collider_entity) in changed_collider_parents.iter() { + println!("changed collider: {:?}", collider_entity); if let Some(body_handle) = context.entity2body.get(&collider_parent.0).copied() { + println!("body parent: {:?}", collider_parent.0); let RapierContext { ref mut colliders, ref mut bodies, .. } = *context; colliders.set_parent(handle.0, Some(body_handle), bodies); + println!("collider parent: {:?}", context.collider_parent(collider_entity)); } } @@ -904,9 +920,10 @@ pub fn step_simulation( #[cfg(all(feature = "dim3", feature = "async-collider"))] pub fn init_async_colliders( mut commands: Commands, - meshes: Res>, + meshes: Option>>, async_colliders: Query<(Entity, &Handle, &AsyncCollider)>, ) { + let Some(meshes) = meshes else { return }; 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) { @@ -927,12 +944,14 @@ pub fn init_async_colliders( #[cfg(all(feature = "dim3", feature = "async-collider"))] pub fn init_async_scene_colliders( mut commands: Commands, - meshes: Res>, - scene_spawner: Res, + meshes: Option>>, + scene_spawner: Option>, async_colliders: Query<(Entity, &SceneInstance, &AsyncSceneCollider)>, children: Query<&Children>, mesh_handles: Query<(&Name, &Handle)>, ) { + let Some(meshes) = meshes else { return }; + let Some(scene_spawner) = scene_spawner else { return }; 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) { @@ -1995,7 +2014,6 @@ mod tests { fn collider_parent() { let mut app = App::new(); app.add_plugins(( - HeadlessRenderPlugin, TransformPlugin, HierarchyPlugin, TimePlugin, @@ -2007,6 +2025,7 @@ mod tests { colliders: Query<(Entity, Option<&ColliderParent>), With>, parents: Query<&Parent>, bodies: Query<(), With>, + names: Query, ) { for (collider_entity, collider_parent) in &colliders { let rapier_parent = ctx.collider_parent(collider_entity); @@ -2024,38 +2043,52 @@ mod tests { entity = parent.get(); } + let disp = |entity: Option| -> String { + entity.map(|entity| format!("{:?}", names.get(entity).unwrap())).unwrap_or("None".to_owned()) + }; println!( - "{:?} == {:?} == {:?}", - rapier_parent, collider_parent, hierarchal_parent + "Collider {}: {} = {} = {}", + disp(Some(collider_entity)), disp(rapier_parent), disp(collider_parent), disp(hierarchal_parent), ); + assert_eq!(rapier_parent, collider_parent); assert_eq!(rapier_parent, hierarchal_parent); } } - app.add_systems(Last, verify_collider_parent); + fn frame(mut frame: Local) { + *frame += 1; + println!("-- frame {} -----------", *frame); + } + + app.add_systems(Last, (frame, verify_collider_parent).chain()); + + app + .world + .spawn((Name::new("Self-parented"), TransformBundle::default(), RigidBody::Dynamic, Collider::default())); let parent1 = app .world - .spawn((TransformBundle::default(), RigidBody::Dynamic)) + .spawn((Name::new("Parent 1"), TransformBundle::default(), RigidBody::Dynamic)) .id(); let parent2 = app .world - .spawn((TransformBundle::default(), RigidBody::Dynamic)) + .spawn((Name::new("Parent 2"), TransformBundle::default(), RigidBody::Dynamic)) .id(); - let inbetween = app.world.spawn((TransformBundle::default(),)).id(); + let inbetween = app.world.spawn((Name::new("Inbetween"), TransformBundle::default(),)).id(); let child = app .world - .spawn((TransformBundle::default(), Collider::default())) + .spawn((Name::new("Child collider"), TransformBundle::default(), Collider::default())) .id(); // Unnested app.update(); app.world.entity_mut(child).set_parent(parent1); app.update(); + /* app.world.entity_mut(child).set_parent(parent2); app.update(); app.world.entity_mut(child).remove_parent(); @@ -2074,6 +2107,7 @@ mod tests { app.world.entity_mut(inbetween).set_parent(parent1); app.world.entity_mut(child).remove_parent(); app.update(); + */ } // Allows run tests for systems containing rendering related things without GPU From e77d3eb60ff497631ddc72c71ef6e4490d3f3da0 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Sat, 29 Jul 2023 09:44:36 -0700 Subject: [PATCH 14/20] Formatting, etc. --- src/plugin/plugin.rs | 22 +++++++++++ src/plugin/systems.rs | 87 ++++++++++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index 66acb636..f1f02a35 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -87,6 +87,28 @@ where ) .chain() .in_set(RapierTransformPropagateSet), +<<<<<<< HEAD +======= + systems::init_async_colliders.after(RapierTransformPropagateSet), + systems::collect_collider_hierarchy_changes + .before(systems::apply_collider_user_changes), + apply_deferred.after(systems::collect_collider_hierarchy_changes), + systems::apply_scale.after(systems::init_async_colliders), + systems::apply_collider_user_changes.after(systems::apply_scale), + systems::apply_rigid_body_user_changes.after(systems::apply_collider_user_changes), + systems::apply_joint_user_changes.after(systems::apply_rigid_body_user_changes), + systems::init_rigid_bodies.after(systems::apply_joint_user_changes), + systems::init_colliders + .after(systems::init_rigid_bodies) + .after(systems::init_async_colliders), + systems::init_joints.after(systems::init_colliders), + systems::apply_initial_rigid_body_impulses + .after(systems::init_colliders) + .ambiguous_with(systems::init_joints), + systems::sync_removals + .after(systems::init_joints) + .after(systems::apply_initial_rigid_body_impulses), +>>>>>>> 9246295 (Formatting, etc.) #[cfg(all(feature = "dim3", feature = "async-collider"))] systems::init_async_scene_colliders.after(bevy::scene::scene_spawner_system), #[cfg(all(feature = "dim3", feature = "async-collider"))] diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index d607f649..32a4c63f 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -168,25 +168,18 @@ pub fn collect_collider_hierarchy_changes( }; for event in hierarchy_events.iter() { - println!("event: {:?}", event); match event { HierarchyEvent::ChildAdded { child, .. } | HierarchyEvent::ChildMoved { child, .. } => { - println!("child add/moved"); let colliders = child_colliders(*child); let Some(rigid_body) = parent_rigid_body(*child) else { - println!("no rigid body parent"); continue; }; - println!("- updating colliders"); for collider in colliders { - println!("- collider: {:?}", collider); let new_collider_parent = ColliderParent(rigid_body); if let Ok(mut collider_parent) = collider_parents.get_mut(collider) { - println!(" - update existing"); *collider_parent = new_collider_parent; } else { - println!(" - insert new"); commands.entity(collider).insert(new_collider_parent); } } @@ -330,23 +323,14 @@ pub fn apply_collider_user_changes( } } - - for entity in &test_query { - println!("entity {:?} has a cparent", entity); - } - - println!("checking changes"); - for (handle, collider_parent, collider_entity) in changed_collider_parents.iter() { - println!("changed collider: {:?}", collider_entity); + for (handle, collider_parent) in changed_collider_parents.iter() { if let Some(body_handle) = context.entity2body.get(&collider_parent.0).copied() { - println!("body parent: {:?}", collider_parent.0); let RapierContext { ref mut colliders, ref mut bodies, .. } = *context; colliders.set_parent(handle.0, Some(body_handle), bodies); - println!("collider parent: {:?}", context.collider_parent(collider_entity)); } } @@ -2025,8 +2009,19 @@ mod tests { colliders: Query<(Entity, Option<&ColliderParent>), With>, parents: Query<&Parent>, bodies: Query<(), With>, - names: Query, + names: Query<&Name>, ) { + let row_length = 60; + let column_length = row_length / 4; + let column = |collider: &str, rapier: &str, component: &str, hierarchal: &str| { + println!( + "{:| -> String { - entity.map(|entity| format!("{:?}", names.get(entity).unwrap())).unwrap_or("None".to_owned()) + let named = |entity: Option| -> String { + entity + .map(|entity| { + names + .get(entity) + .map(|name| name.as_str().to_owned()) + .unwrap_or(format!("{:?}", entity)) + }) + .unwrap_or("None".to_owned()) }; - println!( - "Collider {}: {} = {} = {}", - disp(Some(collider_entity)), disp(rapier_parent), disp(collider_parent), disp(hierarchal_parent), + column( + &named(Some(collider_entity)), + &named(rapier_parent), + &named(collider_parent), + &named(hierarchal_parent), ); assert_eq!(rapier_parent, collider_parent); @@ -2059,36 +2063,58 @@ mod tests { fn frame(mut frame: Local) { *frame += 1; println!("-- frame {} -----------", *frame); + println!(); } app.add_systems(Last, (frame, verify_collider_parent).chain()); - app + let self_parented = app .world - .spawn((Name::new("Self-parented"), TransformBundle::default(), RigidBody::Dynamic, Collider::default())); + .spawn(( + Name::new("Self-parented"), + TransformBundle::default(), + RigidBody::Dynamic, + Collider::default(), + )) + .id(); let parent1 = app .world - .spawn((Name::new("Parent 1"), TransformBundle::default(), RigidBody::Dynamic)) + .spawn(( + Name::new("Parent 1"), + TransformBundle::default(), + RigidBody::Dynamic, + )) .id(); let parent2 = app .world - .spawn((Name::new("Parent 2"), TransformBundle::default(), RigidBody::Dynamic)) + .spawn(( + Name::new("Parent 2"), + TransformBundle::default(), + RigidBody::Dynamic, + )) .id(); - let inbetween = app.world.spawn((Name::new("Inbetween"), TransformBundle::default(),)).id(); + let inbetween = app + .world + .spawn((Name::new("Inbetween"), TransformBundle::default())) + .id(); let child = app .world - .spawn((Name::new("Child collider"), TransformBundle::default(), Collider::default())) + .spawn(( + Name::new("Child collider"), + TransformBundle::default(), + Collider::default(), + )) .id(); // Unnested app.update(); + app.world.entity_mut(self_parented).despawn_recursive(); app.world.entity_mut(child).set_parent(parent1); app.update(); - /* app.world.entity_mut(child).set_parent(parent2); app.update(); app.world.entity_mut(child).remove_parent(); @@ -2107,7 +2133,6 @@ mod tests { app.world.entity_mut(inbetween).set_parent(parent1); app.world.entity_mut(child).remove_parent(); app.update(); - */ } // Allows run tests for systems containing rendering related things without GPU From bbbaa5e048cc77d0a7ce5c58fd2044ef46666085 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Sat, 29 Jul 2023 10:20:10 -0700 Subject: [PATCH 15/20] Reduce allocations by re-using vecs --- src/plugin/systems.rs | 84 +++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index 32a4c63f..a9b64270 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -119,6 +119,35 @@ pub fn apply_scale( } } +fn find_child_colliders( + base_entity: Entity, + colliders: &Query<&Collider>, + rigid_bodies: &Query<&RigidBody>, + childrens: &Query<&Children>, + + found: &mut Vec, + possibilities: &mut Vec, +) { + found.clear(); + possibilities.clear(); + possibilities.push(base_entity); + while let Some(entity) = possibilities.pop() { + if rigid_bodies.contains(entity) { + continue; + } + + if colliders.contains(entity) { + found.push(entity); + } + + if let Ok(children) = childrens.get(entity) { + possibilities.extend(children.iter()); + } else { + continue; + }; + } +} + /// System responsible for detecting changes in the hierarchy that would /// affect the collider's parent rigid body. pub fn collect_collider_hierarchy_changes( @@ -130,29 +159,10 @@ pub fn collect_collider_hierarchy_changes( rigid_bodies: Query<&RigidBody>, colliders: Query<&Collider>, mut collider_parents: Query<&mut ColliderParent>, -) { - let child_colliders = |entity: Entity| -> Vec { - let mut found = Vec::new(); - let mut possibilities = vec![entity]; - while let Some(entity) = possibilities.pop() { - if rigid_bodies.contains(entity) { - continue; - } - - if colliders.contains(entity) { - found.push(entity); - } - - if let Ok(children) = childrens.get(entity) { - possibilities.extend(children.iter()); - } else { - continue; - }; - } - - found - }; + mut found: Local>, + mut possibilities: Local>, +) { let parent_rigid_body = |mut entity: Entity| -> Option { loop { if rigid_bodies.contains(entity) { @@ -170,25 +180,39 @@ pub fn collect_collider_hierarchy_changes( for event in hierarchy_events.iter() { match event { HierarchyEvent::ChildAdded { child, .. } | HierarchyEvent::ChildMoved { child, .. } => { - let colliders = child_colliders(*child); let Some(rigid_body) = parent_rigid_body(*child) else { continue; }; + find_child_colliders( + *child, + &colliders, + &rigid_bodies, + &childrens, + &mut found, + &mut possibilities, + ); - for collider in colliders { + for collider in &found { let new_collider_parent = ColliderParent(rigid_body); - if let Ok(mut collider_parent) = collider_parents.get_mut(collider) { + if let Ok(mut collider_parent) = collider_parents.get_mut(*collider) { *collider_parent = new_collider_parent; } else { - commands.entity(collider).insert(new_collider_parent); + commands.entity(*collider).insert(new_collider_parent); } } } HierarchyEvent::ChildRemoved { child, .. } => { - let colliders = child_colliders(*child); - for collider in colliders { - if collider_parents.contains(collider) { - commands.entity(collider).remove::(); + find_child_colliders( + *child, + &colliders, + &rigid_bodies, + &childrens, + &mut found, + &mut possibilities, + ); + for collider in &found { + if collider_parents.contains(*collider) { + commands.entity(*collider).remove::(); } } } From 70f2c161faf544ca9ea76d66a2e7b24ef311685e Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 21 Aug 2023 11:39:27 -0700 Subject: [PATCH 16/20] PhysicsSet::EveryFrame so we don't miss hierarchy events --- src/plugin/plugin.rs | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index f1f02a35..3e30abed 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -77,6 +77,12 @@ where /// See [`PhysicsSet`] for a description of these systems. pub fn get_systems(set: PhysicsSet) -> SystemConfigs { match set { + PhysicsSet::EveryFrame => ( + systems::collect_collider_hierarchy_changes, + apply_deferred, + systems::sync_removals, + ).chain() + .into_configs(), PhysicsSet::SyncBackend => ( // Run the character controller before the manual transform propagation. systems::update_character_controls, @@ -87,28 +93,6 @@ where ) .chain() .in_set(RapierTransformPropagateSet), -<<<<<<< HEAD -======= - systems::init_async_colliders.after(RapierTransformPropagateSet), - systems::collect_collider_hierarchy_changes - .before(systems::apply_collider_user_changes), - apply_deferred.after(systems::collect_collider_hierarchy_changes), - systems::apply_scale.after(systems::init_async_colliders), - systems::apply_collider_user_changes.after(systems::apply_scale), - systems::apply_rigid_body_user_changes.after(systems::apply_collider_user_changes), - systems::apply_joint_user_changes.after(systems::apply_rigid_body_user_changes), - systems::init_rigid_bodies.after(systems::apply_joint_user_changes), - systems::init_colliders - .after(systems::init_rigid_bodies) - .after(systems::init_async_colliders), - systems::init_joints.after(systems::init_colliders), - systems::apply_initial_rigid_body_impulses - .after(systems::init_colliders) - .ambiguous_with(systems::init_joints), - systems::sync_removals - .after(systems::init_joints) - .after(systems::apply_initial_rigid_body_impulses), ->>>>>>> 9246295 (Formatting, etc.) #[cfg(all(feature = "dim3", feature = "async-collider"))] systems::init_async_scene_colliders.after(bevy::scene::scene_spawner_system), #[cfg(all(feature = "dim3", feature = "async-collider"))] @@ -116,10 +100,10 @@ where systems::init_rigid_bodies, systems::init_colliders, systems::init_joints, - systems::sync_removals, systems::collect_collider_hierarchy_changes, - // Run this here so the folowwing systems do not have a 1 frame delay. + // Run this here so the following systems do not have a 1 frame delay. apply_deferred, + systems::sync_removals, systems::apply_scale, systems::apply_collider_user_changes, systems::apply_rigid_body_user_changes, @@ -182,6 +166,9 @@ pub enum PhysicsSet { /// components and the [`GlobalTransform`] component. /// These systems typically run immediately after [`PhysicsSet::StepSimulation`]. Writeback, + /// The systems responsible for responding to state like `Events` that get + /// cleared every 2 main schedule runs. + EveryFrame, } impl Plugin for RapierPhysicsPlugin @@ -232,6 +219,10 @@ where // Add each set as necessary if self.default_system_setup { + app.configure_sets( + PostUpdate + PhysicsSet::EveryFrame, + ); app.configure_sets( self.schedule.clone(), ( @@ -244,7 +235,7 @@ where ); // These *must* be in the main schedule currently so that they do not miss events. - app.add_systems(PostUpdate, (systems::sync_removals,)); + app.add_systems(PostUpdate, Self::get_systems(PhysicsSet::EveryFrame).in_set(PhysicsSet::EveryFrame)); app.add_systems( self.schedule.clone(), From 90e0d589e515cf80235bbcddef9be735012a9e1d Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 21 Aug 2023 11:48:23 -0700 Subject: [PATCH 17/20] Configure everyframe --- src/plugin/plugin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index 3e30abed..1d027888 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -220,8 +220,8 @@ where // Add each set as necessary if self.default_system_setup { app.configure_sets( - PostUpdate - PhysicsSet::EveryFrame, + PostUpdate, + (PhysicsSet::EveryFrame,), ); app.configure_sets( self.schedule.clone(), From de6c98355932c6252147b082400575903bb8b9f9 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Wed, 23 Aug 2023 13:47:54 -0700 Subject: [PATCH 18/20] Formatting --- src/plugin/plugin.rs | 15 ++++++++------- src/plugin/systems.rs | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index 1d027888..0ca2e088 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -81,8 +81,9 @@ where systems::collect_collider_hierarchy_changes, apply_deferred, systems::sync_removals, - ).chain() - .into_configs(), + ) + .chain() + .into_configs(), PhysicsSet::SyncBackend => ( // Run the character controller before the manual transform propagation. systems::update_character_controls, @@ -219,10 +220,7 @@ where // Add each set as necessary if self.default_system_setup { - app.configure_sets( - PostUpdate, - (PhysicsSet::EveryFrame,), - ); + app.configure_sets(PostUpdate, (PhysicsSet::EveryFrame,)); app.configure_sets( self.schedule.clone(), ( @@ -235,7 +233,10 @@ where ); // These *must* be in the main schedule currently so that they do not miss events. - app.add_systems(PostUpdate, Self::get_systems(PhysicsSet::EveryFrame).in_set(PhysicsSet::EveryFrame)); + app.add_systems( + PostUpdate, + Self::get_systems(PhysicsSet::EveryFrame).in_set(PhysicsSet::EveryFrame), + ); app.add_systems( self.schedule.clone(), diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index a9b64270..ec36ec83 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -20,7 +20,7 @@ use crate::prelude::{ RigidBodyDisabled, }; use crate::utils; -use bevy::ecs::system::{StaticSystemParam, SystemParamItem, SystemParam}; +use bevy::ecs::system::{StaticSystemParam, SystemParam, SystemParamItem}; use bevy::hierarchy::HierarchyEvent; use bevy::prelude::*; use rapier::prelude::*; @@ -221,8 +221,8 @@ pub fn collect_collider_hierarchy_changes( } /// Collection of change queries for colliders. -/// -/// Mainly used because bevy doesn't impl more than +/// +/// Mainly used because bevy doesn't impl more than /// 16 parameters for a system. #[derive(SystemParam)] pub struct ColliderChanges<'w, 's> { From e9d10d46ce86ba703a30c0e91a43e76dd6068ab1 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Thu, 24 Aug 2023 18:19:40 -0700 Subject: [PATCH 19/20] Improve documentation a bit --- src/geometry/collider.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index c88b5bcd..bf73f29c 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -510,12 +510,19 @@ impl CollidingEntities { pub struct ColliderDisabled; /// Rigid body parent of the collider. +/// +/// This is not meant to be set directly, this is controlled by bevy's hierarchy. +/// +/// To change a colliders parent, set the [`Parent`] of the entity to a +/// different rigid body or remove the parent to leave the collider unattached. #[derive(Component, Debug, Eq, PartialEq, Reflect)] #[reflect(Component, MapEntities, PartialEq)] pub struct ColliderParent(pub(crate) Entity); impl ColliderParent { - /// Gets the [`Entity`] ID of the rigid-body parent of the collider. + /// Gets the [`Entity`] ID of the rigid-body parent this collider is attached to. + /// + /// This may be the same entity as the collider. pub fn get(&self) -> Entity { self.0 } @@ -535,7 +542,6 @@ impl MapEntities for ColliderParent { impl std::ops::Deref for ColliderParent { type Target = Entity; - fn deref(&self) -> &Self::Target { &self.0 } From eed69bf18ae5fbe4e811e660f30390473a307eed Mon Sep 17 00:00:00 2001 From: Aceeri Date: Fri, 1 Sep 2023 07:21:32 -0700 Subject: [PATCH 20/20] 1.72 formatting --- src/geometry/collider.rs | 6 +++--- src/plugin/systems.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index bf73f29c..52a7a696 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -510,9 +510,9 @@ impl CollidingEntities { pub struct ColliderDisabled; /// Rigid body parent of the collider. -/// +/// /// This is not meant to be set directly, this is controlled by bevy's hierarchy. -/// +/// /// To change a colliders parent, set the [`Parent`] of the entity to a /// different rigid body or remove the parent to leave the collider unattached. #[derive(Component, Debug, Eq, PartialEq, Reflect)] @@ -521,7 +521,7 @@ pub struct ColliderParent(pub(crate) Entity); impl ColliderParent { /// Gets the [`Entity`] ID of the rigid-body parent this collider is attached to. - /// + /// /// This may be the same entity as the collider. pub fn get(&self) -> Entity { self.0 diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index ec36ec83..943c2fe0 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -959,7 +959,9 @@ pub fn init_async_scene_colliders( mesh_handles: Query<(&Name, &Handle)>, ) { let Some(meshes) = meshes else { return }; - let Some(scene_spawner) = scene_spawner else { return }; + let Some(scene_spawner) = scene_spawner else { + return; + }; 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) { @@ -2058,7 +2060,9 @@ mod tests { break; } - let Ok(parent) = parents.get(entity) else { break }; + let Ok(parent) = parents.get(entity) else { + break; + }; entity = parent.get(); }