use super::{Island, IslandsOptimizer};
use crate::dynamics::{
ImpulseJointSet, MultibodyJointSet, RigidBodyChanges, RigidBodyHandle, RigidBodyIds,
RigidBodySet,
};
use crate::geometry::{ColliderSet, NarrowPhase};
use crate::math::Real;
use crate::prelude::SleepRootState;
use crate::utils::DotProduct;
use std::collections::VecDeque;
use vec_map::VecMap;
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(super) struct SleepCandidate(RigidBodyHandle);
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone, Default)]
pub struct IslandManager {
pub(crate) islands: VecMap<Island>,
pub(crate) awake_islands: Vec<usize>,
pub(crate) free_islands: Vec<usize>,
pub(super) traversal_candidates: VecDeque<SleepCandidate>,
pub(super) traversal_timestamp: u32,
pub(super) optimizer: IslandsOptimizer,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(super) stack: Vec<RigidBodyHandle>, }
impl IslandManager {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn active_islands(&self) -> &[usize] {
&self.awake_islands
}
pub(crate) fn rigid_body_removed_or_disabled(
&mut self,
removed_handle: RigidBodyHandle,
removed_ids: &RigidBodyIds,
bodies: &mut RigidBodySet,
) {
let Some(island) = self.islands.get_mut(removed_ids.active_island_id) else {
return;
};
if let Some(body) = bodies.get_mut_internal(removed_handle) {
body.ids.active_island_id = usize::MAX;
body.ids.active_set_id = usize::MAX;
}
let swapped_handle = island.bodies.last().copied().unwrap_or(removed_handle);
island.bodies.swap_remove(removed_ids.active_set_id);
if swapped_handle != removed_handle {
let swapped_body = bodies
.get_mut(swapped_handle)
.expect("Internal error: bodies must be removed from islands on at a times");
swapped_body.ids.active_set_id = removed_ids.active_set_id;
}
if island.bodies.is_empty() {
if let Some(awake_id) = island.id_in_awake_list {
self.awake_islands.swap_remove(awake_id);
if let Some(moved_id) = self.awake_islands.get(awake_id) {
self.islands[*moved_id].id_in_awake_list = Some(awake_id);
}
}
self.islands.remove(removed_ids.active_island_id);
self.free_islands.push(removed_ids.active_island_id);
}
}
pub(crate) fn interaction_started_or_stopped(
&mut self,
bodies: &mut RigidBodySet,
handle1: Option<RigidBodyHandle>,
handle2: Option<RigidBodyHandle>,
started: bool,
wake_up: bool,
) {
match (handle1, handle2) {
(Some(handle1), Some(handle2)) => {
if wake_up {
self.wake_up(bodies, handle1, false);
self.wake_up(bodies, handle2, false);
}
if started {
if let (Some(rb1), Some(rb2)) = (bodies.get(handle1), bodies.get(handle2)) {
assert!(rb1.is_fixed() || rb1.ids.active_island_id != usize::MAX);
assert!(rb2.is_fixed() || rb2.ids.active_island_id != usize::MAX);
if !rb1.is_fixed()
&& !rb2.is_fixed()
&& rb1.ids.active_island_id != rb2.ids.active_island_id
{
self.merge_islands(
bodies,
rb1.ids.active_island_id,
rb2.ids.active_island_id,
);
}
}
}
}
(Some(handle1), None) => {
if wake_up {
self.wake_up(bodies, handle1, false);
}
}
(None, Some(handle2)) => {
if wake_up {
self.wake_up(bodies, handle2, false);
}
}
(None, None) => { }
}
}
pub(crate) fn island(&self, island_id: usize) -> &Island {
&self.islands[island_id]
}
#[inline]
pub fn active_bodies(&self) -> impl Iterator<Item = RigidBodyHandle> + '_ {
self.awake_islands
.iter()
.flat_map(|i| self.islands[*i].bodies.iter().copied())
}
pub(crate) fn rigid_body_updated(
&mut self,
handle: RigidBodyHandle,
bodies: &mut RigidBodySet,
) {
let Some(rb) = bodies.get_mut(handle) else {
return;
};
if rb.is_fixed() {
return;
}
if rb.ids.active_island_id == usize::MAX {
let insert_in_last_island = self.awake_islands.last().map(|id| {
self.islands[*id].bodies.len() < self.optimizer.min_island_size
&& self.islands[*id].is_sleeping() == rb.is_sleeping()
});
if !rb.is_sleeping() && insert_in_last_island == Some(true) {
let id = *self.awake_islands.last().unwrap_or_else(|| unreachable!());
let target_island = &mut self.islands[id];
rb.ids.active_island_id = id;
rb.ids.active_set_id = target_island.bodies.len();
target_island.bodies.push(handle);
} else {
let mut new_island = Island::singleton(handle, rb);
let id = self.free_islands.pop().unwrap_or(self.islands.len());
if !rb.is_sleeping() {
new_island.id_in_awake_list = Some(self.awake_islands.len());
self.awake_islands.push(id);
}
self.islands.insert(id, new_island);
rb.ids.active_island_id = id;
rb.ids.active_set_id = 0;
}
}
if (rb.changes.contains(RigidBodyChanges::SLEEP)
|| rb.changes.contains(RigidBodyChanges::TYPE))
&& rb.is_enabled()
&& !rb.activation.sleeping
{
self.wake_up(bodies, handle, false);
}
}
pub(crate) fn update_islands(
&mut self,
dt: Real,
length_unit: Real,
bodies: &mut RigidBodySet,
colliders: &ColliderSet,
narrow_phase: &NarrowPhase,
impulse_joints: &ImpulseJointSet,
multibody_joints: &MultibodyJointSet,
) {
for handle in self
.awake_islands
.iter()
.flat_map(|i| self.islands[*i].bodies.iter().copied())
{
let Some(rb) = bodies.get_mut_internal(handle) else {
continue;
};
let sq_linvel = rb.vels.linvel.length_squared();
let sq_angvel = rb.vels.angvel.gdot(rb.vels.angvel);
rb.activation
.update_energy(rb.body_type, length_unit, sq_linvel, sq_angvel, dt);
let can_sleep_now = rb.activation.is_eligible_for_sleep();
if can_sleep_now && rb.activation.sleep_root_state == SleepRootState::Unknown {
self.traversal_candidates.push_back(SleepCandidate(handle));
rb.activation.sleep_root_state = SleepRootState::TraversalPending;
} else if !can_sleep_now {
rb.activation.sleep_root_state = SleepRootState::Unknown;
}
}
let mut cost = 0;
const MAX_PER_FRAME_COST: usize = 1000; while let Some(sleep_root) = self.traversal_candidates.pop_front() {
cost += self.extract_sleeping_island(
bodies,
colliders,
impulse_joints,
multibody_joints,
narrow_phase,
sleep_root.0,
);
if cost > MAX_PER_FRAME_COST {
break;
}
}
self.update_optimizer(
bodies,
colliders,
impulse_joints,
multibody_joints,
narrow_phase,
);
}
}