From 343dd61428ca3dab95f492231bda4579899e9524 Mon Sep 17 00:00:00 2001 From: Anthony Tornetta Date: Fri, 9 Jan 2026 22:35:05 -0500 Subject: [PATCH 1/4] Reworking how changing world removes itself from original world This is buggy --- src/plugin/systems/collider.rs | 14 +++- src/plugin/systems/joint.rs | 10 ++- .../systems/multiple_rapier_contexts.rs | 70 +++++++++++-------- src/plugin/systems/remove.rs | 67 ++++++++++++++++++ src/plugin/systems/rigid_body.rs | 13 +++- 5 files changed, 140 insertions(+), 34 deletions(-) diff --git a/src/plugin/systems/collider.rs b/src/plugin/systems/collider.rs index 423f3071..d960cc8a 100644 --- a/src/plugin/systems/collider.rs +++ b/src/plugin/systems/collider.rs @@ -91,7 +91,11 @@ pub fn apply_collider_user_changes( (changed_collider_transforms, child_of_query, transform_query): ( Query< (RapierEntity, &RapierColliderHandle, &GlobalTransform), - (Without, Changed), + ( + Without, + Changed, + Changed, + ), >, Query<&ChildOf>, Query<&Transform>, @@ -361,7 +365,13 @@ pub fn init_colliders( config: Query<&RapierConfiguration>, mut context_access: Query<(&mut RapierRigidBodySet, &mut RapierContextColliders)>, default_context_access: Query>, - colliders: Query<(ColliderComponents, Option<&GlobalTransform>), Without>, + colliders: Query< + (ColliderComponents, Option<&GlobalTransform>), + Or<( + Without, + Changed, + )>, + >, mut rigid_body_mprops: Query<&mut ReadMassProperties>, child_of_query: Query<&ChildOf>, transform_query: Query<&Transform>, diff --git a/src/plugin/systems/joint.rs b/src/plugin/systems/joint.rs index 08185a93..5d99be98 100644 --- a/src/plugin/systems/joint.rs +++ b/src/plugin/systems/joint.rs @@ -16,11 +16,17 @@ pub fn init_joints( default_context_access: Query>, impulse_joints: Query< (Entity, Option<&RapierContextEntityLink>, &ImpulseJoint), - Without, + Or<( + Without, + Changed, + )>, >, multibody_joints: Query< (Entity, Option<&RapierContextEntityLink>, &MultibodyJoint), - Without, + Or<( + Without, + Changed, + )>, >, child_of_query: Query<&ChildOf>, ) { diff --git a/src/plugin/systems/multiple_rapier_contexts.rs b/src/plugin/systems/multiple_rapier_contexts.rs index 2b366d50..43ef531c 100644 --- a/src/plugin/systems/multiple_rapier_contexts.rs +++ b/src/plugin/systems/multiple_rapier_contexts.rs @@ -1,13 +1,11 @@ //! systems to support multiple physics contexts, and changes between them. -use crate::dynamics::{ - RapierImpulseJointHandle, RapierMultibodyJointHandle, RapierRigidBodyHandle, -}; -use crate::geometry::RapierColliderHandle; use crate::plugin::context::RapierRigidBodySet; use crate::plugin::context::{ RapierContextColliders, RapierContextEntityLink, RapierContextJoints, }; +use crate::plugin::systems::remove_all_physics; +use crate::prelude::{MassModifiedMessage, RapierContextSimulation}; use bevy::prelude::*; /// If an entity is turned into the child of something with a physics context link, @@ -23,17 +21,28 @@ pub fn on_add_entity_with_parent( ), >, q_child_of: Query<&ChildOf>, - q_physics_world: Query<&RapierContextEntityLink>, + q_rapier_context_link: Query<&RapierContextEntityLink>, mut commands: Commands, + mut context_writer: Query<( + &mut RapierContextSimulation, + &mut RapierContextColliders, + &mut RapierContextJoints, + &mut RapierRigidBodySet, + )>, + mut mass_modified: MessageWriter, ) { for (ent, child_of) in &q_add_entity_without_parent { let mut parent = Some(child_of.parent()); while let Some(parent_entity) = parent { - if let Ok(pw) = q_physics_world.get(parent_entity) { + if let Ok(context_entity_link) = q_rapier_context_link.get(parent_entity) { // Change rapier context link only if the existing link isn't the correct one. - if q_physics_world.get(ent).map(|x| x != pw).unwrap_or(true) { - remove_old_physics(ent, &mut commands); - commands.entity(ent).insert(*pw); + if q_rapier_context_link + .get(ent) + .map(|x| x != context_entity_link) + .unwrap_or(true) + { + remove_all_physics(ent, &mut context_writer, &mut mass_modified); + commands.entity(ent).insert(*context_entity_link); } break; } @@ -42,16 +51,6 @@ pub fn on_add_entity_with_parent( } } -/// Flags the entity to have its old physics removed -fn remove_old_physics(entity: Entity, commands: &mut Commands) { - commands - .entity(entity) - .remove::() - .remove::() - .remove::() - .remove::(); -} - /// Reacts to modifications to [`RapierContextEntityLink`] /// to move an entity's physics data from a context to another. /// @@ -63,18 +62,20 @@ pub fn on_change_context( >, q_children: Query<&Children>, q_physics_context: Query<&RapierContextEntityLink>, - q_context: Query<( - &RapierContextColliders, - &RapierContextJoints, - &RapierRigidBodySet, - )>, mut commands: Commands, + mut context_writer: Query<( + &mut RapierContextSimulation, + &mut RapierContextColliders, + &mut RapierContextJoints, + &mut RapierRigidBodySet, + )>, + mut mass_modified: MessageWriter, ) { for (entity, new_physics_context) in &q_changed_contexts { - let context = q_context.get(new_physics_context.0); + let context = context_writer.get(new_physics_context.0); // Ensure the context actually changed before removing them from the context if !context - .map(|(colliders, joints, rigidbody_set)| { + .map(|(_, colliders, joints, rigidbody_set)| { // They are already apart of this context if any of these are true colliders.entity2collider.contains_key(&entity) || rigidbody_set.entity2body.contains_key(&entity) @@ -83,13 +84,16 @@ pub fn on_change_context( }) .unwrap_or(false) { - remove_old_physics(entity, &mut commands); + info!("Adding new context to top-level {entity:?}"); + remove_all_physics(entity, &mut context_writer, &mut mass_modified); bubble_down_context_change( &mut commands, entity, &q_children, *new_physics_context, &q_physics_context, + &mut context_writer, + &mut mass_modified, ); } } @@ -101,6 +105,13 @@ fn bubble_down_context_change( q_children: &Query<&Children>, new_physics_context: RapierContextEntityLink, q_physics_context: &Query<&RapierContextEntityLink>, + context_writer: &mut Query<( + &mut RapierContextSimulation, + &mut RapierContextColliders, + &mut RapierContextJoints, + &mut RapierRigidBodySet, + )>, + mass_modified: &mut MessageWriter, ) { let Ok(children) = q_children.get(entity) else { return; @@ -115,7 +126,8 @@ fn bubble_down_context_change( return; } - remove_old_physics(child, commands); + info!("Adding new context to child {child:?}"); + remove_all_physics(child, context_writer, mass_modified); commands.entity(child).insert(new_physics_context); bubble_down_context_change( @@ -124,6 +136,8 @@ fn bubble_down_context_change( q_children, new_physics_context, q_physics_context, + context_writer, + mass_modified, ); }); } diff --git a/src/plugin/systems/remove.rs b/src/plugin/systems/remove.rs index b0dda53f..d90e3d94 100644 --- a/src/plugin/systems/remove.rs +++ b/src/plugin/systems/remove.rs @@ -57,6 +57,8 @@ pub fn sync_removals( let context = &mut *context; let joints = &mut *joints; + info!("Removing ent {entity:?} from context!"); + let _ = rigidbody_set.last_body_transform_set.remove(&handle); rigidbody_set.bodies.remove( handle, @@ -217,6 +219,71 @@ pub fn sync_removals( // TODO: what about removing forces? } +/// Removes this entity from the physics context it's a part of +/// +/// This will NOT remove any components from the entity, only remove it from its current simulation +/// backend. It will NOT be re-added unless its components are modified or it is manually re-added. +pub fn remove_all_physics( + entity: Entity, + context_writer: &mut Query<( + &mut RapierContextSimulation, + &mut RapierContextColliders, + &mut RapierContextJoints, + &mut RapierRigidBodySet, + )>, + mass_modified: &mut MessageWriter, +) { + let Some((mut context, mut context_colliders, mut joints, mut rigidbody_set)) = + context_writer.iter_mut().find(|context| { + context.3.entity2body.contains_key(&entity) + || context.1.entity2collider.contains_key(&entity) + || context.2.entity2impulse_joint.contains_key(&entity) + || context.2.entity2multibody_joint.contains_key(&entity) + }) + else { + return; + }; + + let context = &mut *context; + let joints = &mut *joints; + + if let Some(handle) = rigidbody_set.entity2body.remove(&entity) { + let _ = rigidbody_set.last_body_transform_set.remove(&handle); + rigidbody_set.bodies.remove( + handle, + &mut context.islands, + &mut context_colliders.colliders, + &mut joints.impulse_joints, + &mut joints.multibody_joints, + false, + ); + } + + if let Some(handle) = context_colliders.entity2collider.remove(&entity) { + let context = &mut *context; + let context_colliders = &mut *context_colliders; + if let Some(parent) = context_colliders.collider_parent(&rigidbody_set, entity) { + mass_modified.write(parent.into()); + } + + context_colliders.colliders.remove( + handle, + &mut context.islands, + &mut rigidbody_set.bodies, + true, + ); + context.deleted_colliders.insert(handle, entity); + } + + if let Some(handle) = joints.entity2impulse_joint.remove(&entity) { + joints.impulse_joints.remove(handle, true); + } + + if let Some(handle) = joints.entity2multibody_joint.remove(&entity) { + joints.multibody_joints.remove(handle, true); + } +} + fn find_context<'a, TReturn, TQueryParams: QueryData>( context_writer: &'a mut Query, item_finder: impl Fn(&mut TQueryParams::Item<'_, '_>) -> Option, diff --git a/src/plugin/systems/rigid_body.rs b/src/plugin/systems/rigid_body.rs index e5401d8b..3810be45 100644 --- a/src/plugin/systems/rigid_body.rs +++ b/src/plugin/systems/rigid_body.rs @@ -564,7 +564,13 @@ pub fn init_rigid_bodies( mut commands: Commands, default_context_access: Query>, mut rigidbody_sets: Query<(Entity, &mut RapierRigidBodySet)>, - rigid_bodies: Query>, + rigid_bodies: Query< + RigidBodyComponents, + Or<( + Without, + Changed, + )>, + >, ) { for ( (entity, entity_context_link), @@ -702,7 +708,10 @@ pub fn apply_initial_rigid_body_impulses( // executed yet. mut init_impulses: Query< (Entity, &RapierContextEntityLink, &mut ExternalImpulse), - Without, + Or<( + Without, + Changed, + )>, >, ) { for (entity, link, mut impulse) in init_impulses.iter_mut() { From 11f545930a64f72cc40c7243f0e60c20f2582662 Mon Sep 17 00:00:00 2001 From: Anthony Tornetta Date: Fri, 9 Jan 2026 22:35:51 -0500 Subject: [PATCH 2/4] Revert "Reworking how changing world removes itself from original world" This reverts commit 343dd61428ca3dab95f492231bda4579899e9524. --- src/plugin/systems/collider.rs | 14 +--- src/plugin/systems/joint.rs | 10 +-- .../systems/multiple_rapier_contexts.rs | 70 ++++++++----------- src/plugin/systems/remove.rs | 67 ------------------ src/plugin/systems/rigid_body.rs | 13 +--- 5 files changed, 34 insertions(+), 140 deletions(-) diff --git a/src/plugin/systems/collider.rs b/src/plugin/systems/collider.rs index d960cc8a..423f3071 100644 --- a/src/plugin/systems/collider.rs +++ b/src/plugin/systems/collider.rs @@ -91,11 +91,7 @@ pub fn apply_collider_user_changes( (changed_collider_transforms, child_of_query, transform_query): ( Query< (RapierEntity, &RapierColliderHandle, &GlobalTransform), - ( - Without, - Changed, - Changed, - ), + (Without, Changed), >, Query<&ChildOf>, Query<&Transform>, @@ -365,13 +361,7 @@ pub fn init_colliders( config: Query<&RapierConfiguration>, mut context_access: Query<(&mut RapierRigidBodySet, &mut RapierContextColliders)>, default_context_access: Query>, - colliders: Query< - (ColliderComponents, Option<&GlobalTransform>), - Or<( - Without, - Changed, - )>, - >, + colliders: Query<(ColliderComponents, Option<&GlobalTransform>), Without>, mut rigid_body_mprops: Query<&mut ReadMassProperties>, child_of_query: Query<&ChildOf>, transform_query: Query<&Transform>, diff --git a/src/plugin/systems/joint.rs b/src/plugin/systems/joint.rs index 5d99be98..08185a93 100644 --- a/src/plugin/systems/joint.rs +++ b/src/plugin/systems/joint.rs @@ -16,17 +16,11 @@ pub fn init_joints( default_context_access: Query>, impulse_joints: Query< (Entity, Option<&RapierContextEntityLink>, &ImpulseJoint), - Or<( - Without, - Changed, - )>, + Without, >, multibody_joints: Query< (Entity, Option<&RapierContextEntityLink>, &MultibodyJoint), - Or<( - Without, - Changed, - )>, + Without, >, child_of_query: Query<&ChildOf>, ) { diff --git a/src/plugin/systems/multiple_rapier_contexts.rs b/src/plugin/systems/multiple_rapier_contexts.rs index 43ef531c..2b366d50 100644 --- a/src/plugin/systems/multiple_rapier_contexts.rs +++ b/src/plugin/systems/multiple_rapier_contexts.rs @@ -1,11 +1,13 @@ //! systems to support multiple physics contexts, and changes between them. +use crate::dynamics::{ + RapierImpulseJointHandle, RapierMultibodyJointHandle, RapierRigidBodyHandle, +}; +use crate::geometry::RapierColliderHandle; use crate::plugin::context::RapierRigidBodySet; use crate::plugin::context::{ RapierContextColliders, RapierContextEntityLink, RapierContextJoints, }; -use crate::plugin::systems::remove_all_physics; -use crate::prelude::{MassModifiedMessage, RapierContextSimulation}; use bevy::prelude::*; /// If an entity is turned into the child of something with a physics context link, @@ -21,28 +23,17 @@ pub fn on_add_entity_with_parent( ), >, q_child_of: Query<&ChildOf>, - q_rapier_context_link: Query<&RapierContextEntityLink>, + q_physics_world: Query<&RapierContextEntityLink>, mut commands: Commands, - mut context_writer: Query<( - &mut RapierContextSimulation, - &mut RapierContextColliders, - &mut RapierContextJoints, - &mut RapierRigidBodySet, - )>, - mut mass_modified: MessageWriter, ) { for (ent, child_of) in &q_add_entity_without_parent { let mut parent = Some(child_of.parent()); while let Some(parent_entity) = parent { - if let Ok(context_entity_link) = q_rapier_context_link.get(parent_entity) { + if let Ok(pw) = q_physics_world.get(parent_entity) { // Change rapier context link only if the existing link isn't the correct one. - if q_rapier_context_link - .get(ent) - .map(|x| x != context_entity_link) - .unwrap_or(true) - { - remove_all_physics(ent, &mut context_writer, &mut mass_modified); - commands.entity(ent).insert(*context_entity_link); + if q_physics_world.get(ent).map(|x| x != pw).unwrap_or(true) { + remove_old_physics(ent, &mut commands); + commands.entity(ent).insert(*pw); } break; } @@ -51,6 +42,16 @@ pub fn on_add_entity_with_parent( } } +/// Flags the entity to have its old physics removed +fn remove_old_physics(entity: Entity, commands: &mut Commands) { + commands + .entity(entity) + .remove::() + .remove::() + .remove::() + .remove::(); +} + /// Reacts to modifications to [`RapierContextEntityLink`] /// to move an entity's physics data from a context to another. /// @@ -62,20 +63,18 @@ pub fn on_change_context( >, q_children: Query<&Children>, q_physics_context: Query<&RapierContextEntityLink>, - mut commands: Commands, - mut context_writer: Query<( - &mut RapierContextSimulation, - &mut RapierContextColliders, - &mut RapierContextJoints, - &mut RapierRigidBodySet, + q_context: Query<( + &RapierContextColliders, + &RapierContextJoints, + &RapierRigidBodySet, )>, - mut mass_modified: MessageWriter, + mut commands: Commands, ) { for (entity, new_physics_context) in &q_changed_contexts { - let context = context_writer.get(new_physics_context.0); + let context = q_context.get(new_physics_context.0); // Ensure the context actually changed before removing them from the context if !context - .map(|(_, colliders, joints, rigidbody_set)| { + .map(|(colliders, joints, rigidbody_set)| { // They are already apart of this context if any of these are true colliders.entity2collider.contains_key(&entity) || rigidbody_set.entity2body.contains_key(&entity) @@ -84,16 +83,13 @@ pub fn on_change_context( }) .unwrap_or(false) { - info!("Adding new context to top-level {entity:?}"); - remove_all_physics(entity, &mut context_writer, &mut mass_modified); + remove_old_physics(entity, &mut commands); bubble_down_context_change( &mut commands, entity, &q_children, *new_physics_context, &q_physics_context, - &mut context_writer, - &mut mass_modified, ); } } @@ -105,13 +101,6 @@ fn bubble_down_context_change( q_children: &Query<&Children>, new_physics_context: RapierContextEntityLink, q_physics_context: &Query<&RapierContextEntityLink>, - context_writer: &mut Query<( - &mut RapierContextSimulation, - &mut RapierContextColliders, - &mut RapierContextJoints, - &mut RapierRigidBodySet, - )>, - mass_modified: &mut MessageWriter, ) { let Ok(children) = q_children.get(entity) else { return; @@ -126,8 +115,7 @@ fn bubble_down_context_change( return; } - info!("Adding new context to child {child:?}"); - remove_all_physics(child, context_writer, mass_modified); + remove_old_physics(child, commands); commands.entity(child).insert(new_physics_context); bubble_down_context_change( @@ -136,8 +124,6 @@ fn bubble_down_context_change( q_children, new_physics_context, q_physics_context, - context_writer, - mass_modified, ); }); } diff --git a/src/plugin/systems/remove.rs b/src/plugin/systems/remove.rs index d90e3d94..b0dda53f 100644 --- a/src/plugin/systems/remove.rs +++ b/src/plugin/systems/remove.rs @@ -57,8 +57,6 @@ pub fn sync_removals( let context = &mut *context; let joints = &mut *joints; - info!("Removing ent {entity:?} from context!"); - let _ = rigidbody_set.last_body_transform_set.remove(&handle); rigidbody_set.bodies.remove( handle, @@ -219,71 +217,6 @@ pub fn sync_removals( // TODO: what about removing forces? } -/// Removes this entity from the physics context it's a part of -/// -/// This will NOT remove any components from the entity, only remove it from its current simulation -/// backend. It will NOT be re-added unless its components are modified or it is manually re-added. -pub fn remove_all_physics( - entity: Entity, - context_writer: &mut Query<( - &mut RapierContextSimulation, - &mut RapierContextColliders, - &mut RapierContextJoints, - &mut RapierRigidBodySet, - )>, - mass_modified: &mut MessageWriter, -) { - let Some((mut context, mut context_colliders, mut joints, mut rigidbody_set)) = - context_writer.iter_mut().find(|context| { - context.3.entity2body.contains_key(&entity) - || context.1.entity2collider.contains_key(&entity) - || context.2.entity2impulse_joint.contains_key(&entity) - || context.2.entity2multibody_joint.contains_key(&entity) - }) - else { - return; - }; - - let context = &mut *context; - let joints = &mut *joints; - - if let Some(handle) = rigidbody_set.entity2body.remove(&entity) { - let _ = rigidbody_set.last_body_transform_set.remove(&handle); - rigidbody_set.bodies.remove( - handle, - &mut context.islands, - &mut context_colliders.colliders, - &mut joints.impulse_joints, - &mut joints.multibody_joints, - false, - ); - } - - if let Some(handle) = context_colliders.entity2collider.remove(&entity) { - let context = &mut *context; - let context_colliders = &mut *context_colliders; - if let Some(parent) = context_colliders.collider_parent(&rigidbody_set, entity) { - mass_modified.write(parent.into()); - } - - context_colliders.colliders.remove( - handle, - &mut context.islands, - &mut rigidbody_set.bodies, - true, - ); - context.deleted_colliders.insert(handle, entity); - } - - if let Some(handle) = joints.entity2impulse_joint.remove(&entity) { - joints.impulse_joints.remove(handle, true); - } - - if let Some(handle) = joints.entity2multibody_joint.remove(&entity) { - joints.multibody_joints.remove(handle, true); - } -} - fn find_context<'a, TReturn, TQueryParams: QueryData>( context_writer: &'a mut Query, item_finder: impl Fn(&mut TQueryParams::Item<'_, '_>) -> Option, diff --git a/src/plugin/systems/rigid_body.rs b/src/plugin/systems/rigid_body.rs index 3810be45..e5401d8b 100644 --- a/src/plugin/systems/rigid_body.rs +++ b/src/plugin/systems/rigid_body.rs @@ -564,13 +564,7 @@ pub fn init_rigid_bodies( mut commands: Commands, default_context_access: Query>, mut rigidbody_sets: Query<(Entity, &mut RapierRigidBodySet)>, - rigid_bodies: Query< - RigidBodyComponents, - Or<( - Without, - Changed, - )>, - >, + rigid_bodies: Query>, ) { for ( (entity, entity_context_link), @@ -708,10 +702,7 @@ pub fn apply_initial_rigid_body_impulses( // executed yet. mut init_impulses: Query< (Entity, &RapierContextEntityLink, &mut ExternalImpulse), - Or<( - Without, - Changed, - )>, + Without, >, ) { for (entity, link, mut impulse) in init_impulses.iter_mut() { From e6327a2102ea73511546a418813189963e9137af Mon Sep 17 00:00:00 2001 From: Anthony Tornetta Date: Tue, 6 Jan 2026 22:40:24 -0500 Subject: [PATCH 3/4] Added changing context example --- bevy_rapier3d/examples/change_contexts3.rs | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 bevy_rapier3d/examples/change_contexts3.rs diff --git a/bevy_rapier3d/examples/change_contexts3.rs b/bevy_rapier3d/examples/change_contexts3.rs new file mode 100644 index 00000000..40a10dfc --- /dev/null +++ b/bevy_rapier3d/examples/change_contexts3.rs @@ -0,0 +1,125 @@ +use bevy::prelude::*; +use bevy_rapier3d::prelude::*; + +const N_CONTEXTS: usize = 5; +const WORLD_CHANGE_DELAY_SEC: f32 = 3.0; + +#[derive(Component)] +/// Denotes which object(s) to change the world of +struct ChangeWorld; + +#[derive(Resource, Debug)] +struct Contexts(Vec); + +fn main() { + App::new() + .insert_resource(ClearColor(Color::linear_rgb( + 0xF9 as f32 / 255.0, + 0xF9 as f32 / 255.0, + 0xFF as f32 / 255.0, + ))) + .add_plugins(( + DefaultPlugins, + RapierPhysicsPlugin::::default(), + RapierDebugRenderPlugin::default(), + )) + .add_systems(Startup, (setup_graphics, setup_physics)) + .add_systems(Update, change_world) + .run(); +} + +fn change_world( + mut query: Query<&mut RapierContextEntityLink, With>, + time: Res