rapier3d/dynamics/
island_manager.rs

1use crate::dynamics::{
2    ImpulseJointSet, MultibodyJointSet, RigidBodyActivation, RigidBodyChanges, RigidBodyColliders,
3    RigidBodyHandle, RigidBodyIds, RigidBodySet, RigidBodyType, RigidBodyVelocity,
4};
5use crate::geometry::{ColliderSet, NarrowPhase};
6use crate::math::Real;
7use crate::utils::SimdDot;
8
9/// Structure responsible for maintaining the set of active rigid-bodies, and
10/// putting non-moving rigid-bodies to sleep to save computation times.
11#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
12#[derive(Clone, Default)]
13pub struct IslandManager {
14    pub(crate) active_dynamic_set: Vec<RigidBodyHandle>,
15    pub(crate) active_kinematic_set: Vec<RigidBodyHandle>,
16    pub(crate) active_islands: Vec<usize>,
17    pub(crate) active_islands_additional_solver_iterations: Vec<usize>,
18    active_set_timestamp: u32,
19    #[cfg_attr(feature = "serde-serialize", serde(skip))]
20    can_sleep: Vec<RigidBodyHandle>, // Workspace.
21    #[cfg_attr(feature = "serde-serialize", serde(skip))]
22    stack: Vec<RigidBodyHandle>, // Workspace.
23}
24
25impl IslandManager {
26    /// Creates a new empty island manager.
27    pub fn new() -> Self {
28        Self {
29            active_dynamic_set: vec![],
30            active_kinematic_set: vec![],
31            active_islands: vec![],
32            active_islands_additional_solver_iterations: vec![],
33            active_set_timestamp: 0,
34            can_sleep: vec![],
35            stack: vec![],
36        }
37    }
38
39    pub(crate) fn num_islands(&self) -> usize {
40        self.active_islands.len().saturating_sub(1)
41    }
42
43    /// Update this data-structure after one or multiple rigid-bodies have been removed for `bodies`.
44    pub fn cleanup_removed_rigid_bodies(&mut self, bodies: &mut RigidBodySet) {
45        let mut active_sets = [&mut self.active_kinematic_set, &mut self.active_dynamic_set];
46
47        for active_set in &mut active_sets {
48            let mut i = 0;
49
50            while i < active_set.len() {
51                let handle = active_set[i];
52                if bodies.get(handle).is_none() {
53                    // This rigid-body no longer exists, so we need to remove it from the active set.
54                    active_set.swap_remove(i);
55
56                    if i < active_set.len() {
57                        // Update the active_set_id for the body that has been swapped.
58                        if let Some(swapped_rb) = bodies.get_mut_internal(active_set[i]) {
59                            swapped_rb.ids.active_set_id = i;
60                        }
61                    }
62                } else {
63                    i += 1;
64                }
65            }
66        }
67    }
68
69    pub(crate) fn rigid_body_removed(
70        &mut self,
71        removed_handle: RigidBodyHandle,
72        removed_ids: &RigidBodyIds,
73        bodies: &mut RigidBodySet,
74    ) {
75        let mut active_sets = [&mut self.active_kinematic_set, &mut self.active_dynamic_set];
76
77        for active_set in &mut active_sets {
78            if active_set.get(removed_ids.active_set_id) == Some(&removed_handle) {
79                active_set.swap_remove(removed_ids.active_set_id);
80
81                if let Some(replacement) = active_set
82                    .get(removed_ids.active_set_id)
83                    .and_then(|h| bodies.get_mut_internal(*h))
84                {
85                    replacement.ids.active_set_id = removed_ids.active_set_id;
86                }
87            }
88        }
89    }
90
91    /// Forces the specified rigid-body to wake up if it is dynamic.
92    ///
93    /// If `strong` is `true` then it is assured that the rigid-body will
94    /// remain awake during multiple subsequent timesteps.
95    pub fn wake_up(&mut self, bodies: &mut RigidBodySet, handle: RigidBodyHandle, strong: bool) {
96        // NOTE: the use an Option here because there are many legitimate cases (like when
97        //       deleting a joint attached to an already-removed body) where we could be
98        //       attempting to wake-up a rigid-body that has already been deleted.
99        if bodies.get(handle).map(|rb| rb.body_type()) == Some(RigidBodyType::Dynamic) {
100            let rb = bodies.index_mut_internal(handle);
101
102            // Check that the user didn’t change the sleeping state explicitly, in which
103            // case we don’t overwrite it.
104            if !rb.changes.contains(RigidBodyChanges::SLEEP) {
105                rb.activation.wake_up(strong);
106
107                if rb.is_enabled()
108                    && self.active_dynamic_set.get(rb.ids.active_set_id) != Some(&handle)
109                {
110                    rb.ids.active_set_id = self.active_dynamic_set.len();
111                    self.active_dynamic_set.push(handle);
112                }
113            }
114        }
115    }
116
117    /// Iter through all the active kinematic rigid-bodies on this set.
118    pub fn active_kinematic_bodies(&self) -> &[RigidBodyHandle] {
119        &self.active_kinematic_set[..]
120    }
121
122    /// Iter through all the active dynamic rigid-bodies on this set.
123    pub fn active_dynamic_bodies(&self) -> &[RigidBodyHandle] {
124        &self.active_dynamic_set[..]
125    }
126
127    pub(crate) fn active_island(&self, island_id: usize) -> &[RigidBodyHandle] {
128        let island_range = self.active_islands[island_id]..self.active_islands[island_id + 1];
129        &self.active_dynamic_set[island_range]
130    }
131
132    pub(crate) fn active_island_additional_solver_iterations(&self, island_id: usize) -> usize {
133        self.active_islands_additional_solver_iterations[island_id]
134    }
135
136    #[inline(always)]
137    pub(crate) fn iter_active_bodies(&self) -> impl Iterator<Item = RigidBodyHandle> + '_ {
138        self.active_dynamic_set
139            .iter()
140            .copied()
141            .chain(self.active_kinematic_set.iter().copied())
142    }
143
144    #[cfg(feature = "parallel")]
145    #[allow(dead_code)] // That will likely be useful when we re-introduce intra-island parallelism.
146    pub(crate) fn active_island_range(&self, island_id: usize) -> std::ops::Range<usize> {
147        self.active_islands[island_id]..self.active_islands[island_id + 1]
148    }
149
150    pub(crate) fn update_active_set_with_contacts(
151        &mut self,
152        dt: Real,
153        length_unit: Real,
154        bodies: &mut RigidBodySet,
155        colliders: &ColliderSet,
156        narrow_phase: &NarrowPhase,
157        impulse_joints: &ImpulseJointSet,
158        multibody_joints: &MultibodyJointSet,
159        min_island_size: usize,
160    ) {
161        assert!(
162            min_island_size > 0,
163            "The minimum island size must be at least 1."
164        );
165
166        // Update the energy of every rigid body and
167        // keep only those that may not sleep.
168        //        let t = Instant::now();
169        self.active_set_timestamp += 1;
170        self.stack.clear();
171        self.can_sleep.clear();
172
173        // NOTE: the `.rev()` is here so that two successive timesteps preserve
174        // the order of the bodies in the `active_dynamic_set` vec. This reversal
175        // does not seem to affect performances nor stability. However it makes
176        // debugging slightly nicer.
177        for h in self.active_dynamic_set.drain(..).rev() {
178            let can_sleep = &mut self.can_sleep;
179            let stack = &mut self.stack;
180
181            let rb = bodies.index_mut_internal(h);
182            let sq_linvel = rb.vels.linvel.norm_squared();
183            let sq_angvel = rb.vels.angvel.gdot(rb.vels.angvel);
184
185            update_energy(length_unit, &mut rb.activation, sq_linvel, sq_angvel, dt);
186
187            if rb.activation.time_since_can_sleep >= rb.activation.time_until_sleep {
188                // Mark them as sleeping for now. This will
189                // be set to false during the graph traversal
190                // if it should not be put to sleep.
191                rb.activation.sleeping = true;
192                can_sleep.push(h);
193            } else {
194                stack.push(h);
195            }
196        }
197
198        // Read all the contacts and push objects touching touching this rigid-body.
199        #[inline(always)]
200        fn push_contacting_bodies(
201            rb_colliders: &RigidBodyColliders,
202            colliders: &ColliderSet,
203            narrow_phase: &NarrowPhase,
204            stack: &mut Vec<RigidBodyHandle>,
205        ) {
206            for collider_handle in &rb_colliders.0 {
207                for inter in narrow_phase.contact_pairs_with(*collider_handle) {
208                    for manifold in &inter.manifolds {
209                        if !manifold.data.solver_contacts.is_empty() {
210                            let other = crate::utils::select_other(
211                                (inter.collider1, inter.collider2),
212                                *collider_handle,
213                            );
214                            if let Some(other_body) = colliders[other].parent {
215                                stack.push(other_body.handle);
216                            }
217                            break;
218                        }
219                    }
220                }
221            }
222        }
223
224        // Now iterate on all active kinematic bodies and push all the bodies
225        // touching them to the stack so they can be woken up.
226        for h in self.active_kinematic_set.iter() {
227            let rb = &bodies[*h];
228
229            if rb.vels.is_zero() {
230                // If the kinematic body does not move, it does not have
231                // to wake up any dynamic body.
232                continue;
233            }
234
235            push_contacting_bodies(&rb.colliders, colliders, narrow_phase, &mut self.stack);
236        }
237
238        //        println!("Selection: {}", Instant::now() - t);
239
240        //        let t = Instant::now();
241        // Propagation of awake state and awake island computation through the
242        // traversal of the interaction graph.
243        self.active_islands_additional_solver_iterations.clear();
244        self.active_islands.clear();
245        self.active_islands.push(0);
246
247        // saturating_sub(1) prevents underflow when the stack is empty.
248        let mut island_marker = self.stack.len().saturating_sub(1);
249
250        // NOTE: islands containing a body with non-standard number of iterations won’t
251        //       be merged with another island, unless another island with standard
252        //       iterations number already started before and got continued due to the
253        //       `min_island_size`. That could be avoided by pushing bodies with non-standard
254        //       iterations on top of the stack (and other bodies on the back). Not sure it’s
255        //       worth it though.
256        let mut additional_solver_iterations = 0;
257
258        while let Some(handle) = self.stack.pop() {
259            let rb = bodies.index_mut_internal(handle);
260
261            if rb.ids.active_set_timestamp == self.active_set_timestamp || !rb.is_dynamic() {
262                // We already visited this body and its neighbors.
263                // Also, we don't propagate awake state through fixed bodies.
264                continue;
265            }
266
267            if self.stack.len() < island_marker {
268                if additional_solver_iterations != rb.additional_solver_iterations
269                    || self.active_dynamic_set.len() - *self.active_islands.last().unwrap()
270                        >= min_island_size
271                {
272                    // We are starting a new island.
273                    self.active_islands_additional_solver_iterations
274                        .push(additional_solver_iterations);
275                    self.active_islands.push(self.active_dynamic_set.len());
276                    additional_solver_iterations = 0;
277                }
278
279                island_marker = self.stack.len();
280            }
281
282            additional_solver_iterations =
283                additional_solver_iterations.max(rb.additional_solver_iterations);
284
285            // Transmit the active state to all the rigid-bodies with colliders
286            // in contact or joined with this collider.
287            push_contacting_bodies(&rb.colliders, colliders, narrow_phase, &mut self.stack);
288
289            for inter in impulse_joints.attached_enabled_joints(handle) {
290                let other = crate::utils::select_other((inter.0, inter.1), handle);
291                self.stack.push(other);
292            }
293
294            for other in multibody_joints.bodies_attached_with_enabled_joint(handle) {
295                self.stack.push(other);
296            }
297
298            rb.activation.wake_up(false);
299            rb.ids.active_island_id = self.active_islands.len() - 1;
300            rb.ids.active_set_id = self.active_dynamic_set.len();
301            rb.ids.active_set_offset =
302                rb.ids.active_set_id - self.active_islands[rb.ids.active_island_id];
303            rb.ids.active_set_timestamp = self.active_set_timestamp;
304
305            self.active_dynamic_set.push(handle);
306        }
307
308        self.active_islands_additional_solver_iterations
309            .push(additional_solver_iterations);
310        self.active_islands.push(self.active_dynamic_set.len());
311        //        println!(
312        //            "Extraction: {}, num islands: {}",
313        //            Instant::now() - t,
314        //            self.active_islands.len() - 1
315        //        );
316
317        // Actually put to sleep bodies which have not been detected as awake.
318        for handle in &self.can_sleep {
319            let rb = bodies.index_mut_internal(*handle);
320            if rb.activation.sleeping {
321                rb.vels = RigidBodyVelocity::zero();
322                rb.activation.sleep();
323            }
324        }
325    }
326}
327
328fn update_energy(
329    length_unit: Real,
330    activation: &mut RigidBodyActivation,
331    sq_linvel: Real,
332    sq_angvel: Real,
333    dt: Real,
334) {
335    let linear_threshold = activation.normalized_linear_threshold * length_unit;
336    if sq_linvel < linear_threshold * linear_threshold.abs()
337        && sq_angvel < activation.angular_threshold * activation.angular_threshold.abs()
338    {
339        activation.time_since_can_sleep += dt;
340    } else {
341        activation.time_since_can_sleep = 0.0;
342    }
343}