use super::IslandManager;
use crate::data::union_find::UnionFind;
use crate::dynamics::{ImpulseJointSet, MultibodyJointSet, RigidBodyHandle, RigidBodySet};
use crate::geometry::NarrowPhase;
use alloc::vec::Vec;
use core::ops::Range;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct SolveGroup {
pub body_range: Range<usize>,
pub extra_iters: u32,
}
#[derive(Clone, Default)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct SubstepGroupsWorkspace {
uf: UnionFind,
keys: Vec<u32>,
distinct: Vec<u32>,
offsets: Vec<usize>,
scratch: Vec<RigidBodyHandle>,
}
impl IslandManager {
pub(crate) fn update_substep_groups(
&mut self,
any_extra: bool,
bodies: &mut RigidBodySet,
narrow_phase: &NarrowPhase,
impulse_joints: &ImpulseJointSet,
multibody_joints: &MultibodyJointSet,
) {
self.solve_groups.clear();
if !any_extra {
return;
}
let Some(awake_id) = self.awake_island else {
return;
};
let num_bodies = self.islands[awake_id].bodies.len();
let ws = &mut self.substep_groups_workspace;
ws.uf.reset(num_bodies);
let slot = |handle: RigidBodyHandle| -> Option<u32> {
let rb = bodies.get(handle)?;
(rb.is_dynamic() && !rb.is_sleeping() && rb.ids.active_island_id == awake_id as u32)
.then_some(rb.ids.active_set_id)
};
for pair in narrow_phase.contact_pairs() {
if !pair.has_any_active_contact() {
continue;
}
let Some(manifold) = pair.manifolds.first() else {
continue;
};
if let (Some(h1), Some(h2)) = (manifold.data.rigid_body1, manifold.data.rigid_body2)
&& let (Some(s1), Some(s2)) = (slot(h1), slot(h2))
{
ws.uf.union(s1, s2);
}
}
for (_, joint) in impulse_joints.iter() {
if let (Some(s1), Some(s2)) = (slot(joint.body1), slot(joint.body2)) {
ws.uf.union(s1, s2);
}
}
for (_, link_id, multibody, link) in multibody_joints.iter() {
if let Some(parent) = multibody.link(link.parent_internal_id)
&& let (Some(s1), Some(s2)) = (
slot(parent.rigid_body_handle()),
slot(link.rigid_body_handle()),
)
{
ws.uf.union(s1, s2);
}
let _ = link_id;
}
ws.keys.clear();
ws.keys.resize(num_bodies, 0);
for i in 0..num_bodies {
let handle = self.islands[awake_id].bodies[i];
let extra = bodies[handle].additional_solver_iterations() as u32;
if extra > 0 {
let root = ws.uf.find(i as u32) as usize;
ws.keys[root] = ws.keys[root].max(extra);
}
}
for i in 0..num_bodies {
let root = ws.uf.find(i as u32) as usize;
ws.keys[i] = ws.keys[root];
}
let kinematic_slot = |handle: RigidBodyHandle| -> Option<u32> {
let rb = bodies.get(handle)?;
(!rb.is_dynamic()
&& rb.is_dynamic_or_kinematic()
&& !rb.is_sleeping()
&& rb.ids.active_island_id == awake_id as u32)
.then_some(rb.ids.active_set_id)
};
let lift = |ws: &mut SubstepGroupsWorkspace, h1, h2| {
if let (Some(k), Some(d)) = (kinematic_slot(h1), slot(h2)) {
ws.keys[k as usize] = ws.keys[k as usize].max(ws.keys[d as usize]);
}
};
for pair in narrow_phase.contact_pairs() {
if !pair.has_any_active_contact() {
continue;
}
let Some(manifold) = pair.manifolds.first() else {
continue;
};
if let (Some(h1), Some(h2)) = (manifold.data.rigid_body1, manifold.data.rigid_body2) {
lift(ws, h1, h2);
lift(ws, h2, h1);
}
}
for (_, joint) in impulse_joints.iter() {
lift(ws, joint.body1, joint.body2);
lift(ws, joint.body2, joint.body1);
}
if ws.keys.is_sorted_by(|a, b| a >= b) {
push_group_ranges(&mut self.solve_groups, &ws.keys);
return;
}
ws.distinct.clear();
for &k in &ws.keys {
if !ws.distinct.contains(&k) {
ws.distinct.push(k);
}
}
ws.distinct.sort_unstable_by(|a, b| b.cmp(a));
ws.offsets.clear();
ws.offsets.resize(ws.distinct.len(), 0);
for &k in &ws.keys {
let ord = ws.distinct.iter().position(|&d| d == k).unwrap();
ws.offsets[ord] += 1;
}
let mut start = 0;
for count in &mut ws.offsets {
let c = *count;
*count = start;
start += c;
}
let island_bodies = &mut self.islands[awake_id].bodies;
ws.scratch.clear();
ws.scratch.resize(num_bodies, RigidBodyHandle::invalid());
for (i, &handle) in island_bodies.iter().enumerate() {
let ord = ws.distinct.iter().position(|&d| d == ws.keys[i]).unwrap();
ws.scratch[ws.offsets[ord]] = handle;
ws.offsets[ord] += 1;
}
core::mem::swap(island_bodies, &mut ws.scratch);
for (i, handle) in self.islands[awake_id].bodies.iter().enumerate() {
bodies.index_mut_internal(*handle).ids.active_set_id = i as u32;
}
ws.keys.sort_unstable_by(|a, b| b.cmp(a));
push_group_ranges(&mut self.solve_groups, &ws.keys);
self.bump_active_set_epoch();
}
}
fn push_group_ranges(groups: &mut Vec<SolveGroup>, keys: &[u32]) {
let mut start = 0;
for i in 1..=keys.len() {
if i == keys.len() || keys[i] != keys[start] {
groups.push(SolveGroup {
body_range: start..i,
extra_iters: keys[start],
});
start = i;
}
}
}