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