use std::sync::mpsc::channel;
use bevy::prelude::*;
use physx::prelude::*;
use physx::traits::Class;
use physx_sys::{
PxArticulationReducedCoordinate_getSleepThreshold,
PxArticulationReducedCoordinate_getStabilizationThreshold,
PxArticulationReducedCoordinate_getWakeCounter,
PxArticulationReducedCoordinate_isSleeping,
PxArticulationReducedCoordinate_putToSleep_mut,
PxArticulationReducedCoordinate_setSleepThreshold_mut,
PxArticulationReducedCoordinate_setStabilizationThreshold_mut,
PxArticulationReducedCoordinate_setWakeCounter_mut,
PxArticulationReducedCoordinate_wakeUp_mut,
};
use crate::types::OnWakeSleep;
use crate::prelude::{Scene, *};
#[derive(Component, Debug, Default, PartialEq, Eq, Clone, Copy, Hash, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component, Default)]
#[component(storage = "SparseSet")]
pub struct Sleeping;
#[derive(Resource)]
pub(crate) struct WakeSleepCallback(pub(crate) OnWakeSleep);
#[derive(Message)]
pub struct WakeSleepEvent {
pub entities: Vec<Entity>,
pub is_waking: bool,
}
#[derive(Component, Debug, Default, PartialEq, Clone, Copy, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component, Default)]
pub struct SleepControl {
pub is_sleeping: bool,
pub sleep_threshold: f32,
pub stabilization_threshold: f32,
pub wake_counter: f32,
}
impl SleepControl {
pub fn put_to_sleep(&mut self) {
self.is_sleeping = true;
}
pub fn wake_up(&mut self) {
self.is_sleeping = false;
}
}
pub struct SleepPlugin;
impl Plugin for SleepPlugin {
fn build(&self, app: &mut App) {
app.register_type::<Sleeping>();
app.register_type::<SleepControl>();
app.add_systems(PhysicsSchedule, sleep_marker_sync.in_set(PhysicsSet::Create));
app.add_systems(PhysicsSchedule, sleep_control_sync.in_set(PhysicsSet::Sync));
let (wake_sleep_sender, wake_sleep_receiver) = channel();
app.world_mut().insert_resource(WakeSleepCallback(OnWakeSleep::new(move |actors, is_waking| {
let entities = actors.iter().map(|actor| {
actor.cast_map(
|articulation| *articulation.get_user_data(),
|rstatic| *rstatic.get_user_data(),
|rdynamic| *rdynamic.get_user_data(),
)
}).collect::<Vec<_>>();
wake_sleep_sender.send(WakeSleepEvent { entities, is_waking }).unwrap();
})));
app.add_physics_event_channel(wake_sleep_receiver);
}
fn cleanup(&self, app: &mut App) {
assert!(!app.world().contains_resource::<WakeSleepCallback>());
}
}
pub fn sleep_marker_sync(
mut commands: Commands,
mut events: MessageReader<WakeSleepEvent>,
) {
for WakeSleepEvent { entities, is_waking } in events.read() {
for entity in entities.iter() {
let Ok(mut cmd) = commands.get_entity(*entity) else { continue; };
if *is_waking {
cmd.remove::<Sleeping>();
} else {
cmd.insert(Sleeping);
}
}
}
}
pub fn sleep_control_sync(
mut scene: ResMut<Scene>,
mut actors: Query<(
Option<&mut RigidDynamicHandle>,
Option<&mut ArticulationRootHandle>,
&mut SleepControl,
), Or<(Changed<SleepControl>, Without<Sleeping>)>>,
) {
for (dynamic, articulation_base, mut control) in actors.iter_mut() {
if let Some(mut actor) = dynamic {
if control.is_changed() || (actor.is_added() && *control != default()) {
let mut handle = actor.get_mut(&mut scene);
if control.is_sleeping != handle.is_sleeping() {
if control.is_sleeping {
handle.put_to_sleep();
} else {
handle.wake_up();
}
}
handle.set_stabilization_threshold(control.stabilization_threshold);
handle.set_sleep_threshold(control.sleep_threshold);
handle.set_wake_counter(control.wake_counter);
} else {
let handle = actor.get(&scene);
let new_control = SleepControl {
is_sleeping: handle.is_sleeping(),
sleep_threshold: handle.get_sleep_threshold(),
stabilization_threshold: handle.get_stabilization_threshold(),
wake_counter: handle.get_wake_counter(),
};
if *control != new_control { *control = new_control; }
}
} else if let Some(mut root) = articulation_base {
if control.is_changed() || (root.is_added() && *control != default()) {
let mut handle = root.get_mut(&mut scene);
if control.is_sleeping != unsafe { PxArticulationReducedCoordinate_isSleeping(handle.as_ptr()) } {
if control.is_sleeping {
unsafe { PxArticulationReducedCoordinate_putToSleep_mut(handle.as_mut_ptr()) };
} else {
unsafe { PxArticulationReducedCoordinate_wakeUp_mut(handle.as_mut_ptr()) };
}
}
unsafe {
PxArticulationReducedCoordinate_setStabilizationThreshold_mut(handle.as_mut_ptr(), control.stabilization_threshold);
PxArticulationReducedCoordinate_setSleepThreshold_mut(handle.as_mut_ptr(), control.sleep_threshold);
PxArticulationReducedCoordinate_setWakeCounter_mut(handle.as_mut_ptr(), control.wake_counter);
}
} else {
let handle = root.get(&scene);
let new_control = SleepControl {
is_sleeping: unsafe { PxArticulationReducedCoordinate_isSleeping(handle.as_ptr()) },
sleep_threshold: unsafe { PxArticulationReducedCoordinate_getSleepThreshold(handle.as_ptr()) },
stabilization_threshold: unsafe { PxArticulationReducedCoordinate_getStabilizationThreshold(handle.as_ptr()) },
wake_counter: unsafe { PxArticulationReducedCoordinate_getWakeCounter(handle.as_ptr()) },
};
if *control != new_control { *control = new_control; }
}
} else if !control.is_added() {
bevy::log::warn!("SleepControl component exists, but it's neither a rigid dynamic nor articulation root");
}
}
}