1use 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
41pub 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 app.register_required_components::<SolverBody, SleepThreshold>();
51 app.register_required_components::<SolverBody, SleepTimer>();
52
53 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 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 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 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 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#[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 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 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 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 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 sleep_timer.0 += delta_secs;
222 } else {
223 sleep_timer.0 = 0.0;
225 }
226
227 if sleep_timer.0 < time_to_sleep.0 {
228 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 if sleep_timer.0 > islands.split_candidate_sleep_timer {
235 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 sleep_buffer.clear();
252 wake_buffer.clear();
253
254 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 sleep_buffer.push(island.id);
263 }
264 }
265
266 let sleep_buffer = sleep_buffer.clone();
268 commands.queue(|world: &mut World| {
269 SleepIslands(sleep_buffer).apply(world);
270 });
271
272 let wake_buffer = wake_buffer.clone();
274 commands.queue(|world: &mut World| {
275 WakeIslands(wake_buffer).apply(world);
276 });
277
278 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
293pub 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 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 let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
329
330 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
361pub 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 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 if let Some(colliders) = colliders {
392 for collider in colliders {
393 contact_graph.sleep_entity_with(collider, |graph, contact_pair| {
394 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 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
450pub 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
473pub 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 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 if let Some(colliders) = colliders {
505 for collider in colliders {
506 contact_graph.wake_entity_with(collider, |graph, contact_pair| {
507 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 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
564fn wake_on_changed(
567 mut query: ParamSet<(
568 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 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
616fn 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}