use crate::alloc_prelude::*;
use parry::utils::hashmap::HashMap;
use super::soft_constraints_set::SoftConstraintsSet;
use super::soft_contact::SoftContact;
use crate::dynamics::IntegrationParameters;
use crate::dynamics::solver::solver_body::SolverBodies;
use crate::math::Vector;
const MAX_CONTACT_COLORS: usize = 16;
const SPLIT_MIN: usize = 128;
const PIECE_LEN: usize = 32;
const MAX_PARTICLE_COLORS: usize = 16;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct ChunkKey {
soft: [u32; 2],
rigid: u32,
}
impl ChunkKey {
fn of(c: &SoftContact) -> Self {
let (soft, rigid) = if c.other_body != u32::MAX {
(
[
c.support_body.min(c.other_body),
c.support_body.max(c.other_body),
],
u32::MAX,
)
} else if c.element.is_none() && !c.soft_other {
([c.support_body, u32::MAX], c.body)
} else {
([c.support_body, u32::MAX], u32::MAX)
};
Self { soft, rigid }
}
}
#[derive(Default)]
struct ColorSlots {
written: Vec<bool>,
}
impl ColorSlots {
fn clear(&mut self, num_slots: usize) {
self.written.clear();
self.written.resize(num_slots, false);
}
fn accepts(&self, mut slots: impl Iterator<Item = u32>) -> bool {
slots.all(|slot| !self.written[slot as usize])
}
fn insert(&mut self, slots: impl Iterator<Item = u32>) {
for slot in slots {
self.written[slot as usize] = true;
}
}
}
fn written_slots(c: &SoftContact) -> impl Iterator<Item = u32> + '_ {
let anchors = c
.particles
.iter()
.chain(core::iter::once(&c.body))
.chain(c.element.iter().flat_map(|e| e.particles.iter()))
.copied()
.filter(|&slot| slot != u32::MAX);
let fem = c
.fem
.iter()
.flatten()
.flat_map(|side| side.first_slot..side.first_slot + side.num_particles);
anchors.chain(fem)
}
#[derive(Default)]
pub(crate) struct ContactChunkWorkspace {
chunk_of_key: HashMap<ChunkKey, u32>,
constraint_chunk: Vec<u32>,
keys: Vec<ChunkKey>,
counts: Vec<usize>,
cursors: Vec<u32>,
color_slots: Vec<ColorSlots>,
color_chunks: Vec<Vec<usize>>,
ordered: Vec<usize>,
chunk_row_offsets: Vec<u32>,
chunk_rows: Vec<u32>,
slot_masks: Vec<u16>,
}
impl SoftConstraintsSet {
pub(super) fn chunk_contacts(&mut self) {
self.contact_chunk_constraints.clear();
self.contact_chunks.clear();
self.contact_colors.clear();
let mut workspace = core::mem::take(&mut self.contact_workspace);
for gi in 0..self.groups.len() {
let constraints = self.groups[gi].contacts.clone();
let colors_start = self.contact_colors.len();
let chunks_start = self.contact_chunks.len();
if constraints.is_empty() {
let g = &mut self.groups[gi];
g.contact_colors = colors_start..colors_start;
g.contact_serial = chunks_start..chunks_start;
continue;
}
workspace.chunk_of_key.clear();
workspace.keys.clear();
workspace.counts.clear();
workspace.constraint_chunk.clear();
let mut last: Option<(ChunkKey, u32)> = None;
for c in &self.contacts[constraints.clone()] {
let key = ChunkKey::of(c);
let chunk = match last {
Some((k, chunk)) if k == key => chunk,
_ => {
let next = workspace.keys.len() as u32;
let chunk = *workspace.chunk_of_key.entry(key).or_insert(next);
if chunk == next {
workspace.keys.push(key);
workspace.counts.push(0);
}
last = Some((key, chunk));
chunk
}
};
workspace.counts[chunk as usize] += 1;
workspace.constraint_chunk.push(chunk);
}
let mut num_slots = 0usize;
for c in &self.contacts[constraints.clone()] {
for slot in written_slots(c) {
num_slots = num_slots.max(slot as usize + 1);
}
}
let plain_chunks = workspace.keys.len();
for chunk in 0..plain_chunks {
let key = workspace.keys[chunk];
if key.rigid != u32::MAX
|| key.soft[1] != u32::MAX
|| workspace.counts[chunk] < SPLIT_MIN
{
continue;
}
workspace.slot_masks.clear();
workspace.slot_masks.resize(num_slots, 0);
let mut pieces = [(usize::MAX, 0usize); MAX_PARTICLE_COLORS];
for (i, c) in self.contacts[constraints.clone()].iter().enumerate() {
if workspace.constraint_chunk[i] as usize != chunk {
continue;
}
if c.fem.iter().any(|side| side.is_some()) {
continue;
}
let mut mask = 0u16;
for slot in written_slots(c) {
mask |= workspace.slot_masks[slot as usize];
}
let color = mask.trailing_ones() as usize;
if color >= MAX_PARTICLE_COLORS {
continue;
}
for slot in written_slots(c) {
workspace.slot_masks[slot as usize] |= 1 << color;
}
let (piece, fill) = &mut pieces[color];
if *fill == PIECE_LEN || *piece == usize::MAX {
*piece = workspace.keys.len();
*fill = 0;
workspace.keys.push(key);
workspace.counts.push(0);
}
*fill += 1;
workspace.constraint_chunk[i] = *piece as u32;
workspace.counts[*piece] += 1;
workspace.counts[chunk] -= 1;
}
}
let num_chunks = workspace.keys.len();
workspace.chunk_row_offsets.clear();
workspace.chunk_row_offsets.resize(num_chunks + 1, 0);
for &chunk in &workspace.constraint_chunk {
workspace.chunk_row_offsets[chunk as usize + 1] += 1;
}
for chunk in 0..num_chunks {
workspace.chunk_row_offsets[chunk + 1] += workspace.chunk_row_offsets[chunk];
}
workspace.cursors.clear();
workspace
.cursors
.extend_from_slice(&workspace.chunk_row_offsets[..num_chunks]);
workspace.chunk_rows.clear();
workspace.chunk_rows.resize(constraints.len(), 0);
for (i, &chunk) in workspace.constraint_chunk.iter().enumerate() {
let slot = &mut workspace.cursors[chunk as usize];
workspace.chunk_rows[*slot as usize] = i as u32;
*slot += 1;
}
workspace
.color_slots
.resize_with(MAX_CONTACT_COLORS, Default::default);
for slots in &mut workspace.color_slots {
slots.clear(num_slots);
}
for color_chunks in &mut workspace.color_chunks {
color_chunks.clear();
}
workspace
.color_chunks
.resize_with(MAX_CONTACT_COLORS + 1, Default::default);
for chunk in 0..num_chunks {
let rows = &workspace.chunk_rows[workspace.chunk_row_offsets[chunk] as usize
..workspace.chunk_row_offsets[chunk + 1] as usize];
let contacts = &self.contacts[constraints.clone()];
let slots = || {
rows.iter()
.flat_map(|&i| written_slots(&contacts[i as usize]))
};
let color = workspace
.color_slots
.iter()
.position(|color| color.accepts(slots()))
.unwrap_or(MAX_CONTACT_COLORS);
if color < MAX_CONTACT_COLORS {
workspace.color_slots[color].insert(slots());
}
workspace.color_chunks[color].push(chunk);
}
let ordered = &mut workspace.ordered;
ordered.clear();
for color_chunks in &workspace.color_chunks[..MAX_CONTACT_COLORS] {
if color_chunks.len() < 2 {
continue;
}
let start = chunks_start + ordered.len();
ordered.extend_from_slice(color_chunks);
self.contact_colors
.push(start..chunks_start + ordered.len());
}
let serial_start = chunks_start + ordered.len();
for color_chunks in &workspace.color_chunks[..MAX_CONTACT_COLORS] {
if color_chunks.len() == 1 {
ordered.push(color_chunks[0]);
}
}
ordered.extend_from_slice(&workspace.color_chunks[MAX_CONTACT_COLORS]);
debug_assert_eq!(ordered.len(), num_chunks);
let mut cursor = self.contact_chunk_constraints.len();
workspace.cursors.clear();
workspace.cursors.resize(num_chunks, 0);
for &chunk in ordered.iter() {
let count = workspace.counts[chunk];
workspace.cursors[chunk] = cursor as u32;
self.contact_chunks.push(cursor..cursor + count);
cursor += count;
}
self.contact_chunk_constraints.resize(cursor, 0);
for (i, &chunk) in workspace.constraint_chunk.iter().enumerate() {
let slot = &mut workspace.cursors[chunk as usize];
self.contact_chunk_constraints[*slot as usize] = (constraints.start + i) as u32;
*slot += 1;
}
let g = &mut self.groups[gi];
g.contact_colors = colors_start..self.contact_colors.len();
g.contact_serial = serial_start..self.contact_chunks.len();
#[cfg(debug_assertions)]
for color in &self.contact_colors[g.contact_colors.clone()] {
let mut seen = vec![usize::MAX; num_slots];
for chunk in color.clone() {
for &row in &self.contact_chunk_constraints[self.contact_chunks[chunk].clone()]
{
let c = &self.contacts[row as usize];
for slot in written_slots(c) {
let prev = core::mem::replace(&mut seen[slot as usize], chunk);
if prev != usize::MAX && prev != chunk {
let key = |ch: usize| {
workspace.keys[workspace.ordered[ch - chunks_start]]
};
let (k1, k2) = (key(prev), key(chunk));
panic!(
"slot {slot} shared: chunk {prev} (soft {:?} rigid {}) and chunk {chunk} (soft {:?} rigid {}); row body {} element {} soft_other {} fem {:?} particles {:?}",
k1.soft,
k1.rigid,
k2.soft,
k2.rigid,
c.body,
c.element.is_some(),
c.soft_other,
c.fem.iter().map(|f| f.is_some()).collect::<Vec<_>>(),
c.particles
);
}
}
}
}
}
}
self.contact_workspace = workspace;
}
#[inline]
pub fn solve_contact_chunk(
&mut self,
chunk: usize,
bodies: &mut SolverBodies,
params: &IntegrationParameters,
wo_bias: bool,
update: bool,
warmstart: bool,
) {
let Self {
contacts,
contact_chunk_constraints,
contact_chunks,
fem_responses,
..
} = self;
let pool: &[Vector] = fem_responses;
for &constraint in &contact_chunk_constraints[contact_chunks[chunk].clone()] {
let c = &mut contacts[constraint as usize];
if wo_bias && c.soft_other && c.dist0 > 0.0 {
continue;
}
if update {
c.update(bodies, params, wo_bias);
}
if warmstart {
c.warmstart(bodies, pool);
}
c.solve(bodies, pool);
}
}
}