use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use super::functions::{
NodeIdx, aabb_all_pairs_overlap, add3, max3, min3, par_box_box, par_sphere_box,
par_sphere_sphere, scale3, sub3,
};
#[derive(Debug)]
pub struct ParallelUnionFind {
pub(super) parent: Mutex<Vec<usize>>,
pub(super) rank: Mutex<Vec<usize>>,
pub(super) n: usize,
}
impl ParallelUnionFind {
pub fn new(n: usize) -> Self {
Self {
parent: Mutex::new((0..n).collect()),
rank: Mutex::new(vec![0; n]),
n,
}
}
pub fn find(&self, x: usize) -> usize {
let mut parent = self.parent.lock().unwrap_or_else(|e| e.into_inner());
let mut root = x;
while parent[root] != root {
root = parent[root];
}
let mut node = x;
while parent[node] != root {
let next = parent[node];
parent[node] = root;
node = next;
}
root
}
pub fn union(&self, x: usize, y: usize) {
let rx = self.find(x);
let ry = self.find(y);
if rx == ry {
return;
}
let mut parent = self.parent.lock().unwrap_or_else(|e| e.into_inner());
let mut rank = self.rank.lock().unwrap_or_else(|e| e.into_inner());
if rank[rx] < rank[ry] {
parent[rx] = ry;
} else if rank[rx] > rank[ry] {
parent[ry] = rx;
} else {
parent[ry] = rx;
rank[rx] += 1;
}
}
pub fn islands(&self) -> Vec<Vec<usize>> {
let mut map: HashMap<usize, Vec<usize>> = HashMap::new();
for i in 0..self.n {
let root = self.find(i);
map.entry(root).or_default().push(i);
}
map.into_values().collect()
}
pub fn num_components(&self) -> usize {
let mut roots = std::collections::HashSet::new();
for i in 0..self.n {
roots.insert(self.find(i));
}
roots.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ParAabb {
pub min: [f64; 3],
pub max: [f64; 3],
}
impl ParAabb {
pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
Self { min, max }
}
pub fn from_center_half_extents(center: [f64; 3], half: [f64; 3]) -> Self {
Self {
min: sub3(center, half),
max: add3(center, half),
}
}
pub fn merge(&self, other: &Self) -> Self {
Self {
min: min3(self.min, other.min),
max: max3(self.max, other.max),
}
}
pub fn center(&self) -> [f64; 3] {
scale3(add3(self.min, self.max), 0.5)
}
pub fn half_extents(&self) -> [f64; 3] {
scale3(sub3(self.max, self.min), 0.5)
}
pub fn surface_area(&self) -> f64 {
let d = sub3(self.max, self.min);
2.0 * (d[0] * d[1] + d[1] * d[2] + d[2] * d[0])
}
#[inline(always)]
pub fn overlaps(&self, other: &Self) -> bool {
self.min[0] <= other.max[0]
&& self.max[0] >= other.min[0]
&& self.min[1] <= other.max[1]
&& self.max[1] >= other.min[1]
&& self.min[2] <= other.max[2]
&& self.max[2] >= other.min[2]
}
pub fn expanded(&self, margin: f64) -> Self {
Self {
min: [
self.min[0] - margin,
self.min[1] - margin,
self.min[2] - margin,
],
max: [
self.max[0] + margin,
self.max[1] + margin,
self.max[2] + margin,
],
}
}
pub fn contains_point(&self, p: [f64; 3]) -> bool {
p[0] >= self.min[0]
&& p[0] <= self.max[0]
&& p[1] >= self.min[1]
&& p[1] <= self.max[1]
&& p[2] >= self.min[2]
&& p[2] <= self.max[2]
}
}
#[derive(Debug, Clone, Copy)]
pub struct CcdHit {
pub toi: f64,
pub body_a: u32,
pub body_b: u32,
pub normal: [f64; 3],
}
#[derive(Debug, Clone, Copy)]
pub struct GpuAabbBufferDesc {
pub offset_min_x: u32,
pub offset_min_y: u32,
pub offset_min_z: u32,
pub offset_max_x: u32,
pub offset_max_y: u32,
pub offset_max_z: u32,
pub count: u32,
pub stride: u32,
}
impl GpuAabbBufferDesc {
pub fn new_f32_soa(count: u32) -> Self {
let stride = 4u32;
let array_bytes = count * stride;
Self {
offset_min_x: 0,
offset_min_y: array_bytes,
offset_min_z: array_bytes * 2,
offset_max_x: array_bytes * 3,
offset_max_y: array_bytes * 4,
offset_max_z: array_bytes * 5,
count,
stride,
}
}
pub fn total_bytes(&self) -> u32 {
self.count * self.stride * 6
}
pub fn serialize_soa(buf: &SoaAabbBuffer) -> Vec<u8> {
let n = buf.len();
let mut out = Vec::with_capacity(n * 6 * 4);
let write_slice = |dst: &mut Vec<u8>, src: &[f32]| {
for &v in src {
dst.extend_from_slice(&v.to_le_bytes());
}
};
write_slice(&mut out, &buf.min_x);
write_slice(&mut out, &buf.min_y);
write_slice(&mut out, &buf.min_z);
write_slice(&mut out, &buf.max_x);
write_slice(&mut out, &buf.max_y);
write_slice(&mut out, &buf.max_z);
out
}
}
#[derive(Debug, Clone)]
pub(super) struct AabbTreeInner {
pub(super) nodes: Vec<ParAabbNode>,
pub(super) root: NodeIdx,
pub(super) free_list: Vec<NodeIdx>,
}
impl AabbTreeInner {
fn new() -> Self {
Self {
nodes: Vec::new(),
root: u32::MAX,
free_list: Vec::new(),
}
}
fn alloc_node(&mut self) -> NodeIdx {
if let Some(idx) = self.free_list.pop() {
idx
} else {
let idx = self.nodes.len() as NodeIdx;
self.nodes.push(ParAabbNode {
aabb: ParAabb::default(),
left: u32::MAX,
right: u32::MAX,
handle: u32::MAX,
height: 0,
});
idx
}
}
fn insert(&mut self, aabb: ParAabb, handle: u32) -> NodeIdx {
let leaf = self.alloc_node();
self.nodes[leaf as usize].aabb = aabb;
self.nodes[leaf as usize].handle = handle;
self.nodes[leaf as usize].left = u32::MAX;
self.nodes[leaf as usize].right = u32::MAX;
self.nodes[leaf as usize].height = 0;
if self.root == u32::MAX {
self.root = leaf;
return leaf;
}
let mut best = self.root;
loop {
let node = &self.nodes[best as usize];
if node.is_leaf() {
break;
}
let merged_area = node.aabb.merge(&aabb).surface_area();
let left = node.left;
let right = node.right;
let la = self.nodes[left as usize].aabb.merge(&aabb).surface_area();
let ra = self.nodes[right as usize].aabb.merge(&aabb).surface_area();
if merged_area < la.min(ra) {
break;
}
best = if la < ra { left } else { right };
}
let old_parent = self.alloc_node();
let sib_aabb = self.nodes[best as usize].aabb;
self.nodes[old_parent as usize].aabb = sib_aabb.merge(&aabb);
self.nodes[old_parent as usize].left = best;
self.nodes[old_parent as usize].right = leaf;
let sib_h = self.nodes[best as usize].height;
self.nodes[old_parent as usize].height = sib_h + 1;
self.nodes[old_parent as usize].handle = u32::MAX;
if self.root == best {
self.root = old_parent;
}
leaf
}
fn query_overlaps(&self, query: &ParAabb) -> Vec<u32> {
let mut result = Vec::new();
if self.root == u32::MAX {
return result;
}
let mut stack = vec![self.root];
while let Some(idx) = stack.pop() {
let node = &self.nodes[idx as usize];
if !node.aabb.overlaps(query) {
continue;
}
if node.is_leaf() {
result.push(node.handle);
} else {
if node.left != u32::MAX {
stack.push(node.left);
}
if node.right != u32::MAX {
stack.push(node.right);
}
}
}
result
}
}
#[derive(Debug, Clone, Copy)]
pub enum ParShapeKind {
Sphere(ParSphere),
Box(ParBox),
}
#[derive(Debug, Clone, Copy)]
pub struct CcdBodyState {
pub pos0: [f64; 3],
pub pos1: [f64; 3],
pub radius: f64,
pub index: u32,
}
impl CcdBodyState {
pub fn new(pos0: [f64; 3], pos1: [f64; 3], radius: f64, index: u32) -> Self {
Self {
pos0,
pos1,
radius,
index,
}
}
pub fn swept_aabb(&self) -> ParAabb {
let margin = [self.radius; 3];
let mn0 = sub3(self.pos0, margin);
let mx0 = add3(self.pos0, margin);
let mn1 = sub3(self.pos1, margin);
let mx1 = add3(self.pos1, margin);
ParAabb {
min: min3(mn0, mn1),
max: max3(mx0, mx1),
}
}
}
#[derive(Debug, Clone)]
pub struct GpuBvhBuffer {
pub nodes: Vec<GpuBvhNode>,
}
impl GpuBvhBuffer {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn build(aabbs: &[(ParAabb, u32)]) -> Self {
let mut buf = Self::new();
if aabbs.is_empty() {
return buf;
}
Self::build_recursive(&mut buf.nodes, aabbs);
buf
}
fn build_recursive(nodes: &mut Vec<GpuBvhNode>, aabbs: &[(ParAabb, u32)]) -> u32 {
if aabbs.len() == 1 {
let (aabb, handle) = &aabbs[0];
let node = GpuBvhNode::leaf(
[aabb.min[0] as f32, aabb.min[1] as f32, aabb.min[2] as f32],
[aabb.max[0] as f32, aabb.max[1] as f32, aabb.max[2] as f32],
*handle,
);
let idx = nodes.len() as u32;
nodes.push(node);
return idx;
}
let mut combined = aabbs[0].0;
for (a, _) in aabbs.iter().skip(1) {
combined = combined.merge(a);
}
let extent = sub3(combined.max, combined.min);
let axis = if extent[0] >= extent[1] && extent[0] >= extent[2] {
0
} else if extent[1] >= extent[2] {
1
} else {
2
};
let mut sorted: Vec<_> = aabbs.to_vec();
sorted.sort_by(|(a, _), (b, _)| {
let ca = (a.min[axis] + a.max[axis]) * 0.5;
let cb = (b.min[axis] + b.max[axis]) * 0.5;
ca.partial_cmp(&cb).unwrap_or(std::cmp::Ordering::Equal)
});
let mid = sorted.len() / 2;
let my_idx = nodes.len() as u32;
nodes.push(GpuBvhNode::internal([0.0; 3], [0.0; 3], 0, 0));
let left = Self::build_recursive(nodes, &sorted[..mid]);
let right = Self::build_recursive(nodes, &sorted[mid..]);
nodes[my_idx as usize] = GpuBvhNode::internal(
[
combined.min[0] as f32,
combined.min[1] as f32,
combined.min[2] as f32,
],
[
combined.max[0] as f32,
combined.max[1] as f32,
combined.max[2] as f32,
],
left,
right,
);
my_idx
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.nodes.len() * 32);
for node in &self.nodes {
for &v in &node.min {
out.extend_from_slice(&v.to_le_bytes());
}
out.extend_from_slice(&node.left_or_leaf.to_le_bytes());
for &v in &node.max {
out.extend_from_slice(&v.to_le_bytes());
}
out.extend_from_slice(&node.right_or_handle.to_le_bytes());
}
out
}
}
#[derive(Debug, Clone, Copy)]
pub struct ParContact {
pub body_a: u32,
pub body_b: u32,
pub normal: [f64; 3],
pub depth: f64,
pub point: [f64; 3],
}
#[derive(Debug, Clone)]
pub struct ParCollisionResult {
pub contacts: Vec<ParContact>,
pub islands: Vec<Vec<usize>>,
pub ccd_hits: Vec<CcdHit>,
}
#[derive(Debug, Clone, Copy)]
pub struct ParBox {
pub center: [f64; 3],
pub half_extents: [f64; 3],
}
#[derive(Debug, Clone, Copy)]
pub struct ParSphere {
pub center: [f64; 3],
pub radius: f64,
}
#[derive(Debug, Clone)]
pub struct ParAabbNode {
pub aabb: ParAabb,
pub left: NodeIdx,
pub right: NodeIdx,
pub handle: u32,
pub height: i32,
}
impl ParAabbNode {
pub fn is_leaf(&self) -> bool {
self.left == u32::MAX
}
}
#[derive(Debug, Clone)]
pub struct SoaAabbBuffer {
pub min_x: Vec<f32>,
pub min_y: Vec<f32>,
pub min_z: Vec<f32>,
pub max_x: Vec<f32>,
pub max_y: Vec<f32>,
pub max_z: Vec<f32>,
pub handles: Vec<u32>,
}
impl SoaAabbBuffer {
pub fn new() -> Self {
Self {
min_x: Vec::new(),
min_y: Vec::new(),
min_z: Vec::new(),
max_x: Vec::new(),
max_y: Vec::new(),
max_z: Vec::new(),
handles: Vec::new(),
}
}
pub fn push(&mut self, aabb: &ParAabb, handle: u32) {
self.min_x.push(aabb.min[0] as f32);
self.min_y.push(aabb.min[1] as f32);
self.min_z.push(aabb.min[2] as f32);
self.max_x.push(aabb.max[0] as f32);
self.max_y.push(aabb.max[1] as f32);
self.max_z.push(aabb.max[2] as f32);
self.handles.push(handle);
}
pub fn len(&self) -> usize {
self.handles.len()
}
pub fn is_empty(&self) -> bool {
self.handles.is_empty()
}
pub fn query(&self, aabb: &ParAabb) -> Vec<usize> {
let qminx = aabb.min[0] as f32;
let qminy = aabb.min[1] as f32;
let qminz = aabb.min[2] as f32;
let qmaxx = aabb.max[0] as f32;
let qmaxy = aabb.max[1] as f32;
let qmaxz = aabb.max[2] as f32;
let mut result = Vec::new();
let n = self.handles.len();
for i in 0..n {
let hit = (qminx <= self.max_x[i])
& (qmaxx >= self.min_x[i])
& (qminy <= self.max_y[i])
& (qmaxy >= self.min_y[i])
& (qminz <= self.max_z[i])
& (qmaxz >= self.min_z[i]);
if hit {
result.push(i);
}
}
result
}
pub fn rebuild(&mut self, entries: &[(ParAabb, u32)]) {
self.min_x.clear();
self.min_y.clear();
self.min_z.clear();
self.max_x.clear();
self.max_y.clear();
self.max_z.clear();
self.handles.clear();
for (aabb, handle) in entries {
self.push(aabb, *handle);
}
}
}
#[derive(Debug)]
pub struct WorkStealingBroadphase {
pub(super) aabbs: Arc<Vec<ParAabb>>,
pub(super) chunk_size: usize,
pub(super) pending: Mutex<Vec<BroadphaseWorkItem>>,
}
impl WorkStealingBroadphase {
pub fn new(aabbs: Vec<ParAabb>, chunk_size: usize) -> Self {
let pairs = aabb_all_pairs_overlap(&aabbs);
let pending = pairs
.into_iter()
.map(|(a, b)| BroadphaseWorkItem {
body_a: a,
body_b: b,
})
.collect();
Self {
aabbs: Arc::new(aabbs),
chunk_size,
pending: Mutex::new(pending),
}
}
pub fn steal(&self) -> Vec<BroadphaseWorkItem> {
let mut q = self.pending.lock().unwrap_or_else(|e| e.into_inner());
let n = self.chunk_size.min(q.len());
q.drain(..n).collect()
}
pub fn is_done(&self) -> bool {
self.pending
.lock()
.unwrap_or_else(|e| e.into_inner())
.is_empty()
}
pub fn remaining(&self) -> usize {
self.pending.lock().unwrap_or_else(|e| e.into_inner()).len()
}
pub fn aabbs(&self) -> &[ParAabb] {
&self.aabbs
}
}
#[derive(Debug)]
pub struct ParallelAabbTree {
pub(super) inner: RwLock<AabbTreeInner>,
}
impl ParallelAabbTree {
pub fn new() -> Self {
Self {
inner: RwLock::new(AabbTreeInner::new()),
}
}
pub fn insert(&self, aabb: ParAabb, handle: u32) -> NodeIdx {
self.inner
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(aabb, handle)
}
pub fn query(&self, aabb: &ParAabb) -> Vec<u32> {
self.inner
.read()
.unwrap_or_else(|e| e.into_inner())
.query_overlaps(aabb)
}
pub fn node_count(&self) -> usize {
self.inner
.read()
.unwrap_or_else(|e| e.into_inner())
.nodes
.len()
}
}
#[derive(Debug)]
pub struct NarrowphaseBatch {
pub pairs: Vec<(u32, ParShapeKind, u32, ParShapeKind)>,
}
impl NarrowphaseBatch {
pub fn new() -> Self {
Self { pairs: Vec::new() }
}
pub fn push(&mut self, a_idx: u32, a: ParShapeKind, b_idx: u32, b: ParShapeKind) {
self.pairs.push((a_idx, a, b_idx, b));
}
pub fn process(&self) -> Vec<ParContact> {
let mut contacts = Vec::new();
for &(a_idx, ref a_shape, b_idx, ref b_shape) in &self.pairs {
match (a_shape, b_shape) {
(ParShapeKind::Sphere(sa), ParShapeKind::Sphere(sb)) => {
if let Some(c) = par_sphere_sphere(a_idx, b_idx, sa, sb) {
contacts.push(c);
}
}
(ParShapeKind::Sphere(sa), ParShapeKind::Box(bb)) => {
if let Some(c) = par_sphere_box(a_idx, b_idx, sa, bb) {
contacts.push(c);
}
}
(ParShapeKind::Box(ba), ParShapeKind::Sphere(sb)) => {
if let Some(mut c) = par_sphere_box(b_idx, a_idx, sb, ba) {
c.normal = scale3(c.normal, -1.0);
c.body_a = a_idx;
c.body_b = b_idx;
contacts.push(c);
}
}
(ParShapeKind::Box(_ba), ParShapeKind::Box(_bb)) => {
if let (ParShapeKind::Box(ba_), ParShapeKind::Box(bb_)) = (a_shape, b_shape)
&& let Some(c) = par_box_box(a_idx, b_idx, ba_, bb_)
{
contacts.push(c);
}
}
}
}
contacts
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct GpuBvhNode {
pub min: [f32; 3],
pub left_or_leaf: u32,
pub max: [f32; 3],
pub right_or_handle: u32,
}
impl GpuBvhNode {
pub fn leaf(min: [f32; 3], max: [f32; 3], handle: u32) -> Self {
Self {
min,
max,
left_or_leaf: u32::MAX,
right_or_handle: handle,
}
}
pub fn internal(min: [f32; 3], max: [f32; 3], left: u32, right: u32) -> Self {
Self {
min,
max,
left_or_leaf: left,
right_or_handle: right,
}
}
pub fn is_leaf(&self) -> bool {
self.left_or_leaf == u32::MAX
}
}
#[derive(Debug)]
pub struct LockFreeContactBuffer {
pub(super) contacts: Vec<Mutex<Option<ParContact>>>,
pub(super) write_idx: AtomicUsize,
pub(super) capacity: usize,
}
impl LockFreeContactBuffer {
pub fn new(capacity: usize) -> Self {
let mut contacts = Vec::with_capacity(capacity);
for _ in 0..capacity {
contacts.push(Mutex::new(None));
}
Self {
contacts,
write_idx: AtomicUsize::new(0),
capacity,
}
}
pub fn push_contact(&self, contact: ParContact) -> Option<usize> {
let idx = self.write_idx.fetch_add(1, Ordering::AcqRel);
if idx < self.capacity {
*self.contacts[idx].lock().unwrap_or_else(|e| e.into_inner()) = Some(contact);
Some(idx)
} else {
self.write_idx.fetch_sub(1, Ordering::AcqRel);
None
}
}
pub fn len(&self) -> usize {
self.write_idx.load(Ordering::Acquire).min(self.capacity)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn reset(&self) {
let old = self.write_idx.swap(0, Ordering::AcqRel);
for i in 0..old.min(self.capacity) {
*self.contacts[i].lock().unwrap_or_else(|e| e.into_inner()) = None;
}
}
pub fn collect(&self) -> Vec<ParContact> {
let n = self.len();
let mut out = Vec::with_capacity(n);
for i in 0..n {
if let Some(c) = *self.contacts[i].lock().unwrap_or_else(|e| e.into_inner()) {
out.push(c);
}
}
out
}
}
#[derive(Debug, Clone)]
pub struct ParCollisionConfig {
pub max_contacts_per_pair: usize,
pub aabb_margin: f64,
pub enable_ccd: bool,
}
#[derive(Debug, Clone)]
pub struct BroadphaseWorkItem {
pub body_a: usize,
pub body_b: usize,
}