Skip to main content

rapier2d/dynamics/island_manager/
sleep.rs

1use crate::alloc_prelude::*;
2use crate::dynamics::{RigidBodyHandle, RigidBodySet};
3use crate::geometry::NarrowPhase;
4
5use super::{Island, IslandManager};
6
7impl IslandManager {
8    /// Wakes up a sleeping body, forcing it back into the active simulation.
9    ///
10    /// Waking any body of a sleeping island wakes the **whole island** (its
11    /// entire touching-contact/joint connected component) and resets every
12    /// member's sleep timer.
13    ///
14    /// # Parameters
15    /// * `strong` - If `true`, the body is guaranteed to stay awake for multiple frames.
16    ///   If `false`, it might sleep again immediately if conditions are met.
17    ///
18    /// # Example
19    /// ```
20    /// # use rapier3d::prelude::*;
21    /// # let mut bodies = RigidBodySet::new();
22    /// # let mut islands = IslandManager::new();
23    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
24    /// islands.wake_up(&mut bodies, body_handle, true);
25    /// let body = bodies.get_mut(body_handle).unwrap();
26    /// // Wake up a body before applying force to it
27    /// body.add_force(Vector::new(100.0, 0.0, 0.0), false);
28    /// ```
29    ///
30    /// Only affects dynamic bodies (kinematic and fixed bodies don't sleep).
31    pub fn wake_up(&mut self, bodies: &mut RigidBodySet, handle: RigidBodyHandle, strong: bool) {
32        // NOTE: the use an Option here because there are many legitimate cases (like when
33        //       deleting a joint attached to an already-removed body) where we could be
34        //       attempting to wake-up a rigid-body that has already been deleted.
35        if bodies.get(handle).map(|rb| !rb.is_fixed()) == Some(true) {
36            let rb = bodies.index_mut_internal(handle);
37
38            rb.activation.wake_up(strong);
39            let persistent_id = rb.ids.island_id;
40            let island_to_wake_up = rb.ids.active_island_id;
41
42            // Whole-island wake: waking any body wakes the entire persistent island, with
43            // a *strong* timer reset for every member — `RigidBody::sleep` leaves timers at the
44            // eligibility threshold, so a freshly woken island would otherwise re-sleep next step.
45            if persistent_id != crate::dynamics::INVALID_ISLAND {
46                let sleeping_island = self
47                    .persistent
48                    .islands
49                    .get(persistent_id as usize)
50                    .is_some_and(|island| island.sleeping);
51                if sleeping_island {
52                    let island = &mut self.persistent.islands[persistent_id as usize];
53                    island.sleeping = false;
54                    // The island's bodies normally share one sleeping-chunk
55                    // container, but joint-merged sleeping islands can span
56                    // several: wake each body's chunk.
57                    let handles = island.bodies.clone();
58                    for h in &handles {
59                        if let Some(rb) = bodies.get_mut(*h) {
60                            rb.activation.wake_up(true);
61                        }
62                    }
63                    for h in handles {
64                        let chunk = match bodies.get(h) {
65                            Some(rb) => rb.ids.active_island_id,
66                            None => continue,
67                        };
68                        self.wake_up_island(bodies, chunk as usize);
69                    }
70                    return;
71                }
72            }
73
74            self.wake_up_island(bodies, island_to_wake_up as usize);
75        }
76    }
77
78    /// Puts `chunks` (disjoint subsets of the awake island's bodies, all
79    /// sleep-eligible) to sleep: in place if they cover the entire awake
80    /// island, otherwise by extracting each chunk into a new sleeping island.
81    pub(super) fn commit_sleeping_chunks(
82        &mut self,
83        bodies: &mut RigidBodySet,
84        narrow_phase: &mut NarrowPhase,
85        active_island_id: usize,
86        active_island_len: usize,
87        mut chunks: Vec<Vec<RigidBodyHandle>>,
88    ) {
89        if chunks.len() == 1 && chunks[0].len() == active_island_len {
90            // The whole island is asleep. No need to insert a new one.
91            // Put all its bodies to sleep.
92            let active_island = &mut self.islands[active_island_id];
93            for handle in &active_island.bodies {
94                bodies.index_mut_internal(*handle).sleep();
95            }
96
97            for handle in &active_island.bodies {
98                let rb = &bodies[*handle];
99                for co_handle in rb.colliders.0.iter().copied() {
100                    narrow_phase.clear_asleep_pair_solver_hint_counts_of(co_handle);
101                }
102            }
103
104            // Membership changed (the whole island leaves the active set): bump the epoch so
105            // epoch-keyed caches can't go stale. The hint count-clears above only cover bodies
106            // WITH colliders — collider-less (joint-only) bodies would otherwise sleep without invalidating e.g. the cached body qualification table.
107            self.active_set_epoch = self.active_set_epoch.wrapping_add(1);
108
109            // Mark the island as sleeping: no island is awake anymore.
110            debug_assert_eq!(self.awake_island, Some(active_island_id));
111            self.awake_island = None;
112        } else {
113            let slept: Vec<RigidBodyHandle> = chunks.iter().flatten().copied().collect();
114
115            for chunk in &mut chunks {
116                let new_island = Island {
117                    bodies: core::mem::take(chunk),
118                };
119                self.extract_sleeping_sub_island(bodies, active_island_id, new_island);
120            }
121
122            // Clear hints after the extractions (which flag the bodies as
123            // sleeping).
124            for handle in &slept {
125                let rb = &bodies[*handle];
126                for co_handle in rb.colliders.0.iter().copied() {
127                    narrow_phase.clear_asleep_pair_solver_hint_counts_of(co_handle);
128                }
129            }
130        }
131    }
132
133    pub(super) fn wake_up_island(&mut self, bodies: &mut RigidBodySet, island_id: usize) {
134        if self.awake_island == Some(island_id) {
135            // Already awake.
136            return;
137        }
138
139        let Some(island) = self.islands.get_mut(island_id) else {
140            return;
141        };
142
143        match self.awake_island {
144            None => {
145                // Nothing is awake: this chunk becomes the awake island. No renumbering (bodies
146                // keep their `active_set_id`s), but the active-set *membership* changes, so
147                // epoch-keyed caches (body qualification table, persistent solver graph, solver constraint caches) must not survive — bump the epoch like the merge branch. (Direct field bump: `island` still borrows `self.islands`.)
148                self.active_set_epoch = self.active_set_epoch.wrapping_add(1);
149                self.awake_island = Some(island_id);
150
151                for handle in &island.bodies {
152                    if let Some(rb) = bodies.get_mut(*handle) {
153                        rb.wake_up(false);
154                    }
155                }
156            }
157            Some(awake_id) => {
158                // Merge the chunk's bodies into the single awake island.
159                self.bump_active_set_epoch();
160                let Some(removed) = self.islands.remove(island_id) else {
161                    unreachable!()
162                };
163                self.free_islands.push(island_id);
164
165                let target = &mut self.islands[awake_id];
166                for handle in &removed.bodies {
167                    let Some(rb) = bodies.get_mut(*handle) else {
168                        // This body no longer exists.
169                        continue;
170                    };
171                    rb.wake_up(false);
172                    rb.ids.active_island_id = awake_id as u32;
173                    rb.ids.active_set_id = (target.bodies.len()) as u32;
174                    target.bodies.push(*handle);
175                }
176            }
177        }
178    }
179}