use super::IslandManager;
use crate::data::union_find::UnionFind;
use crate::dynamics::{
ImpulseJointSet, MultibodyJointSet, RigidBodyHandle, RigidBodySet, SoftBodySet,
};
use crate::geometry::{ColliderSet, ContactPair, 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,
pub extra_pgs: u32,
}
#[inline]
fn key(extra_substeps: u32, extra_pgs: u32) -> u32 {
(extra_substeps.min(0xFFFF) << 16) | extra_pgs.min(0xFFFF)
}
#[inline]
fn max_key(a: u32, b: u32) -> u32 {
key((a >> 16).max(b >> 16), (a & 0xFFFF).max(b & 0xFFFF))
}
#[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>,
workspace: Vec<RigidBodyHandle>,
}
impl IslandManager {
pub(crate) fn update_substep_groups(
&mut self,
any_extra: bool,
bodies: &mut RigidBodySet,
narrow_phase: &NarrowPhase,
colliders: &ColliderSet,
impulse_joints: &ImpulseJointSet,
multibody_joints: &MultibodyJointSet,
soft_bodies: &SoftBodySet,
) {
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;
}
if let (Some(h1), Some(h2)) = pair_bodies(pair, colliders)
&& 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;
}
for (_, sb) in soft_bodies.iter() {
let Some(root) = slot(sb.root_body()) else {
continue;
};
for attachment in sb.particle_attachments() {
if let Some(s) = slot(attachment.body) {
ws.uf.union(root, s);
}
}
for (_, cluster) in sb.live_clusters() {
if let Some(s) = slot(cluster.proxy()) {
ws.uf.union(root, s);
}
}
}
ws.keys.clear();
ws.keys.resize(num_bodies, 0);
for i in 0..num_bodies {
let handle = self.islands[awake_id].bodies[i];
let rb = &bodies[handle];
let body_key = key(
rb.additional_solver_iterations() as u32,
rb.additional_pgs_iterations() as u32,
);
if body_key > 0 {
let root = ws.uf.find(i as u32) as usize;
ws.keys[root] = max_key(ws.keys[root], body_key);
}
}
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;
}
if let (Some(h1), Some(h2)) = pair_bodies(pair, colliders) {
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.workspace.clear();
ws.workspace.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.workspace[ws.offsets[ord]] = handle;
ws.offsets[ord] += 1;
}
core::mem::swap(island_bodies, &mut ws.workspace);
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 pair_bodies(
pair: &ContactPair,
colliders: &ColliderSet,
) -> (Option<RigidBodyHandle>, Option<RigidBodyHandle>) {
match pair.manifolds().first() {
Some(manifold) => (manifold.data.rigid_body1, manifold.data.rigid_body2),
None => (
colliders.get(pair.collider1).and_then(|c| c.parent()),
colliders.get(pair.collider2).and_then(|c| c.parent()),
),
}
}
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] >> 16,
extra_pgs: keys[start] & 0xFFFF,
});
start = i;
}
}
}