From 176c3bae149b6e46b97a154d335951edba183ea9 Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Fri, 28 Mar 2025 12:48:25 +0100 Subject: [PATCH] Fix user changes handling (#803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add failing test from @Johannes0021 * apply fix on update_positions * apply fix on ColliderSet::iter_mut * fix clippy.. * more complete test * feat: refactor modified sets into a wrapper to avoid future mistakes * chore: fix typos --------- Co-authored-by: Sébastien Crozet --- src/data/mod.rs | 2 + src/data/modified_objects.rs | 65 +++++++++++++ src/dynamics/mod.rs | 1 + src/dynamics/rigid_body_components.rs | 12 +-- src/dynamics/rigid_body_set.rs | 52 ++++++----- src/geometry/collider_set.rs | 63 ++++++++----- src/geometry/mod.rs | 1 + src/pipeline/collision_pipeline.rs | 11 ++- src/pipeline/physics_pipeline.rs | 126 ++++++++++++++++++++++++-- src/pipeline/user_changes.rs | 17 ++-- 10 files changed, 272 insertions(+), 78 deletions(-) create mode 100644 src/data/modified_objects.rs diff --git a/src/data/mod.rs b/src/data/mod.rs index 6d9e2ce..1a89f1e 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -2,8 +2,10 @@ pub use self::arena::{Arena, Index}; pub use self::coarena::Coarena; +pub(crate) use self::modified_objects::{HasModifiedFlag, ModifiedObjects}; pub mod arena; mod coarena; pub(crate) mod graph; +mod modified_objects; pub mod pubsub; diff --git a/src/data/modified_objects.rs b/src/data/modified_objects.rs new file mode 100644 index 0000000..6ac85c6 --- /dev/null +++ b/src/data/modified_objects.rs @@ -0,0 +1,65 @@ +use std::marker::PhantomData; +use std::ops::Deref; + +/// Contains handles of modified objects. +/// +/// This is a wrapper over a `Vec` to ensure we don’t forget to set the object’s +/// MODIFIED flag when adding it to this set. +/// It is possible to bypass the wrapper with `.as_mut_internal`. But this should only +/// be done for internal engine usage (like the physics pipeline). +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Clone, Debug)] +pub(crate) struct ModifiedObjects(Vec, PhantomData); + +impl Default for ModifiedObjects { + fn default() -> Self { + Self(Vec::new(), PhantomData) + } +} + +pub(crate) trait HasModifiedFlag { + fn has_modified_flag(&self) -> bool; + fn set_modified_flag(&mut self); +} + +impl Deref for ModifiedObjects { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ModifiedObjects { + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity), PhantomData) + } + + /// Remove every handle from this set. + /// + /// Note that the corresponding object MODIFIED flags won’t be reset automatically by this function. + pub fn clear(&mut self) { + self.0.clear() + } + + /// Pushes a object handle to this set after checking that it doesn’t have the MODIFIED + /// flag set. + /// + /// This will also set the object’s MODIFIED flag. + pub fn push_once(&mut self, handle: Handle, object: &mut Object) { + if !object.has_modified_flag() { + self.push_unchecked(handle, object); + } + } + + /// Pushes an object handle to this set without checking if the object already has the MODIFIED + /// flags. + /// + /// Only use in situation where you are certain (due to other contextual information) that + /// the object isn’t already in the set. + /// + /// This will also set the object’s MODIFIED flag. + pub fn push_unchecked(&mut self, handle: Handle, object: &mut Object) { + object.set_modified_flag(); + self.0.push(handle); + } +} diff --git a/src/dynamics/mod.rs b/src/dynamics/mod.rs index 0f9a019..3e68402 100644 --- a/src/dynamics/mod.rs +++ b/src/dynamics/mod.rs @@ -8,6 +8,7 @@ pub(crate) use self::joint::JointGraphEdge; pub(crate) use self::joint::JointIndex; pub use self::joint::*; pub use self::rigid_body_components::*; +pub(crate) use self::rigid_body_set::ModifiedRigidBodies; // #[cfg(not(feature = "parallel"))] pub(crate) use self::solver::IslandSolver; // #[cfg(feature = "parallel")] diff --git a/src/dynamics/rigid_body_components.rs b/src/dynamics/rigid_body_components.rs index f68f42f..36bf799 100644 --- a/src/dynamics/rigid_body_components.rs +++ b/src/dynamics/rigid_body_components.rs @@ -4,7 +4,7 @@ use crate::control::PdErrors; use crate::dynamics::MassProperties; use crate::geometry::{ ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition, - ColliderSet, ColliderShape, + ColliderSet, ColliderShape, ModifiedColliders, }; use crate::math::{ AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector, @@ -1020,10 +1020,10 @@ impl RigidBodyColliders { } /// Update the positions of all the colliders attached to this rigid-body. - pub fn update_positions( + pub(crate) fn update_positions( &self, colliders: &mut ColliderSet, - modified_colliders: &mut Vec, + modified_colliders: &mut ModifiedColliders, parent_pos: &Isometry, ) { for handle in &self.0 { @@ -1031,12 +1031,10 @@ impl RigidBodyColliders { let co = colliders.index_mut_internal(*handle); let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent; - if !co.changes.contains(ColliderChanges::MODIFIED) { - modified_colliders.push(*handle); - } - // Set the modification flag so we can benefit from the modification-tracking // when updating the narrow-phase/broad-phase afterwards. + modified_colliders.push_once(*handle, co); + co.changes |= ColliderChanges::POSITION; co.pos = ColliderPosition(new_pos); } diff --git a/src/dynamics/rigid_body_set.rs b/src/dynamics/rigid_body_set.rs index 1261e4a..641fd10 100644 --- a/src/dynamics/rigid_body_set.rs +++ b/src/dynamics/rigid_body_set.rs @@ -1,4 +1,4 @@ -use crate::data::Arena; +use crate::data::{Arena, HasModifiedFlag, ModifiedObjects}; use crate::dynamics::{ ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBody, RigidBodyChanges, RigidBodyHandle, }; @@ -22,6 +22,20 @@ impl BodyPair { } } +pub(crate) type ModifiedRigidBodies = ModifiedObjects; + +impl HasModifiedFlag for RigidBody { + #[inline] + fn has_modified_flag(&self) -> bool { + self.changes.contains(RigidBodyChanges::MODIFIED) + } + + #[inline] + fn set_modified_flag(&mut self) { + self.changes |= RigidBodyChanges::MODIFIED; + } +} + #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Default, Debug)] /// A set of rigid bodies that can be handled by a physics pipeline. @@ -31,7 +45,7 @@ pub struct RigidBodySet { // parallelism because the `Receiver` breaks the Sync impl. // Could we avoid this? pub(crate) bodies: Arena, - pub(crate) modified_bodies: Vec, + pub(crate) modified_bodies: ModifiedRigidBodies, } impl RigidBodySet { @@ -39,7 +53,7 @@ impl RigidBodySet { pub fn new() -> Self { RigidBodySet { bodies: Arena::new(), - modified_bodies: Vec::new(), + modified_bodies: ModifiedObjects::default(), } } @@ -47,11 +61,11 @@ impl RigidBodySet { pub fn with_capacity(capacity: usize) -> Self { RigidBodySet { bodies: Arena::with_capacity(capacity), - modified_bodies: Vec::with_capacity(capacity), + modified_bodies: ModifiedRigidBodies::with_capacity(capacity), } } - pub(crate) fn take_modified(&mut self) -> Vec { + pub(crate) fn take_modified(&mut self) -> ModifiedRigidBodies { std::mem::take(&mut self.modified_bodies) } @@ -79,7 +93,10 @@ impl RigidBodySet { rb.changes.set(RigidBodyChanges::all(), true); let handle = RigidBodyHandle(self.bodies.insert(rb)); - self.modified_bodies.push(handle); + // Using push_unchecked because this is a brand new rigid-body with the MODIFIED + // flags set but isn’t in the modified_bodies yet. + self.modified_bodies + .push_unchecked(handle, &mut self.bodies[handle.0]); handle } @@ -152,7 +169,7 @@ impl RigidBodySet { pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut RigidBody, RigidBodyHandle)> { let (rb, handle) = self.bodies.get_unknown_gen_mut(i)?; let handle = RigidBodyHandle(handle); - Self::mark_as_modified(handle, rb, &mut self.modified_bodies); + self.modified_bodies.push_once(handle, rb); Some((rb, handle)) } @@ -161,22 +178,11 @@ impl RigidBodySet { self.bodies.get(handle.0) } - pub(crate) fn mark_as_modified( - handle: RigidBodyHandle, - rb: &mut RigidBody, - modified_bodies: &mut Vec, - ) { - if !rb.changes.contains(RigidBodyChanges::MODIFIED) { - rb.changes = RigidBodyChanges::MODIFIED; - modified_bodies.push(handle); - } - } - /// Gets a mutable reference to the rigid-body with the given handle. #[cfg(not(feature = "dev-remove-slow-accessors"))] pub fn get_mut(&mut self, handle: RigidBodyHandle) -> Option<&mut RigidBody> { let result = self.bodies.get_mut(handle.0)?; - Self::mark_as_modified(handle, result, &mut self.modified_bodies); + self.modified_bodies.push_once(handle, result); Some(result) } @@ -195,7 +201,7 @@ impl RigidBodySet { handle: RigidBodyHandle, ) -> Option<&mut RigidBody> { let result = self.bodies.get_mut(handle.0)?; - Self::mark_as_modified(handle, result, &mut self.modified_bodies); + self.modified_bodies.push_once(handle, result); Some(result) } @@ -210,7 +216,9 @@ impl RigidBodySet { self.modified_bodies.clear(); let modified_bodies = &mut self.modified_bodies; self.bodies.iter_mut().map(move |(h, b)| { - modified_bodies.push(RigidBodyHandle(h)); + // NOTE: using `push_unchecked` because we just cleared `modified_bodies` + // before iterating. + modified_bodies.push_unchecked(RigidBodyHandle(h), b); (RigidBodyHandle(h), b) }) } @@ -256,7 +264,7 @@ impl Index for RigidBodySet { impl IndexMut for RigidBodySet { fn index_mut(&mut self, handle: RigidBodyHandle) -> &mut RigidBody { let rb = &mut self.bodies[handle.0]; - Self::mark_as_modified(handle, rb, &mut self.modified_bodies); + self.modified_bodies.push_once(handle, rb); rb } } diff --git a/src/geometry/collider_set.rs b/src/geometry/collider_set.rs index 7a2b6aa..87ffbd0 100644 --- a/src/geometry/collider_set.rs +++ b/src/geometry/collider_set.rs @@ -1,15 +1,30 @@ use crate::data::arena::Arena; +use crate::data::{HasModifiedFlag, ModifiedObjects}; use crate::dynamics::{IslandManager, RigidBodyHandle, RigidBodySet}; use crate::geometry::{Collider, ColliderChanges, ColliderHandle, ColliderParent}; use crate::math::Isometry; use std::ops::{Index, IndexMut}; +pub(crate) type ModifiedColliders = ModifiedObjects; + +impl HasModifiedFlag for Collider { + #[inline] + fn has_modified_flag(&self) -> bool { + self.changes.contains(ColliderChanges::MODIFIED) + } + + #[inline] + fn set_modified_flag(&mut self) { + self.changes |= ColliderChanges::MODIFIED; + } +} + #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Default, Debug)] /// A set of colliders that can be handled by a physics `World`. pub struct ColliderSet { pub(crate) colliders: Arena, - pub(crate) modified_colliders: Vec, + pub(crate) modified_colliders: ModifiedColliders, pub(crate) removed_colliders: Vec, } @@ -18,7 +33,7 @@ impl ColliderSet { pub fn new() -> Self { ColliderSet { colliders: Arena::new(), - modified_colliders: Vec::new(), + modified_colliders: Default::default(), removed_colliders: Vec::new(), } } @@ -29,12 +44,12 @@ impl ColliderSet { pub fn with_capacity(capacity: usize) -> Self { ColliderSet { colliders: Arena::with_capacity(capacity), - modified_colliders: Vec::with_capacity(capacity), + modified_colliders: ModifiedColliders::with_capacity(capacity), removed_colliders: Vec::new(), } } - pub(crate) fn take_modified(&mut self) -> Vec { + pub(crate) fn take_modified(&mut self) -> ModifiedColliders { std::mem::take(&mut self.modified_colliders) } @@ -65,9 +80,11 @@ impl ColliderSet { pub fn iter_mut(&mut self) -> impl Iterator { self.modified_colliders.clear(); let modified_colliders = &mut self.modified_colliders; - self.colliders.iter_mut().map(move |(h, b)| { - modified_colliders.push(ColliderHandle(h)); - (ColliderHandle(h), b) + self.colliders.iter_mut().map(move |(h, co)| { + // NOTE: we push unchecked here since we are just re-populating the + // `modified_colliders` set that we just cleared before iteration. + modified_colliders.push_unchecked(ColliderHandle(h), co); + (ColliderHandle(h), co) }) } @@ -100,7 +117,11 @@ impl ColliderSet { coll.reset_internal_references(); coll.parent = None; let handle = ColliderHandle(self.colliders.insert(coll)); - self.modified_colliders.push(handle); + // NOTE: we push unchecked because this is a brand-new collider + // so it was initialized with the changed flag but isn’t in + // the set yet. + self.modified_colliders + .push_unchecked(handle, &mut self.colliders[handle.0]); handle } @@ -131,9 +152,12 @@ impl ColliderSet { .get_mut_internal_with_modification_tracking(parent_handle) .expect("Parent rigid body not found."); let handle = ColliderHandle(self.colliders.insert(coll)); - self.modified_colliders.push(handle); - let coll = self.colliders.get_mut(handle.0).unwrap(); + // NOTE: we push unchecked because this is a brand-new collider + // so it was initialized with the changed flag but isn’t in + // the set yet. + self.modified_colliders.push_unchecked(handle, coll); + parent.add_collider_internal( handle, coll.parent.as_mut().unwrap(), @@ -258,7 +282,7 @@ impl ColliderSet { pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut Collider, ColliderHandle)> { let (collider, handle) = self.colliders.get_unknown_gen_mut(i)?; let handle = ColliderHandle(handle); - Self::mark_as_modified(handle, collider, &mut self.modified_colliders); + self.modified_colliders.push_once(handle, collider); Some((collider, handle)) } @@ -267,22 +291,11 @@ impl ColliderSet { self.colliders.get(handle.0) } - fn mark_as_modified( - handle: ColliderHandle, - collider: &mut Collider, - modified_colliders: &mut Vec, - ) { - if !collider.changes.contains(ColliderChanges::MODIFIED) { - collider.changes = ColliderChanges::MODIFIED; - modified_colliders.push(handle); - } - } - /// Gets a mutable reference to the collider with the given handle. #[cfg(not(feature = "dev-remove-slow-accessors"))] pub fn get_mut(&mut self, handle: ColliderHandle) -> Option<&mut Collider> { let result = self.colliders.get_mut(handle.0)?; - Self::mark_as_modified(handle, result, &mut self.modified_colliders); + self.modified_colliders.push_once(handle, result); Some(result) } @@ -302,7 +315,7 @@ impl ColliderSet { handle: ColliderHandle, ) -> Option<&mut Collider> { let result = self.colliders.get_mut(handle.0)?; - Self::mark_as_modified(handle, result, &mut self.modified_colliders); + self.modified_colliders.push_once(handle, result); Some(result) } } @@ -327,7 +340,7 @@ impl Index for ColliderSet { impl IndexMut for ColliderSet { fn index_mut(&mut self, handle: ColliderHandle) -> &mut Collider { let collider = &mut self.colliders[handle.0]; - Self::mark_as_modified(handle, collider, &mut self.modified_colliders); + self.modified_colliders.push_once(handle, collider); collider } } diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index ea0c7fb..1e94365 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -182,6 +182,7 @@ impl ContactForceEvent { } pub(crate) use self::broad_phase::BroadPhaseProxyIndex; +pub(crate) use self::collider_set::ModifiedColliders; pub(crate) use self::narrow_phase::ContactManifoldIndex; pub(crate) use parry::partitioning::Qbvh; pub use parry::shape::*; diff --git a/src/pipeline/collision_pipeline.rs b/src/pipeline/collision_pipeline.rs index 9004946..b75ca5c 100644 --- a/src/pipeline/collision_pipeline.rs +++ b/src/pipeline/collision_pipeline.rs @@ -2,7 +2,8 @@ use crate::dynamics::{ImpulseJointSet, MultibodyJointSet}; use crate::geometry::{ - BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, NarrowPhase, + BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, + ModifiedColliders, NarrowPhase, }; use crate::math::Real; use crate::pipeline::{EventHandler, PhysicsHooks, QueryPipeline}; @@ -97,13 +98,15 @@ impl CollisionPipeline { fn clear_modified_colliders( &mut self, colliders: &mut ColliderSet, - modified_colliders: &mut Vec, + modified_colliders: &mut ModifiedColliders, ) { - for handle in modified_colliders.drain(..) { - if let Some(co) = colliders.get_mut_internal(handle) { + for handle in modified_colliders.iter() { + if let Some(co) = colliders.get_mut_internal(*handle) { co.changes = ColliderChanges::empty(); } } + + modified_colliders.clear(); } /// Executes one step of the collision detection. diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index 91a737c..aea95c6 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -7,14 +7,15 @@ use crate::dynamics::IslandSolver; use crate::dynamics::JointGraphEdge; use crate::dynamics::{ CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, - RigidBodyChanges, RigidBodyHandle, RigidBodyPosition, RigidBodyType, + RigidBodyChanges, RigidBodyPosition, RigidBodyType, }; use crate::geometry::{ BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair, - ContactManifoldIndex, NarrowPhase, TemporaryInteractionIndex, + ContactManifoldIndex, ModifiedColliders, NarrowPhase, TemporaryInteractionIndex, }; use crate::math::{Real, Vector}; use crate::pipeline::{EventHandler, PhysicsHooks, QueryPipeline}; +use crate::prelude::ModifiedRigidBodies; use {crate::dynamics::RigidBodySet, crate::geometry::ColliderSet}; /// The physics pipeline, responsible for stepping the whole physics simulation. @@ -68,25 +69,29 @@ impl PhysicsPipeline { fn clear_modified_colliders( &mut self, colliders: &mut ColliderSet, - modified_colliders: &mut Vec, + modified_colliders: &mut ModifiedColliders, ) { - for handle in modified_colliders.drain(..) { - if let Some(co) = colliders.get_mut_internal(handle) { + for handle in modified_colliders.iter() { + if let Some(co) = colliders.get_mut_internal(*handle) { co.changes = ColliderChanges::empty(); } } + + modified_colliders.clear(); } fn clear_modified_bodies( &mut self, bodies: &mut RigidBodySet, - modified_bodies: &mut Vec, + modified_bodies: &mut ModifiedRigidBodies, ) { - for handle in modified_bodies.drain(..) { - if let Some(rb) = bodies.get_mut_internal(handle) { + for handle in modified_bodies.iter() { + if let Some(rb) = bodies.get_mut_internal(*handle) { rb.changes = RigidBodyChanges::empty(); } } + + modified_bodies.clear(); } fn detect_collisions( @@ -359,7 +364,7 @@ impl PhysicsPipeline { islands: &IslandManager, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, - modified_colliders: &mut Vec, + modified_colliders: &mut ModifiedColliders, ) { // Set the rigid-bodies and kinematic bodies to their final position. for handle in islands.iter_active_bodies() { @@ -1011,4 +1016,107 @@ mod test { assert!(rotation.w.is_finite()); } } + + #[test] + #[cfg(feature = "dim2")] + fn test_multi_sap_disable_body() { + use na::vector; + let mut rigid_body_set = RigidBodySet::new(); + let mut collider_set = ColliderSet::new(); + + /* Create the ground. */ + let collider = ColliderBuilder::cuboid(100.0, 0.1).build(); + collider_set.insert(collider); + + /* Create the bouncing ball. */ + let rigid_body = RigidBodyBuilder::dynamic() + .translation(vector![0.0, 10.0]) + .build(); + let collider = ColliderBuilder::ball(0.5).restitution(0.7).build(); + let ball_body_handle = rigid_body_set.insert(rigid_body); + collider_set.insert_with_parent(collider, ball_body_handle, &mut rigid_body_set); + + /* Create other structures necessary for the simulation. */ + let gravity = vector![0.0, -9.81]; + let integration_parameters = IntegrationParameters::default(); + let mut physics_pipeline = PhysicsPipeline::new(); + let mut island_manager = IslandManager::new(); + let mut broad_phase = BroadPhaseMultiSap::new(); + let mut narrow_phase = NarrowPhase::new(); + let mut impulse_joint_set = ImpulseJointSet::new(); + let mut multibody_joint_set = MultibodyJointSet::new(); + let mut ccd_solver = CCDSolver::new(); + let physics_hooks = (); + let event_handler = (); + + physics_pipeline.step( + &gravity, + &integration_parameters, + &mut island_manager, + &mut broad_phase, + &mut narrow_phase, + &mut rigid_body_set, + &mut collider_set, + &mut impulse_joint_set, + &mut multibody_joint_set, + &mut ccd_solver, + None, + &physics_hooks, + &event_handler, + ); + + // Test RigidBodyChanges::POSITION and disable + { + let ball_body = &mut rigid_body_set[ball_body_handle]; + + // Also, change the translation and rotation to different values + ball_body.set_translation(vector![1.0, 1.0], true); + ball_body.set_rotation(nalgebra::UnitComplex::new(1.0), true); + + ball_body.set_enabled(false); + } + + physics_pipeline.step( + &gravity, + &integration_parameters, + &mut island_manager, + &mut broad_phase, + &mut narrow_phase, + &mut rigid_body_set, + &mut collider_set, + &mut impulse_joint_set, + &mut multibody_joint_set, + &mut ccd_solver, + None, + &physics_hooks, + &event_handler, + ); + + // Test RigidBodyChanges::POSITION and enable + { + let ball_body = &mut rigid_body_set[ball_body_handle]; + + // Also, change the translation and rotation to different values + ball_body.set_translation(vector![0.0, 0.0], true); + ball_body.set_rotation(nalgebra::UnitComplex::new(0.0), true); + + ball_body.set_enabled(true); + } + + physics_pipeline.step( + &gravity, + &integration_parameters, + &mut island_manager, + &mut broad_phase, + &mut narrow_phase, + &mut rigid_body_set, + &mut collider_set, + &mut impulse_joint_set, + &mut multibody_joint_set, + &mut ccd_solver, + None, + &physics_hooks, + &event_handler, + ); + } } diff --git a/src/pipeline/user_changes.rs b/src/pipeline/user_changes.rs index a2b00a1..c326d6a 100644 --- a/src/pipeline/user_changes.rs +++ b/src/pipeline/user_changes.rs @@ -4,6 +4,7 @@ use crate::dynamics::{ }; use crate::geometry::{ ColliderChanges, ColliderEnabled, ColliderHandle, ColliderPosition, ColliderSet, + ModifiedColliders, }; pub(crate) fn handle_user_changes_to_colliders( @@ -48,7 +49,7 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( impulse_joints: &mut ImpulseJointSet, _multibody_joints: &mut MultibodyJointSet, // FIXME: propagate disabled state to multibodies modified_bodies: &[RigidBodyHandle], - modified_colliders: &mut Vec, + modified_colliders: &mut ModifiedColliders, ) { enum FinalAction { UpdateActiveKinematicSetId(usize), @@ -150,12 +151,8 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( // here because that would modify the `modified_colliders` inside of the `ColliderSet` // instead of the one passed to this method. let co = colliders.index_mut_internal(*handle); - if !co.changes.contains(ColliderChanges::MODIFIED) { - modified_colliders.push(*handle); - } - - co.changes |= - ColliderChanges::MODIFIED | ColliderChanges::PARENT_EFFECTIVE_DOMINANCE; + modified_colliders.push_once(*handle, co); + co.changes |= ColliderChanges::PARENT_EFFECTIVE_DOMINANCE; } } @@ -166,9 +163,7 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( // here because that would modify the `modified_colliders` inside of the `ColliderSet` // instead of the one passed to this method. let co = colliders.index_mut_internal(*handle); - if !co.changes.contains(ColliderChanges::MODIFIED) { - modified_colliders.push(*handle); - } + modified_colliders.push_once(*handle, co); if rb.enabled && co.flags.enabled == ColliderEnabled::DisabledByParent { co.flags.enabled = ColliderEnabled::Enabled; @@ -176,7 +171,7 @@ pub(crate) fn handle_user_changes_to_rigid_bodies( co.flags.enabled = ColliderEnabled::DisabledByParent; } - co.changes |= ColliderChanges::MODIFIED | ColliderChanges::ENABLED_OR_DISABLED; + co.changes |= ColliderChanges::ENABLED_OR_DISABLED; } // Propagate the rigid-body’s enabled/disable status to its attached impulse joints.