rapier2d/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/// System that manages which bodies are active (awake) vs sleeping to optimize performance.
10///
11/// ## Sleeping Optimization
12///
13/// Bodies at rest automatically "sleep" - they're excluded from simulation until something
14/// disturbs them (collision, joint connection to moving body, manual wake-up). This can
15/// dramatically improve performance in scenes with many static/resting objects.
16///
17/// ## Islands
18///
19/// Connected bodies (via contacts or joints) are grouped into "islands" that are solved together.
20/// This allows parallel solving and better organization.
21///
22/// You rarely interact with this directly - it's automatically managed by [`PhysicsPipeline`](crate::pipeline::PhysicsPipeline).
23#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
24#[derive(Clone, Default)]
25pub struct IslandManager {
26    pub(crate) active_set: Vec<RigidBodyHandle>,
27    pub(crate) active_islands: Vec<usize>,
28    pub(crate) active_islands_additional_solver_iterations: Vec<usize>,
29    active_set_timestamp: u32,
30    #[cfg_attr(feature = "serde-serialize", serde(skip))]
31    can_sleep: Vec<RigidBodyHandle>, // Workspace.
32    #[cfg_attr(feature = "serde-serialize", serde(skip))]
33    stack: Vec<RigidBodyHandle>, // Workspace.
34}
35
36impl IslandManager {
37    /// Creates a new empty island manager.
38    pub fn new() -> Self {
39        Self {
40            active_set: vec![],
41            active_islands: vec![],
42            active_islands_additional_solver_iterations: vec![],
43            active_set_timestamp: 0,
44            can_sleep: vec![],
45            stack: vec![],
46        }
47    }
48
49    pub(crate) fn num_islands(&self) -> usize {
50        self.active_islands.len().saturating_sub(1)
51    }
52
53    /// Update this data-structure after one or multiple rigid-bodies have been removed for `bodies`.
54    pub fn cleanup_removed_rigid_bodies(&mut self, bodies: &mut RigidBodySet) {
55        let mut i = 0;
56
57        while i < self.active_set.len() {
58            let handle = self.active_set[i];
59            if bodies.get(handle).is_none() {
60                // This rigid-body no longer exists, so we need to remove it from the active set.
61                self.active_set.swap_remove(i);
62
63                if i < self.active_set.len() {
64                    // Update the self.active_set_id for the body that has been swapped.
65                    if let Some(swapped_rb) = bodies.get_mut_internal(self.active_set[i]) {
66                        swapped_rb.ids.active_set_id = i;
67                    }
68                }
69            } else {
70                i += 1;
71            }
72        }
73    }
74
75    pub(crate) fn rigid_body_removed(
76        &mut self,
77        removed_handle: RigidBodyHandle,
78        removed_ids: &RigidBodyIds,
79        bodies: &mut RigidBodySet,
80    ) {
81        if self.active_set.get(removed_ids.active_set_id) == Some(&removed_handle) {
82            self.active_set.swap_remove(removed_ids.active_set_id);
83
84            if let Some(replacement) = self
85                .active_set
86                .get(removed_ids.active_set_id)
87                .and_then(|h| bodies.get_mut_internal(*h))
88            {
89                replacement.ids.active_set_id = removed_ids.active_set_id;
90            }
91        }
92    }
93
94    /// Wakes up a sleeping body, forcing it back into the active simulation.
95    ///
96    /// Use this when you want to ensure a body is active (useful after manually moving
97    /// a sleeping body, or to prevent it from sleeping in the next few frames).
98    ///
99    /// # Parameters
100    /// * `strong` - If `true`, the body is guaranteed to stay awake for multiple frames.
101    ///   If `false`, it might sleep again immediately if conditions are met.
102    ///
103    /// # Example
104    /// ```
105    /// # use rapier3d::prelude::*;
106    /// # let mut bodies = RigidBodySet::new();
107    /// # let mut islands = IslandManager::new();
108    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
109    /// islands.wake_up(&mut bodies, body_handle, true);
110    /// let body = bodies.get_mut(body_handle).unwrap();
111    /// // Wake up a body before applying force to it
112    /// body.add_force(vector![100.0, 0.0, 0.0], false);
113    /// ```
114    ///
115    /// Only affects dynamic bodies (kinematic and fixed bodies don't sleep).
116    pub fn wake_up(&mut self, bodies: &mut RigidBodySet, handle: RigidBodyHandle, strong: bool) {
117        // NOTE: the use an Option here because there are many legitimate cases (like when
118        //       deleting a joint attached to an already-removed body) where we could be
119        //       attempting to wake-up a rigid-body that has already been deleted.
120        if bodies.get(handle).map(|rb| rb.body_type()) == Some(RigidBodyType::Dynamic) {
121            let rb = bodies.index_mut_internal(handle);
122
123            // Check that the user didn’t change the sleeping state explicitly, in which
124            // case we don’t overwrite it.
125            if !rb.changes.contains(RigidBodyChanges::SLEEP) {
126                rb.activation.wake_up(strong);
127
128                if rb.is_enabled() && self.active_set.get(rb.ids.active_set_id) != Some(&handle) {
129                    rb.ids.active_set_id = self.active_set.len();
130                    self.active_set.push(handle);
131                }
132            }
133        }
134    }
135
136    pub(crate) fn active_island(&self, island_id: usize) -> &[RigidBodyHandle] {
137        let island_range = self.active_islands[island_id]..self.active_islands[island_id + 1];
138        &self.active_set[island_range]
139    }
140
141    pub(crate) fn active_island_additional_solver_iterations(&self, island_id: usize) -> usize {
142        self.active_islands_additional_solver_iterations[island_id]
143    }
144
145    /// Handls of dynamic and kinematic rigid-bodies that are currently active (i.e. not sleeping).
146    #[inline]
147    pub fn active_bodies(&self) -> &[RigidBodyHandle] {
148        &self.active_set
149    }
150
151    #[cfg(feature = "parallel")]
152    #[allow(dead_code)] // That will likely be useful when we re-introduce intra-island parallelism.
153    pub(crate) fn active_island_range(&self, island_id: usize) -> std::ops::Range<usize> {
154        self.active_islands[island_id]..self.active_islands[island_id + 1]
155    }
156
157    pub(crate) fn update_active_set_with_contacts(
158        &mut self,
159        dt: Real,
160        length_unit: Real,
161        bodies: &mut RigidBodySet,
162        colliders: &ColliderSet,
163        narrow_phase: &NarrowPhase,
164        impulse_joints: &ImpulseJointSet,
165        multibody_joints: &MultibodyJointSet,
166        min_island_size: usize,
167    ) {
168        assert!(
169            min_island_size > 0,
170            "The minimum island size must be at least 1."
171        );
172
173        // Update the energy of every rigid body and
174        // keep only those that may not sleep.
175        //        let t = Instant::now();
176        self.active_set_timestamp += 1;
177        self.stack.clear();
178        self.can_sleep.clear();
179
180        // NOTE: the `.rev()` is here so that two successive timesteps preserve
181        // the order of the bodies in the `active_set` vec. This reversal
182        // does not seem to affect performances nor stability. However it makes
183        // debugging slightly nicer.
184        for h in self.active_set.drain(..).rev() {
185            let can_sleep = &mut self.can_sleep;
186            let stack = &mut self.stack;
187
188            let rb = bodies.index_mut_internal(h);
189            let sq_linvel = rb.vels.linvel.norm_squared();
190            let sq_angvel = rb.vels.angvel.gdot(rb.vels.angvel);
191
192            update_energy(
193                &mut rb.activation,
194                rb.body_type,
195                length_unit,
196                sq_linvel,
197                sq_angvel,
198                dt,
199            );
200
201            if rb.activation.time_since_can_sleep >= rb.activation.time_until_sleep {
202                // Mark them as sleeping for now. This will
203                // be set to false during the graph traversal
204                // if it should not be put to sleep.
205                rb.activation.sleeping = true;
206                can_sleep.push(h);
207            } else {
208                stack.push(h);
209            }
210        }
211
212        // Read all the contacts and push objects touching touching this rigid-body.
213        #[inline]
214        fn push_contacting_bodies(
215            rb_colliders: &RigidBodyColliders,
216            colliders: &ColliderSet,
217            narrow_phase: &NarrowPhase,
218            stack: &mut Vec<RigidBodyHandle>,
219        ) {
220            for collider_handle in &rb_colliders.0 {
221                for inter in narrow_phase.contact_pairs_with(*collider_handle) {
222                    for manifold in &inter.manifolds {
223                        if !manifold.data.solver_contacts.is_empty() {
224                            let other = crate::utils::select_other(
225                                (inter.collider1, inter.collider2),
226                                *collider_handle,
227                            );
228                            if let Some(other_body) = colliders[other].parent {
229                                stack.push(other_body.handle);
230                            }
231                            break;
232                        }
233                    }
234                }
235            }
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
262                || !rb.is_dynamic_or_kinematic()
263            {
264                // We already visited this body and its neighbors.
265                // Also, we don't propagate awake state through fixed bodies.
266                continue;
267            }
268
269            if self.stack.len() < island_marker {
270                if additional_solver_iterations != rb.additional_solver_iterations
271                    || self.active_set.len() - *self.active_islands.last().unwrap()
272                        >= min_island_size
273                {
274                    // We are starting a new island.
275                    self.active_islands_additional_solver_iterations
276                        .push(additional_solver_iterations);
277                    self.active_islands.push(self.active_set.len());
278                    additional_solver_iterations = 0;
279                }
280
281                island_marker = self.stack.len();
282            }
283
284            additional_solver_iterations =
285                additional_solver_iterations.max(rb.additional_solver_iterations);
286
287            // Transmit the active state to all the rigid-bodies with colliders
288            // in contact or joined with this collider.
289            push_contacting_bodies(&rb.colliders, colliders, narrow_phase, &mut self.stack);
290
291            for inter in impulse_joints.attached_enabled_joints(handle) {
292                let other = crate::utils::select_other((inter.0, inter.1), handle);
293                self.stack.push(other);
294            }
295
296            for other in multibody_joints.bodies_attached_with_enabled_joint(handle) {
297                self.stack.push(other);
298            }
299
300            rb.activation.wake_up(false);
301            rb.ids.active_island_id = self.active_islands.len() - 1;
302            rb.ids.active_set_id = self.active_set.len();
303            rb.ids.active_set_offset =
304                (rb.ids.active_set_id - self.active_islands[rb.ids.active_island_id]) as u32;
305            rb.ids.active_set_timestamp = self.active_set_timestamp;
306
307            self.active_set.push(handle);
308        }
309
310        self.active_islands_additional_solver_iterations
311            .push(additional_solver_iterations);
312        self.active_islands.push(self.active_set.len());
313        //        println!(
314        //            "Extraction: {}, num islands: {}",
315        //            Instant::now() - t,
316        //            self.active_islands.len() - 1
317        //        );
318
319        // Actually put to sleep bodies which have not been detected as awake.
320        for handle in &self.can_sleep {
321            let rb = bodies.index_mut_internal(*handle);
322            if rb.activation.sleeping {
323                rb.vels = RigidBodyVelocity::zero();
324                rb.activation.sleep();
325            }
326        }
327    }
328}
329
330fn update_energy(
331    activation: &mut RigidBodyActivation,
332    body_type: RigidBodyType,
333    length_unit: Real,
334    sq_linvel: Real,
335    sq_angvel: Real,
336    dt: Real,
337) {
338    let can_sleep = match body_type {
339        RigidBodyType::Dynamic => {
340            let linear_threshold = activation.normalized_linear_threshold * length_unit;
341            sq_linvel < linear_threshold * linear_threshold.abs()
342                && sq_angvel < activation.angular_threshold * activation.angular_threshold.abs()
343        }
344        RigidBodyType::KinematicPositionBased | RigidBodyType::KinematicVelocityBased => {
345            // Platforms only sleep if both velocities are exactly zero. If it’s not exactly
346            // zero, then the user really wants them to move.
347            sq_linvel == 0.0 && sq_angvel == 0.0
348        }
349        RigidBodyType::Fixed => true,
350    };
351
352    if can_sleep {
353        activation.time_since_can_sleep += dt;
354    } else {
355        activation.time_since_can_sleep = 0.0;
356    }
357}