Skip to content

Commit 176c3ba

Browse files
Vrixyzsebcrozet
andauthored
Fix user changes handling (#803)
* 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 <[email protected]>
1 parent d291041 commit 176c3ba

10 files changed

+272
-78
lines changed

src/data/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
33
pub use self::arena::{Arena, Index};
44
pub use self::coarena::Coarena;
5+
pub(crate) use self::modified_objects::{HasModifiedFlag, ModifiedObjects};
56

67
pub mod arena;
78
mod coarena;
89
pub(crate) mod graph;
10+
mod modified_objects;
911
pub mod pubsub;

src/data/modified_objects.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::marker::PhantomData;
2+
use std::ops::Deref;
3+
4+
/// Contains handles of modified objects.
5+
///
6+
/// This is a wrapper over a `Vec` to ensure we don’t forget to set the object’s
7+
/// MODIFIED flag when adding it to this set.
8+
/// It is possible to bypass the wrapper with `.as_mut_internal`. But this should only
9+
/// be done for internal engine usage (like the physics pipeline).
10+
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
11+
#[derive(Clone, Debug)]
12+
pub(crate) struct ModifiedObjects<Handle, Object>(Vec<Handle>, PhantomData<Object>);
13+
14+
impl<Handle, Object> Default for ModifiedObjects<Handle, Object> {
15+
fn default() -> Self {
16+
Self(Vec::new(), PhantomData)
17+
}
18+
}
19+
20+
pub(crate) trait HasModifiedFlag {
21+
fn has_modified_flag(&self) -> bool;
22+
fn set_modified_flag(&mut self);
23+
}
24+
25+
impl<Handle, Object> Deref for ModifiedObjects<Handle, Object> {
26+
type Target = Vec<Handle>;
27+
fn deref(&self) -> &Self::Target {
28+
&self.0
29+
}
30+
}
31+
32+
impl<Handle, Object: HasModifiedFlag> ModifiedObjects<Handle, Object> {
33+
pub fn with_capacity(capacity: usize) -> Self {
34+
Self(Vec::with_capacity(capacity), PhantomData)
35+
}
36+
37+
/// Remove every handle from this set.
38+
///
39+
/// Note that the corresponding object MODIFIED flags won’t be reset automatically by this function.
40+
pub fn clear(&mut self) {
41+
self.0.clear()
42+
}
43+
44+
/// Pushes a object handle to this set after checking that it doesn’t have the MODIFIED
45+
/// flag set.
46+
///
47+
/// This will also set the object’s MODIFIED flag.
48+
pub fn push_once(&mut self, handle: Handle, object: &mut Object) {
49+
if !object.has_modified_flag() {
50+
self.push_unchecked(handle, object);
51+
}
52+
}
53+
54+
/// Pushes an object handle to this set without checking if the object already has the MODIFIED
55+
/// flags.
56+
///
57+
/// Only use in situation where you are certain (due to other contextual information) that
58+
/// the object isn’t already in the set.
59+
///
60+
/// This will also set the object’s MODIFIED flag.
61+
pub fn push_unchecked(&mut self, handle: Handle, object: &mut Object) {
62+
object.set_modified_flag();
63+
self.0.push(handle);
64+
}
65+
}

src/dynamics/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub(crate) use self::joint::JointGraphEdge;
88
pub(crate) use self::joint::JointIndex;
99
pub use self::joint::*;
1010
pub use self::rigid_body_components::*;
11+
pub(crate) use self::rigid_body_set::ModifiedRigidBodies;
1112
// #[cfg(not(feature = "parallel"))]
1213
pub(crate) use self::solver::IslandSolver;
1314
// #[cfg(feature = "parallel")]

src/dynamics/rigid_body_components.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::control::PdErrors;
44
use crate::dynamics::MassProperties;
55
use crate::geometry::{
66
ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
7-
ColliderSet, ColliderShape,
7+
ColliderSet, ColliderShape, ModifiedColliders,
88
};
99
use crate::math::{
1010
AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector,
@@ -1020,23 +1020,21 @@ impl RigidBodyColliders {
10201020
}
10211021

10221022
/// Update the positions of all the colliders attached to this rigid-body.
1023-
pub fn update_positions(
1023+
pub(crate) fn update_positions(
10241024
&self,
10251025
colliders: &mut ColliderSet,
1026-
modified_colliders: &mut Vec<ColliderHandle>,
1026+
modified_colliders: &mut ModifiedColliders,
10271027
parent_pos: &Isometry<Real>,
10281028
) {
10291029
for handle in &self.0 {
10301030
// NOTE: the ColliderParent component must exist if we enter this method.
10311031
let co = colliders.index_mut_internal(*handle);
10321032
let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
10331033

1034-
if !co.changes.contains(ColliderChanges::MODIFIED) {
1035-
modified_colliders.push(*handle);
1036-
}
1037-
10381034
// Set the modification flag so we can benefit from the modification-tracking
10391035
// when updating the narrow-phase/broad-phase afterwards.
1036+
modified_colliders.push_once(*handle, co);
1037+
10401038
co.changes |= ColliderChanges::POSITION;
10411039
co.pos = ColliderPosition(new_pos);
10421040
}

src/dynamics/rigid_body_set.rs

+30-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::data::Arena;
1+
use crate::data::{Arena, HasModifiedFlag, ModifiedObjects};
22
use crate::dynamics::{
33
ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBody, RigidBodyChanges, RigidBodyHandle,
44
};
@@ -22,6 +22,20 @@ impl BodyPair {
2222
}
2323
}
2424

25+
pub(crate) type ModifiedRigidBodies = ModifiedObjects<RigidBodyHandle, RigidBody>;
26+
27+
impl HasModifiedFlag for RigidBody {
28+
#[inline]
29+
fn has_modified_flag(&self) -> bool {
30+
self.changes.contains(RigidBodyChanges::MODIFIED)
31+
}
32+
33+
#[inline]
34+
fn set_modified_flag(&mut self) {
35+
self.changes |= RigidBodyChanges::MODIFIED;
36+
}
37+
}
38+
2539
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
2640
#[derive(Clone, Default, Debug)]
2741
/// A set of rigid bodies that can be handled by a physics pipeline.
@@ -31,27 +45,27 @@ pub struct RigidBodySet {
3145
// parallelism because the `Receiver` breaks the Sync impl.
3246
// Could we avoid this?
3347
pub(crate) bodies: Arena<RigidBody>,
34-
pub(crate) modified_bodies: Vec<RigidBodyHandle>,
48+
pub(crate) modified_bodies: ModifiedRigidBodies,
3549
}
3650

3751
impl RigidBodySet {
3852
/// Create a new empty set of rigid bodies.
3953
pub fn new() -> Self {
4054
RigidBodySet {
4155
bodies: Arena::new(),
42-
modified_bodies: Vec::new(),
56+
modified_bodies: ModifiedObjects::default(),
4357
}
4458
}
4559

4660
/// Create a new set of rigid bodies, with an initial capacity.
4761
pub fn with_capacity(capacity: usize) -> Self {
4862
RigidBodySet {
4963
bodies: Arena::with_capacity(capacity),
50-
modified_bodies: Vec::with_capacity(capacity),
64+
modified_bodies: ModifiedRigidBodies::with_capacity(capacity),
5165
}
5266
}
5367

54-
pub(crate) fn take_modified(&mut self) -> Vec<RigidBodyHandle> {
68+
pub(crate) fn take_modified(&mut self) -> ModifiedRigidBodies {
5569
std::mem::take(&mut self.modified_bodies)
5670
}
5771

@@ -79,7 +93,10 @@ impl RigidBodySet {
7993
rb.changes.set(RigidBodyChanges::all(), true);
8094

8195
let handle = RigidBodyHandle(self.bodies.insert(rb));
82-
self.modified_bodies.push(handle);
96+
// Using push_unchecked because this is a brand new rigid-body with the MODIFIED
97+
// flags set but isn’t in the modified_bodies yet.
98+
self.modified_bodies
99+
.push_unchecked(handle, &mut self.bodies[handle.0]);
83100
handle
84101
}
85102

@@ -152,7 +169,7 @@ impl RigidBodySet {
152169
pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut RigidBody, RigidBodyHandle)> {
153170
let (rb, handle) = self.bodies.get_unknown_gen_mut(i)?;
154171
let handle = RigidBodyHandle(handle);
155-
Self::mark_as_modified(handle, rb, &mut self.modified_bodies);
172+
self.modified_bodies.push_once(handle, rb);
156173
Some((rb, handle))
157174
}
158175

@@ -161,22 +178,11 @@ impl RigidBodySet {
161178
self.bodies.get(handle.0)
162179
}
163180

164-
pub(crate) fn mark_as_modified(
165-
handle: RigidBodyHandle,
166-
rb: &mut RigidBody,
167-
modified_bodies: &mut Vec<RigidBodyHandle>,
168-
) {
169-
if !rb.changes.contains(RigidBodyChanges::MODIFIED) {
170-
rb.changes = RigidBodyChanges::MODIFIED;
171-
modified_bodies.push(handle);
172-
}
173-
}
174-
175181
/// Gets a mutable reference to the rigid-body with the given handle.
176182
#[cfg(not(feature = "dev-remove-slow-accessors"))]
177183
pub fn get_mut(&mut self, handle: RigidBodyHandle) -> Option<&mut RigidBody> {
178184
let result = self.bodies.get_mut(handle.0)?;
179-
Self::mark_as_modified(handle, result, &mut self.modified_bodies);
185+
self.modified_bodies.push_once(handle, result);
180186
Some(result)
181187
}
182188

@@ -195,7 +201,7 @@ impl RigidBodySet {
195201
handle: RigidBodyHandle,
196202
) -> Option<&mut RigidBody> {
197203
let result = self.bodies.get_mut(handle.0)?;
198-
Self::mark_as_modified(handle, result, &mut self.modified_bodies);
204+
self.modified_bodies.push_once(handle, result);
199205
Some(result)
200206
}
201207

@@ -210,7 +216,9 @@ impl RigidBodySet {
210216
self.modified_bodies.clear();
211217
let modified_bodies = &mut self.modified_bodies;
212218
self.bodies.iter_mut().map(move |(h, b)| {
213-
modified_bodies.push(RigidBodyHandle(h));
219+
// NOTE: using `push_unchecked` because we just cleared `modified_bodies`
220+
// before iterating.
221+
modified_bodies.push_unchecked(RigidBodyHandle(h), b);
214222
(RigidBodyHandle(h), b)
215223
})
216224
}
@@ -256,7 +264,7 @@ impl Index<crate::data::Index> for RigidBodySet {
256264
impl IndexMut<RigidBodyHandle> for RigidBodySet {
257265
fn index_mut(&mut self, handle: RigidBodyHandle) -> &mut RigidBody {
258266
let rb = &mut self.bodies[handle.0];
259-
Self::mark_as_modified(handle, rb, &mut self.modified_bodies);
267+
self.modified_bodies.push_once(handle, rb);
260268
rb
261269
}
262270
}

0 commit comments

Comments
 (0)