use crate::math::sqrt;
use alloc::vec::Vec;
use crate::memory::Pool;
use crate::physics::fanout::Fanout;
use super::body::Body;
use super::broadphase::Pair;
use super::collide::heightfield::{self, FieldPair, Heightfields, Incoming};
use super::collide::{self, Pose};
use super::contact::Manifold;
const MIN_FANOUT_PAIRS: usize = 96;
#[derive(Debug, Default)]
struct Share {
from: usize,
to: usize,
out: Vec<Manifold>,
}
pub(crate) struct Narrow {
shares: Vec<Share>,
}
impl Narrow {
pub(crate) fn new() -> Self {
Narrow { shares: Vec::new() }
}
pub(crate) fn reserve_workers(&mut self, workers: usize, capacity: usize) {
self.shares.clear();
if workers < 2 {
return;
}
let share = (capacity * 2).div_ceil(workers);
self.shares.resize_with(workers, || Share {
from: 0,
to: 0,
out: Vec::with_capacity(share),
});
}
pub(crate) fn reserved_bytes(&self) -> u64 {
self.shares
.iter()
.map(|share| (share.out.capacity() * size_of::<Manifold>()) as u64)
.sum()
}
pub(crate) fn build(&mut self, work: Work<'_>, fanout: &impl Fanout, workers: usize) {
let Work {
bodies,
fields,
pairs,
previous,
out,
margin,
} = work;
let workers = workers.min(self.shares.len());
if workers < 2 || pairs.len() < MIN_FANOUT_PAIRS {
build_range(bodies, fields, pairs, previous, out, margin);
return;
}
let share = pairs.len().div_ceil(workers);
let shares = &mut self.shares[..workers];
for (index, slot) in shares.iter_mut().enumerate() {
slot.from = (index * share).min(pairs.len());
slot.to = ((index + 1) * share).min(pairs.len());
}
fanout.for_each(shares, |slot| {
slot.out.clear();
build_range(
bodies,
fields,
&pairs[slot.from..slot.to],
previous,
&mut slot.out,
margin,
);
});
for slot in shares.iter() {
out.extend_from_slice(&slot.out);
}
}
}
pub(crate) struct Work<'a> {
pub(crate) bodies: &'a Pool<Body>,
pub(crate) fields: &'a Heightfields,
pub(crate) pairs: &'a [Pair],
pub(crate) previous: &'a [Manifold],
pub(crate) out: &'a mut Vec<Manifold>,
pub(crate) margin: f32,
}
fn build_range(
bodies: &Pool<Body>,
fields: &Heightfields,
pairs: &[Pair],
previous: &[Manifold],
out: &mut Vec<Manifold>,
margin: f32,
) {
let mut scratch = Manifold::new(0, 0);
for &(a, b) in pairs {
let (Some(body_a), Some(body_b)) = (bodies.get_at(a as usize), bodies.get_at(b as usize))
else {
continue;
};
if !body_a.is_simulated() && !body_b.is_simulated() {
out.extend_from_slice(super::contact::find(previous, (a, b)));
continue;
}
let pair = FieldPair {
a,
b,
reversed: false,
friction: sqrt(body_a.friction * body_b.friction),
restitution: body_a.restitution.max(body_b.restitution),
};
match (body_a.terrain_index(), body_b.terrain_index()) {
(Some(_), Some(_)) => {}
(Some(index), None) => {
collide_field(fields, index, body_b, margin, pair, out);
}
(None, Some(index)) => {
collide_field(
fields,
index,
body_a,
margin,
FieldPair {
reversed: true,
..pair
},
out,
);
}
(None, None) => {
let (Some(shape_a), Some(shape_b)) = (body_a.convex(), body_b.convex()) else {
continue;
};
scratch.a = a;
scratch.b = b;
if !collide::collide(
shape_a,
pose_of(body_a),
shape_b,
pose_of(body_b),
margin,
&mut scratch,
) {
continue;
}
scratch.friction = pair.friction;
scratch.restitution = pair.restitution;
out.push(scratch);
}
}
}
}
fn collide_field(
fields: &Heightfields,
index: u32,
body: &Body,
margin: f32,
pair: FieldPair,
out: &mut Vec<Manifold>,
) {
let Some(shape) = body.convex() else {
return;
};
heightfield::collide_into(
fields,
index,
Incoming {
shape,
pose: pose_of(body),
bounds: body.tight_bounds().expanded(margin),
},
margin,
pair,
out,
);
}
fn pose_of(body: &Body) -> Pose {
Pose {
position: body.position,
rotation: body.orientation,
}
}