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}