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::{
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
43pub 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 app.register_required_components::<SolverBodyIndex, SleepThreshold>();
53 app.register_required_components::<SolverBodyIndex, SleepTimer>();
54
55 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 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 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 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 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#[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 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 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 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 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 sleep_timer.0 += delta_secs;
228 } else {
229 sleep_timer.0 = 0.0;
231 }
232
233 if sleep_timer.0 < time_to_sleep.0 {
234 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 if sleep_timer.0 > islands.split_candidate_sleep_timer {
241 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 sleep_buffer.clear();
258 wake_buffer.clear();
259
260 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 sleep_buffer.push(island.id);
269 }
270 }
271
272 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 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 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
303pub 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 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 let island_id = body_islands.get(self.0).map(|node| node.island_id).unwrap();
339
340 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
371pub 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 for island_id in self.0 {
389 if let Some(island) = islands.get_mut(island_id) {
390 if island.is_sleeping {
391 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 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 for collider in colliders_to_sleep {
431 contact_graph.sleep_entity_with(
432 collider,
433 |_graph, contact_pair| {
434 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 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
467pub 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
490pub 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 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 if let Some(colliders) = colliders {
525 for collider in colliders {
526 contact_graph.wake_entity_with(collider, |_graph, contact_pair| {
527 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 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
576fn wake_on_changed(
579 mut query: ParamSet<(
580 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 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
628fn 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}