Skip to main content

avian3d/dynamics/solver/islands/
sleeping.rs

1//! Sleeping and waking for [`PhysicsIsland`](super::PhysicsIsland)s.
2//!
3//! See [`IslandSleepingPlugin`].
4
5use bevy::{
6    app::{App, Plugin},
7    ecs::{
8        entity::Entity,
9        entity_disabling::Disabled,
10        error::Result,
11        lifecycle::{Discard, HookContext, Insert},
12        observer::On,
13        query::{Changed, Has, Or, With, Without},
14        resource::Resource,
15        schedule::{
16            IntoScheduleConfigs,
17            common_conditions::{resource_changed, resource_exists},
18        },
19        system::{
20            Command, Commands, Local, ParamSet, Query, Res, ResMut, SystemChangeTick, SystemState,
21            lifetimeless::{SQuery, SResMut},
22        },
23        world::{DeferredWorld, Mut, Ref, World},
24    },
25    prelude::{Deref, DerefMut},
26    time::Time,
27};
28
29use crate::{
30    data_structures::bit_vec::BitVec,
31    dynamics::{
32        joints::joint_graph::JointGraph,
33        solver::{
34            constraint_graph::ConstraintGraph,
35            islands::{BodyIslandNode, IslandId, PhysicsIslands},
36            solver_body::{SolverBodies, SolverBodyIndex},
37        },
38    },
39    prelude::*,
40    schedule::{LastPhysicsTick, is_changed_after_tick},
41};
42
43/// A plugin for managing sleeping and waking of [`PhysicsIsland`](super::PhysicsIsland)s.
44pub struct IslandSleepingPlugin;
45
46impl Plugin for IslandSleepingPlugin {
47    fn build(&self, app: &mut App) {
48        app.init_resource::<AwakeIslandBitVec>();
49        app.init_resource::<TimeToSleep>();
50
51        // Insert `SleepThreshold` and `SleepTimer` for each body that has a solver body.
52        app.register_required_components::<SolverBodyIndex, SleepThreshold>();
53        app.register_required_components::<SolverBodyIndex, SleepTimer>();
54
55        // Set up cached system states for sleeping and waking bodies or islands.
56        let cached_system_state1 = CachedBodySleepingSystemState(SystemState::new(app.world_mut()));
57        let cached_system_state2 =
58            CachedIslandSleepingSystemState(SystemState::new(app.world_mut()));
59        let cached_system_state3 = CachedIslandWakingSystemState(SystemState::new(app.world_mut()));
60        app.insert_resource(cached_system_state1);
61        app.insert_resource(cached_system_state2);
62        app.insert_resource(cached_system_state3);
63
64        // Set up hooks to automatically sleep/wake islands when `Sleeping` is added/removed.
65        app.world_mut()
66            .register_component_hooks::<Sleeping>()
67            .on_add(sleep_on_add_sleeping)
68            .on_remove(wake_on_remove_sleeping);
69
70        app.add_observer(wake_on_replace_rigid_body);
71        app.add_observer(wake_on_enable_rigid_body);
72
73        app.add_systems(
74            PhysicsSchedule,
75            (
76                update_sleeping_states,
77                wake_islands_with_sleeping_disabled,
78                wake_on_changed,
79                wake_all_islands.run_if(resource_changed::<Gravity>),
80                sleep_islands,
81            )
82                .chain()
83                .run_if(resource_exists::<PhysicsIslands>)
84                .in_set(PhysicsStepSystems::Sleeping),
85        );
86    }
87}
88
89fn sleep_on_add_sleeping(mut world: DeferredWorld, ctx: HookContext) {
90    let Some(body_island) = world.get::<BodyIslandNode>(ctx.entity) else {
91        return;
92    };
93
94    let island_id = body_island.island_id;
95
96    // Check if the island is already sleeping.
97    if let Some(island) = world
98        .get_resource::<PhysicsIslands>()
99        .and_then(|islands| islands.get(island_id))
100        && island.is_sleeping
101    {
102        return;
103    }
104
105    world.commands().queue_silenced(SleepBody(ctx.entity));
106}
107
108fn wake_on_remove_sleeping(mut world: DeferredWorld, ctx: HookContext) {
109    let Some(body_island) = world.get::<BodyIslandNode>(ctx.entity) else {
110        return;
111    };
112
113    let island_id = body_island.island_id;
114
115    // Check if the island is already awake.
116    if let Some(island) = world
117        .get_resource::<PhysicsIslands>()
118        .and_then(|islands| islands.get(island_id))
119        && !island.is_sleeping
120    {
121        return;
122    }
123
124    world.commands().queue_silenced(WakeBody(ctx.entity));
125}
126
127fn wake_on_replace_rigid_body(
128    trigger: On<Discard<RigidBody>>,
129    mut commands: Commands,
130    query: Query<&BodyIslandNode>,
131) {
132    let Ok(body_island) = query.get(trigger.entity) else {
133        return;
134    };
135
136    commands.queue(WakeIslands(vec![body_island.island_id]));
137}
138
139fn wake_on_enable_rigid_body(
140    trigger: On<Insert<BodyIslandNode>>,
141    mut commands: Commands,
142    mut query: Query<
143        (&BodyIslandNode, &mut SleepTimer, Has<Sleeping>),
144        Or<(With<Disabled>, Without<Disabled>)>,
145    >,
146) {
147    let Ok((body_island, mut sleep_timer, is_sleeping)) = query.get_mut(trigger.entity) else {
148        return;
149    };
150
151    if is_sleeping {
152        commands.entity(trigger.entity).try_remove::<Sleeping>();
153    }
154
155    // Reset the sleep timer and wake up the island.
156    if sleep_timer.0 > 0.0 {
157        sleep_timer.0 = 0.0;
158        commands.queue(WakeIslands(vec![body_island.island_id]));
159    }
160}
161
162/// A bit vector that stores which islands are kept awake and which are allowed to sleep.
163#[derive(Resource, Default, Deref, DerefMut)]
164pub(crate) struct AwakeIslandBitVec(pub(crate) BitVec);
165
166fn wake_islands_with_sleeping_disabled(
167    mut awake_island_bit_vec: ResMut<AwakeIslandBitVec>,
168    mut query: Query<
169        (&BodyIslandNode, &mut SleepTimer),
170        Or<(
171            With<SleepingDisabled>,
172            With<Disabled>,
173            With<RigidBodyDisabled>,
174        )>,
175    >,
176) {
177    // Wake up all islands that have a body with `SleepingDisabled`.
178    for (body_island, mut sleep_timer) in &mut query {
179        awake_island_bit_vec.set_and_grow(body_island.island_id.0 as usize);
180
181        // Reset the sleep timer.
182        sleep_timer.0 = 0.0;
183    }
184}
185
186fn update_sleeping_states(
187    mut awake_island_bit_vec: ResMut<AwakeIslandBitVec>,
188    mut islands: ResMut<PhysicsIslands>,
189    solver_bodies: Res<SolverBodies>,
190    mut query: Query<
191        (
192            &mut SleepTimer,
193            &SleepThreshold,
194            &SolverBodyIndex,
195            &BodyIslandNode,
196        ),
197        (Without<Sleeping>, Without<SleepingDisabled>),
198    >,
199    length_unit: Res<PhysicsLengthUnit>,
200    time_to_sleep: Res<TimeToSleep>,
201    time: Res<Time>,
202) {
203    let length_unit_squared = length_unit.0 * length_unit.0;
204    let delta_secs = time.delta_secs();
205
206    islands.split_candidate_sleep_timer = 0.0;
207
208    // TODO: This would be nice to do in parallel.
209    for (mut sleep_timer, sleep_threshold, index, island_data) in query.iter_mut() {
210        let Some(solver_body) = solver_bodies.get(*index) else {
211            continue;
212        };
213        let lin_vel_squared = solver_body.linear_velocity.length_squared();
214        #[cfg(feature = "2d")]
215        let ang_vel_squared = solver_body.angular_velocity * solver_body.angular_velocity;
216        #[cfg(feature = "3d")]
217        let ang_vel_squared = solver_body.angular_velocity.length_squared();
218
219        // Keep signs.
220        let lin_threshold_squared = sleep_threshold.linear * sleep_threshold.linear.abs();
221        let ang_threshold_squared = sleep_threshold.angular * sleep_threshold.angular.abs();
222
223        if lin_vel_squared < length_unit_squared * lin_threshold_squared
224            && ang_vel_squared < ang_threshold_squared
225        {
226            // Increment the sleep timer.
227            sleep_timer.0 += delta_secs;
228        } else {
229            // Reset the sleep timer if the body is moving.
230            sleep_timer.0 = 0.0;
231        }
232
233        if sleep_timer.0 < time_to_sleep.0 {
234            // Keep the island awake.
235            awake_island_bit_vec.set_and_grow(island_data.island_id.0 as usize);
236        } else if let Some(island) = islands.get(island_data.island_id)
237            && island.constraints_removed > 0
238        {
239            // The body wants to sleep, but its island needs splitting first.
240            if sleep_timer.0 > islands.split_candidate_sleep_timer {
241                // This island is now the sleepiest candidate for splitting.
242                islands.split_candidate = Some(island_data.island_id);
243                islands.split_candidate_sleep_timer = sleep_timer.0;
244            }
245        }
246    }
247}
248
249fn sleep_islands(
250    mut awake_island_bit_vec: ResMut<AwakeIslandBitVec>,
251    mut islands: ResMut<PhysicsIslands>,
252    mut commands: Commands,
253    mut sleep_buffer: Local<Vec<IslandId>>,
254    mut wake_buffer: Local<Vec<IslandId>>,
255) {
256    // Clear the buffers.
257    sleep_buffer.clear();
258    wake_buffer.clear();
259
260    // Sleep islands that are not in the awake bit vector.
261    for island in islands.iter_mut() {
262        if awake_island_bit_vec.get(island.id.0 as usize) {
263            if island.is_sleeping {
264                wake_buffer.push(island.id);
265            }
266        } else if !island.is_sleeping && island.constraints_removed == 0 {
267            // The island does not have a pending split, so it can go to sleep.
268            sleep_buffer.push(island.id);
269        }
270    }
271
272    // Sleep islands.
273    if !sleep_buffer.is_empty() {
274        let sleep_buffer = sleep_buffer.clone();
275        commands.queue(|world: &mut World| {
276            SleepIslands(sleep_buffer).apply(world);
277        });
278    }
279
280    // Wake islands.
281    if !wake_buffer.is_empty() {
282        let wake_buffer = wake_buffer.clone();
283        commands.queue(|world: &mut World| {
284            WakeIslands(wake_buffer).apply(world);
285        });
286    }
287
288    // Reset the awake island bit vector.
289    awake_island_bit_vec.set_bit_count_and_clear(islands.len());
290}
291
292#[derive(Resource)]
293struct CachedBodySleepingSystemState(
294    SystemState<(
295        SQuery<&'static mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
296        SQuery<&'static RigidBodyColliders>,
297        SResMut<PhysicsIslands>,
298        SResMut<ContactGraph>,
299        SResMut<JointGraph>,
300    )>,
301);
302
303/// A [`Command`] that forces a [`RigidBody`] and its [`PhysicsIsland`][super::PhysicsIsland] to be [`Sleeping`].
304pub struct SleepBody(pub Entity);
305
306impl Command for SleepBody {
307    type Out = Result;
308    fn apply(self, world: &mut World) -> Result {
309        if let Ok(entity) = world.get_entity(self.0) {
310            if let Some(island_id) = entity.get::<BodyIslandNode>().map(|node| node.island_id) {
311                world.try_resource_scope(|world, mut state: Mut<CachedBodySleepingSystemState>| {
312                    let (
313                        mut body_islands,
314                        body_colliders,
315                        mut islands,
316                        contact_graph,
317                        joint_graph,
318                    ) = state.0.get_mut(world).unwrap();
319
320                    let Some(island) = islands.get_mut(island_id) else {
321                        return;
322                    };
323
324                    // The island must be split before it can be woken up.
325                    // Note that this is expensive.
326                    if island.constraints_removed > 0 {
327                        islands.split_island(
328                            island_id,
329                            &mut body_islands,
330                            &body_colliders,
331                            &contact_graph,
332                            &joint_graph,
333                        );
334                    }
335
336                    // The ID of the body's island might have changed due to the split,
337                    // so we need to retrieve it again.
338                    let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
339
340                    // Sleep the island.
341                    SleepIslands(vec![island_id]).apply(world);
342                });
343                Ok(())
344            } else {
345                Err(format!(
346                    "Tried to sleep entity {:?} that is not a body or does not belong to an island",
347                    self.0
348                )
349                .into())
350            }
351        } else {
352            Err(format!("Tried to sleep entity {:?} that does not exist", self.0).into())
353        }
354    }
355}
356
357#[derive(Resource)]
358struct CachedIslandSleepingSystemState(
359    SystemState<(
360        SQuery<(
361            &'static BodyIslandNode,
362            &'static mut SleepTimer,
363            Option<&'static RigidBodyColliders>,
364        )>,
365        SResMut<PhysicsIslands>,
366        SResMut<ContactGraph>,
367        SResMut<ConstraintGraph>,
368    )>,
369);
370
371/// A [`Command`] that makes the [`PhysicsIsland`](super::PhysicsIsland)s with the given IDs sleep if they are not already sleeping.
372pub struct SleepIslands(pub Vec<IslandId>);
373
374impl Command for SleepIslands {
375    type Out = ();
376    fn apply(self, world: &mut World) {
377        if self.0.is_empty() {
378            return;
379        }
380        world.try_resource_scope(|world, mut state: Mut<CachedIslandSleepingSystemState>| {
381            let (bodies, mut islands, mut contact_graph, mut constraint_graph) =
382                state.0.get_mut(world).unwrap();
383
384            let mut bodies_to_sleep = Vec::<(Entity, Sleeping)>::new();
385            let mut colliders_to_sleep = Vec::<Entity>::new();
386
387            // First, mark every island in this batch as sleeping and gather their bodies and colliders.
388            for island_id in self.0 {
389                if let Some(island) = islands.get_mut(island_id) {
390                    if island.is_sleeping {
391                        // The island is already sleeping, no need to sleep it again.
392                        continue;
393                    }
394
395                    island.is_sleeping = true;
396
397                    let mut body = island.head_body;
398
399                    while let Some(entity) = body {
400                        let Ok((body_island, _, colliders)) = bodies.get(entity) else {
401                            body = None;
402                            continue;
403                        };
404
405                        if let Some(colliders) = colliders {
406                            colliders_to_sleep.extend(colliders);
407                        }
408
409                        bodies_to_sleep.push((entity, Sleeping));
410                        body = body_island.next;
411                    }
412                }
413            }
414
415            // A pair may only be put to sleep once its other body is also asleep or immovable.
416            let is_other_body_asleep = |other_body: Option<Entity>| -> bool {
417                let Some(other) = other_body else {
418                    return true;
419                };
420                match bodies.get(other) {
421                    Ok((body_island, _, _)) => islands
422                        .get(body_island.island_id)
423                        .is_none_or(|island| island.is_sleeping),
424                    Err(_) => true,
425                }
426            };
427
428            // Transfer the contact pairs to the sleeping set, and remove touching contacts
429            // from the constraint graph.
430            for collider in colliders_to_sleep {
431                contact_graph.sleep_entity_with(
432                    collider,
433                    |_graph, contact_pair| {
434                        // Remove touching contacts from the constraint graph.
435                        if !contact_pair.is_touching() || !contact_pair.generates_constraints() {
436                            return;
437                        }
438                        if let (Some(body1), Some(body2)) = (contact_pair.body1, contact_pair.body2)
439                        {
440                            constraint_graph.remove_contact(contact_pair.contact_id, body1, body2);
441                        }
442                    },
443                    is_other_body_asleep,
444                );
445            }
446
447            // Batch insert `Sleeping` to the bodies.
448            world.insert_batch(bodies_to_sleep);
449        });
450    }
451}
452
453#[derive(Resource)]
454struct CachedIslandWakingSystemState(
455    SystemState<(
456        SQuery<(
457            &'static BodyIslandNode,
458            &'static mut SleepTimer,
459            Option<&'static RigidBodyColliders>,
460        )>,
461        SResMut<PhysicsIslands>,
462        SResMut<ContactGraph>,
463        SResMut<ConstraintGraph>,
464    )>,
465);
466
467/// A [`Command`] that wakes up a [`RigidBody`] and its [`PhysicsIsland`](super::PhysicsIsland) if it is [`Sleeping`].
468pub struct WakeBody(pub Entity);
469
470impl Command for WakeBody {
471    type Out = Result;
472    fn apply(self, world: &mut World) -> Result {
473        if let Ok(entity) = world.get_entity(self.0) {
474            if let Some(body_island) = entity.get::<BodyIslandNode>() {
475                WakeIslands(vec![body_island.island_id]).apply(world);
476                Ok(())
477            } else {
478                Err(format!(
479                    "Tried to wake entity {:?} that is not a body or does not belong to an island",
480                    self.0
481                )
482                .into())
483            }
484        } else {
485            Err(format!("Tried to wake entity {:?} that does not exist", self.0).into())
486        }
487    }
488}
489
490/// A [`Command`] that wakes up the [`PhysicsIsland`](super::PhysicsIsland)s with the given IDs if they are sleeping.
491pub struct WakeIslands(pub Vec<IslandId>);
492
493impl Command for WakeIslands {
494    type Out = ();
495    fn apply(self, world: &mut World) {
496        if self.0.is_empty() {
497            return;
498        }
499        world.try_resource_scope(|world, mut state: Mut<CachedIslandWakingSystemState>| {
500            let (mut bodies, mut islands, mut contact_graph, mut constraint_graph) =
501                state.0.get_mut(world).unwrap();
502
503            let mut bodies_to_wake = Vec::<Entity>::new();
504
505            for island_id in self.0 {
506                if let Some(island) = islands.get_mut(island_id) {
507                    if !island.is_sleeping {
508                        // The island is not sleeping, no need to wake it up.
509                        continue;
510                    }
511
512                    island.is_sleeping = false;
513
514                    let mut body = island.head_body;
515
516                    while let Some(entity) = body {
517                        let Ok((body_island, mut sleep_timer, colliders)) = bodies.get_mut(entity)
518                        else {
519                            body = None;
520                            continue;
521                        };
522
523                        // Transfer the contact pairs to the awake set, and add touching contacts to the constraint graph.
524                        if let Some(colliders) = colliders {
525                            for collider in colliders {
526                                contact_graph.wake_entity_with(collider, |_graph, contact_pair| {
527                                    // Add touching contacts to the constraint graph.
528                                    if !contact_pair.is_touching()
529                                        || !contact_pair.generates_constraints()
530                                    {
531                                        return;
532                                    }
533                                    for _ in contact_pair.manifolds.iter() {
534                                        constraint_graph.push_manifold(contact_pair);
535                                    }
536                                });
537                            }
538                        }
539
540                        bodies_to_wake.push(entity);
541                        body = body_island.next;
542                        sleep_timer.0 = 0.0;
543                    }
544                }
545            }
546
547            // Remove `Sleeping` from the bodies.
548            bodies_to_wake.into_iter().for_each(|entity| {
549                world.entity_mut(entity).remove::<Sleeping>();
550            });
551        });
552    }
553}
554
555#[cfg(feature = "2d")]
556type ConstantForceChanges = Or<(
557    Changed<ConstantForce>,
558    Changed<ConstantTorque>,
559    Changed<ConstantLinearAcceleration>,
560    Changed<ConstantAngularAcceleration>,
561    Changed<ConstantLocalForce>,
562    Changed<ConstantLocalLinearAcceleration>,
563)>;
564#[cfg(feature = "3d")]
565type ConstantForceChanges = Or<(
566    Changed<ConstantForce>,
567    Changed<ConstantTorque>,
568    Changed<ConstantLinearAcceleration>,
569    Changed<ConstantAngularAcceleration>,
570    Changed<ConstantLocalForce>,
571    Changed<ConstantLocalTorque>,
572    Changed<ConstantLocalLinearAcceleration>,
573    Changed<ConstantLocalAngularAcceleration>,
574)>;
575
576/// Removes the [`Sleeping`] component from sleeping bodies when properties like
577/// position, rotation, velocity and external forces are changed by the user.
578fn wake_on_changed(
579    mut query: ParamSet<(
580        // These could've been changed by physics too.
581        // We need to ignore non-user changes.
582        Query<
583            (
584                Ref<Position>,
585                Ref<Rotation>,
586                Ref<LinearVelocity>,
587                Ref<AngularVelocity>,
588                Ref<SleepTimer>,
589                &BodyIslandNode,
590            ),
591            (
592                With<Sleeping>,
593                Or<(
594                    Changed<Position>,
595                    Changed<Rotation>,
596                    Changed<LinearVelocity>,
597                    Changed<AngularVelocity>,
598                    Changed<SleepTimer>,
599                )>,
600            ),
601        >,
602        // These are not modified by the physics engine
603        // and don't need special handling.
604        Query<&BodyIslandNode, Or<(ConstantForceChanges, Changed<GravityScale>)>>,
605    )>,
606    mut awake_island_bit_vec: ResMut<AwakeIslandBitVec>,
607    last_physics_tick: Res<LastPhysicsTick>,
608    system_tick: SystemChangeTick,
609) {
610    let this_run = system_tick.this_run();
611
612    for (pos, rot, lin_vel, ang_vel, sleep_timer, body_island) in &query.p0() {
613        if is_changed_after_tick(pos, last_physics_tick.0, this_run)
614            || is_changed_after_tick(rot, last_physics_tick.0, this_run)
615            || is_changed_after_tick(lin_vel, last_physics_tick.0, this_run)
616            || is_changed_after_tick(ang_vel, last_physics_tick.0, this_run)
617            || is_changed_after_tick(sleep_timer, last_physics_tick.0, this_run)
618        {
619            awake_island_bit_vec.set_and_grow(body_island.island_id.0 as usize);
620        }
621    }
622
623    for body_island in &query.p1() {
624        awake_island_bit_vec.set_and_grow(body_island.island_id.0 as usize);
625    }
626}
627
628/// Wakes up all sleeping [`PhysicsIsland`](super::PhysicsIsland)s. Triggered automatically when [`Gravity`] is changed.
629fn wake_all_islands(mut commands: Commands, islands: Res<PhysicsIslands>) {
630    let sleeping_islands: Vec<IslandId> = islands
631        .iter()
632        .filter_map(|island| island.is_sleeping.then_some(island.id))
633        .collect();
634
635    if !sleeping_islands.is_empty() {
636        commands.queue(WakeIslands(sleeping_islands));
637    }
638}