1use std::collections::HashMap;
6use std::sync::atomic::{AtomicUsize, Ordering};
7use std::sync::{Arc, Mutex, RwLock};
8
9use super::functions::{
10 NodeIdx, aabb_all_pairs_overlap, add3, max3, min3, par_box_box, par_sphere_box,
11 par_sphere_sphere, scale3, sub3,
12};
13
14#[derive(Debug)]
19pub struct ParallelUnionFind {
20 pub(super) parent: Mutex<Vec<usize>>,
21 pub(super) rank: Mutex<Vec<usize>>,
22 pub(super) n: usize,
23}
24impl ParallelUnionFind {
25 pub fn new(n: usize) -> Self {
27 Self {
28 parent: Mutex::new((0..n).collect()),
29 rank: Mutex::new(vec![0; n]),
30 n,
31 }
32 }
33 pub fn find(&self, x: usize) -> usize {
35 let mut parent = self.parent.lock().unwrap_or_else(|e| e.into_inner());
36 let mut root = x;
37 while parent[root] != root {
38 root = parent[root];
39 }
40 let mut node = x;
41 while parent[node] != root {
42 let next = parent[node];
43 parent[node] = root;
44 node = next;
45 }
46 root
47 }
48 pub fn union(&self, x: usize, y: usize) {
50 let rx = self.find(x);
51 let ry = self.find(y);
52 if rx == ry {
53 return;
54 }
55 let mut parent = self.parent.lock().unwrap_or_else(|e| e.into_inner());
56 let mut rank = self.rank.lock().unwrap_or_else(|e| e.into_inner());
57 if rank[rx] < rank[ry] {
58 parent[rx] = ry;
59 } else if rank[rx] > rank[ry] {
60 parent[ry] = rx;
61 } else {
62 parent[ry] = rx;
63 rank[rx] += 1;
64 }
65 }
66 pub fn islands(&self) -> Vec<Vec<usize>> {
68 let mut map: HashMap<usize, Vec<usize>> = HashMap::new();
69 for i in 0..self.n {
70 let root = self.find(i);
71 map.entry(root).or_default().push(i);
72 }
73 map.into_values().collect()
74 }
75 pub fn num_components(&self) -> usize {
77 let mut roots = std::collections::HashSet::new();
78 for i in 0..self.n {
79 roots.insert(self.find(i));
80 }
81 roots.len()
82 }
83}
84#[derive(Debug, Clone, Copy, PartialEq)]
88pub struct ParAabb {
89 pub min: [f64; 3],
91 pub max: [f64; 3],
93}
94impl ParAabb {
95 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
97 Self { min, max }
98 }
99 pub fn from_center_half_extents(center: [f64; 3], half: [f64; 3]) -> Self {
101 Self {
102 min: sub3(center, half),
103 max: add3(center, half),
104 }
105 }
106 pub fn merge(&self, other: &Self) -> Self {
108 Self {
109 min: min3(self.min, other.min),
110 max: max3(self.max, other.max),
111 }
112 }
113 pub fn center(&self) -> [f64; 3] {
115 scale3(add3(self.min, self.max), 0.5)
116 }
117 pub fn half_extents(&self) -> [f64; 3] {
119 scale3(sub3(self.max, self.min), 0.5)
120 }
121 pub fn surface_area(&self) -> f64 {
123 let d = sub3(self.max, self.min);
124 2.0 * (d[0] * d[1] + d[1] * d[2] + d[2] * d[0])
125 }
126 #[inline(always)]
130 pub fn overlaps(&self, other: &Self) -> bool {
131 self.min[0] <= other.max[0]
132 && self.max[0] >= other.min[0]
133 && self.min[1] <= other.max[1]
134 && self.max[1] >= other.min[1]
135 && self.min[2] <= other.max[2]
136 && self.max[2] >= other.min[2]
137 }
138 pub fn expanded(&self, margin: f64) -> Self {
140 Self {
141 min: [
142 self.min[0] - margin,
143 self.min[1] - margin,
144 self.min[2] - margin,
145 ],
146 max: [
147 self.max[0] + margin,
148 self.max[1] + margin,
149 self.max[2] + margin,
150 ],
151 }
152 }
153 pub fn contains_point(&self, p: [f64; 3]) -> bool {
155 p[0] >= self.min[0]
156 && p[0] <= self.max[0]
157 && p[1] >= self.min[1]
158 && p[1] <= self.max[1]
159 && p[2] >= self.min[2]
160 && p[2] <= self.max[2]
161 }
162}
163#[derive(Debug, Clone, Copy)]
165pub struct CcdHit {
166 pub toi: f64,
168 pub body_a: u32,
170 pub body_b: u32,
172 pub normal: [f64; 3],
174}
175#[derive(Debug, Clone, Copy)]
180pub struct GpuAabbBufferDesc {
181 pub offset_min_x: u32,
183 pub offset_min_y: u32,
185 pub offset_min_z: u32,
187 pub offset_max_x: u32,
189 pub offset_max_y: u32,
191 pub offset_max_z: u32,
193 pub count: u32,
195 pub stride: u32,
197}
198impl GpuAabbBufferDesc {
199 pub fn new_f32_soa(count: u32) -> Self {
201 let stride = 4u32;
202 let array_bytes = count * stride;
203 Self {
204 offset_min_x: 0,
205 offset_min_y: array_bytes,
206 offset_min_z: array_bytes * 2,
207 offset_max_x: array_bytes * 3,
208 offset_max_y: array_bytes * 4,
209 offset_max_z: array_bytes * 5,
210 count,
211 stride,
212 }
213 }
214 pub fn total_bytes(&self) -> u32 {
216 self.count * self.stride * 6
217 }
218 pub fn serialize_soa(buf: &SoaAabbBuffer) -> Vec<u8> {
220 let n = buf.len();
221 let mut out = Vec::with_capacity(n * 6 * 4);
222 let write_slice = |dst: &mut Vec<u8>, src: &[f32]| {
223 for &v in src {
224 dst.extend_from_slice(&v.to_le_bytes());
225 }
226 };
227 write_slice(&mut out, &buf.min_x);
228 write_slice(&mut out, &buf.min_y);
229 write_slice(&mut out, &buf.min_z);
230 write_slice(&mut out, &buf.max_x);
231 write_slice(&mut out, &buf.max_y);
232 write_slice(&mut out, &buf.max_z);
233 out
234 }
235}
236#[derive(Debug, Clone)]
237pub(super) struct AabbTreeInner {
238 pub(super) nodes: Vec<ParAabbNode>,
239 pub(super) root: NodeIdx,
240 pub(super) free_list: Vec<NodeIdx>,
241}
242impl AabbTreeInner {
243 fn new() -> Self {
244 Self {
245 nodes: Vec::new(),
246 root: u32::MAX,
247 free_list: Vec::new(),
248 }
249 }
250 fn alloc_node(&mut self) -> NodeIdx {
251 if let Some(idx) = self.free_list.pop() {
252 idx
253 } else {
254 let idx = self.nodes.len() as NodeIdx;
255 self.nodes.push(ParAabbNode {
256 aabb: ParAabb::default(),
257 left: u32::MAX,
258 right: u32::MAX,
259 handle: u32::MAX,
260 height: 0,
261 });
262 idx
263 }
264 }
265 fn insert(&mut self, aabb: ParAabb, handle: u32) -> NodeIdx {
267 let leaf = self.alloc_node();
268 self.nodes[leaf as usize].aabb = aabb;
269 self.nodes[leaf as usize].handle = handle;
270 self.nodes[leaf as usize].left = u32::MAX;
271 self.nodes[leaf as usize].right = u32::MAX;
272 self.nodes[leaf as usize].height = 0;
273 if self.root == u32::MAX {
274 self.root = leaf;
275 return leaf;
276 }
277 let mut best = self.root;
278 loop {
279 let node = &self.nodes[best as usize];
280 if node.is_leaf() {
281 break;
282 }
283 let merged_area = node.aabb.merge(&aabb).surface_area();
284 let left = node.left;
285 let right = node.right;
286 let la = self.nodes[left as usize].aabb.merge(&aabb).surface_area();
287 let ra = self.nodes[right as usize].aabb.merge(&aabb).surface_area();
288 if merged_area < la.min(ra) {
289 break;
290 }
291 best = if la < ra { left } else { right };
292 }
293 let old_parent = self.alloc_node();
294 let sib_aabb = self.nodes[best as usize].aabb;
295 self.nodes[old_parent as usize].aabb = sib_aabb.merge(&aabb);
296 self.nodes[old_parent as usize].left = best;
297 self.nodes[old_parent as usize].right = leaf;
298 let sib_h = self.nodes[best as usize].height;
299 self.nodes[old_parent as usize].height = sib_h + 1;
300 self.nodes[old_parent as usize].handle = u32::MAX;
301 if self.root == best {
302 self.root = old_parent;
303 }
304 leaf
305 }
306 fn query_overlaps(&self, query: &ParAabb) -> Vec<u32> {
308 let mut result = Vec::new();
309 if self.root == u32::MAX {
310 return result;
311 }
312 let mut stack = vec![self.root];
313 while let Some(idx) = stack.pop() {
314 let node = &self.nodes[idx as usize];
315 if !node.aabb.overlaps(query) {
316 continue;
317 }
318 if node.is_leaf() {
319 result.push(node.handle);
320 } else {
321 if node.left != u32::MAX {
322 stack.push(node.left);
323 }
324 if node.right != u32::MAX {
325 stack.push(node.right);
326 }
327 }
328 }
329 result
330 }
331}
332#[derive(Debug, Clone, Copy)]
334pub enum ParShapeKind {
335 Sphere(ParSphere),
337 Box(ParBox),
339}
340#[derive(Debug, Clone, Copy)]
342pub struct CcdBodyState {
343 pub pos0: [f64; 3],
345 pub pos1: [f64; 3],
347 pub radius: f64,
349 pub index: u32,
351}
352impl CcdBodyState {
353 pub fn new(pos0: [f64; 3], pos1: [f64; 3], radius: f64, index: u32) -> Self {
355 Self {
356 pos0,
357 pos1,
358 radius,
359 index,
360 }
361 }
362 pub fn swept_aabb(&self) -> ParAabb {
364 let margin = [self.radius; 3];
365 let mn0 = sub3(self.pos0, margin);
366 let mx0 = add3(self.pos0, margin);
367 let mn1 = sub3(self.pos1, margin);
368 let mx1 = add3(self.pos1, margin);
369 ParAabb {
370 min: min3(mn0, mn1),
371 max: max3(mx0, mx1),
372 }
373 }
374}
375#[derive(Debug, Clone)]
377pub struct GpuBvhBuffer {
378 pub nodes: Vec<GpuBvhNode>,
380}
381impl GpuBvhBuffer {
382 pub fn new() -> Self {
384 Self { nodes: Vec::new() }
385 }
386 pub fn build(aabbs: &[(ParAabb, u32)]) -> Self {
390 let mut buf = Self::new();
391 if aabbs.is_empty() {
392 return buf;
393 }
394 Self::build_recursive(&mut buf.nodes, aabbs);
395 buf
396 }
397 fn build_recursive(nodes: &mut Vec<GpuBvhNode>, aabbs: &[(ParAabb, u32)]) -> u32 {
398 if aabbs.len() == 1 {
399 let (aabb, handle) = &aabbs[0];
400 let node = GpuBvhNode::leaf(
401 [aabb.min[0] as f32, aabb.min[1] as f32, aabb.min[2] as f32],
402 [aabb.max[0] as f32, aabb.max[1] as f32, aabb.max[2] as f32],
403 *handle,
404 );
405 let idx = nodes.len() as u32;
406 nodes.push(node);
407 return idx;
408 }
409 let mut combined = aabbs[0].0;
410 for (a, _) in aabbs.iter().skip(1) {
411 combined = combined.merge(a);
412 }
413 let extent = sub3(combined.max, combined.min);
414 let axis = if extent[0] >= extent[1] && extent[0] >= extent[2] {
415 0
416 } else if extent[1] >= extent[2] {
417 1
418 } else {
419 2
420 };
421 let mut sorted: Vec<_> = aabbs.to_vec();
422 sorted.sort_by(|(a, _), (b, _)| {
423 let ca = (a.min[axis] + a.max[axis]) * 0.5;
424 let cb = (b.min[axis] + b.max[axis]) * 0.5;
425 ca.partial_cmp(&cb).unwrap_or(std::cmp::Ordering::Equal)
426 });
427 let mid = sorted.len() / 2;
428 let my_idx = nodes.len() as u32;
429 nodes.push(GpuBvhNode::internal([0.0; 3], [0.0; 3], 0, 0));
430 let left = Self::build_recursive(nodes, &sorted[..mid]);
431 let right = Self::build_recursive(nodes, &sorted[mid..]);
432 nodes[my_idx as usize] = GpuBvhNode::internal(
433 [
434 combined.min[0] as f32,
435 combined.min[1] as f32,
436 combined.min[2] as f32,
437 ],
438 [
439 combined.max[0] as f32,
440 combined.max[1] as f32,
441 combined.max[2] as f32,
442 ],
443 left,
444 right,
445 );
446 my_idx
447 }
448 pub fn node_count(&self) -> usize {
450 self.nodes.len()
451 }
452 pub fn to_bytes(&self) -> Vec<u8> {
454 let mut out = Vec::with_capacity(self.nodes.len() * 32);
455 for node in &self.nodes {
456 for &v in &node.min {
457 out.extend_from_slice(&v.to_le_bytes());
458 }
459 out.extend_from_slice(&node.left_or_leaf.to_le_bytes());
460 for &v in &node.max {
461 out.extend_from_slice(&v.to_le_bytes());
462 }
463 out.extend_from_slice(&node.right_or_handle.to_le_bytes());
464 }
465 out
466 }
467}
468#[derive(Debug, Clone, Copy)]
470pub struct ParContact {
471 pub body_a: u32,
473 pub body_b: u32,
475 pub normal: [f64; 3],
477 pub depth: f64,
479 pub point: [f64; 3],
481}
482#[derive(Debug, Clone)]
484pub struct ParCollisionResult {
485 pub contacts: Vec<ParContact>,
487 pub islands: Vec<Vec<usize>>,
489 pub ccd_hits: Vec<CcdHit>,
491}
492#[derive(Debug, Clone, Copy)]
496pub struct ParBox {
497 pub center: [f64; 3],
499 pub half_extents: [f64; 3],
501}
502#[derive(Debug, Clone, Copy)]
504pub struct ParSphere {
505 pub center: [f64; 3],
507 pub radius: f64,
509}
510#[derive(Debug, Clone)]
512pub struct ParAabbNode {
513 pub aabb: ParAabb,
515 pub left: NodeIdx,
517 pub right: NodeIdx,
519 pub handle: u32,
521 pub height: i32,
523}
524impl ParAabbNode {
525 pub fn is_leaf(&self) -> bool {
527 self.left == u32::MAX
528 }
529}
530#[derive(Debug, Clone)]
535pub struct SoaAabbBuffer {
536 pub min_x: Vec<f32>,
538 pub min_y: Vec<f32>,
540 pub min_z: Vec<f32>,
542 pub max_x: Vec<f32>,
544 pub max_y: Vec<f32>,
546 pub max_z: Vec<f32>,
548 pub handles: Vec<u32>,
550}
551impl SoaAabbBuffer {
552 pub fn new() -> Self {
554 Self {
555 min_x: Vec::new(),
556 min_y: Vec::new(),
557 min_z: Vec::new(),
558 max_x: Vec::new(),
559 max_y: Vec::new(),
560 max_z: Vec::new(),
561 handles: Vec::new(),
562 }
563 }
564 pub fn push(&mut self, aabb: &ParAabb, handle: u32) {
566 self.min_x.push(aabb.min[0] as f32);
567 self.min_y.push(aabb.min[1] as f32);
568 self.min_z.push(aabb.min[2] as f32);
569 self.max_x.push(aabb.max[0] as f32);
570 self.max_y.push(aabb.max[1] as f32);
571 self.max_z.push(aabb.max[2] as f32);
572 self.handles.push(handle);
573 }
574 pub fn len(&self) -> usize {
576 self.handles.len()
577 }
578 pub fn is_empty(&self) -> bool {
580 self.handles.is_empty()
581 }
582 pub fn query(&self, aabb: &ParAabb) -> Vec<usize> {
584 let qminx = aabb.min[0] as f32;
585 let qminy = aabb.min[1] as f32;
586 let qminz = aabb.min[2] as f32;
587 let qmaxx = aabb.max[0] as f32;
588 let qmaxy = aabb.max[1] as f32;
589 let qmaxz = aabb.max[2] as f32;
590 let mut result = Vec::new();
591 let n = self.handles.len();
592 for i in 0..n {
593 let hit = (qminx <= self.max_x[i])
594 & (qmaxx >= self.min_x[i])
595 & (qminy <= self.max_y[i])
596 & (qmaxy >= self.min_y[i])
597 & (qminz <= self.max_z[i])
598 & (qmaxz >= self.min_z[i]);
599 if hit {
600 result.push(i);
601 }
602 }
603 result
604 }
605 pub fn rebuild(&mut self, entries: &[(ParAabb, u32)]) {
607 self.min_x.clear();
608 self.min_y.clear();
609 self.min_z.clear();
610 self.max_x.clear();
611 self.max_y.clear();
612 self.max_z.clear();
613 self.handles.clear();
614 for (aabb, handle) in entries {
615 self.push(aabb, *handle);
616 }
617 }
618}
619#[derive(Debug)]
624pub struct WorkStealingBroadphase {
625 pub(super) aabbs: Arc<Vec<ParAabb>>,
626 pub(super) chunk_size: usize,
627 pub(super) pending: Mutex<Vec<BroadphaseWorkItem>>,
628}
629impl WorkStealingBroadphase {
630 pub fn new(aabbs: Vec<ParAabb>, chunk_size: usize) -> Self {
632 let pairs = aabb_all_pairs_overlap(&aabbs);
633 let pending = pairs
634 .into_iter()
635 .map(|(a, b)| BroadphaseWorkItem {
636 body_a: a,
637 body_b: b,
638 })
639 .collect();
640 Self {
641 aabbs: Arc::new(aabbs),
642 chunk_size,
643 pending: Mutex::new(pending),
644 }
645 }
646 pub fn steal(&self) -> Vec<BroadphaseWorkItem> {
648 let mut q = self.pending.lock().unwrap_or_else(|e| e.into_inner());
649 let n = self.chunk_size.min(q.len());
650 q.drain(..n).collect()
651 }
652 pub fn is_done(&self) -> bool {
654 self.pending
655 .lock()
656 .unwrap_or_else(|e| e.into_inner())
657 .is_empty()
658 }
659 pub fn remaining(&self) -> usize {
661 self.pending.lock().unwrap_or_else(|e| e.into_inner()).len()
662 }
663 pub fn aabbs(&self) -> &[ParAabb] {
665 &self.aabbs
666 }
667}
668#[derive(Debug)]
674pub struct ParallelAabbTree {
675 pub(super) inner: RwLock<AabbTreeInner>,
676}
677impl ParallelAabbTree {
678 pub fn new() -> Self {
680 Self {
681 inner: RwLock::new(AabbTreeInner::new()),
682 }
683 }
684 pub fn insert(&self, aabb: ParAabb, handle: u32) -> NodeIdx {
686 self.inner
687 .write()
688 .unwrap_or_else(|e| e.into_inner())
689 .insert(aabb, handle)
690 }
691 pub fn query(&self, aabb: &ParAabb) -> Vec<u32> {
695 self.inner
696 .read()
697 .unwrap_or_else(|e| e.into_inner())
698 .query_overlaps(aabb)
699 }
700 pub fn node_count(&self) -> usize {
702 self.inner
703 .read()
704 .unwrap_or_else(|e| e.into_inner())
705 .nodes
706 .len()
707 }
708}
709#[derive(Debug)]
711pub struct NarrowphaseBatch {
712 pub pairs: Vec<(u32, ParShapeKind, u32, ParShapeKind)>,
714}
715impl NarrowphaseBatch {
716 pub fn new() -> Self {
718 Self { pairs: Vec::new() }
719 }
720 pub fn push(&mut self, a_idx: u32, a: ParShapeKind, b_idx: u32, b: ParShapeKind) {
722 self.pairs.push((a_idx, a, b_idx, b));
723 }
724 pub fn process(&self) -> Vec<ParContact> {
728 let mut contacts = Vec::new();
729 for &(a_idx, ref a_shape, b_idx, ref b_shape) in &self.pairs {
730 match (a_shape, b_shape) {
731 (ParShapeKind::Sphere(sa), ParShapeKind::Sphere(sb)) => {
732 if let Some(c) = par_sphere_sphere(a_idx, b_idx, sa, sb) {
733 contacts.push(c);
734 }
735 }
736 (ParShapeKind::Sphere(sa), ParShapeKind::Box(bb)) => {
737 if let Some(c) = par_sphere_box(a_idx, b_idx, sa, bb) {
738 contacts.push(c);
739 }
740 }
741 (ParShapeKind::Box(ba), ParShapeKind::Sphere(sb)) => {
742 if let Some(mut c) = par_sphere_box(b_idx, a_idx, sb, ba) {
743 c.normal = scale3(c.normal, -1.0);
744 c.body_a = a_idx;
745 c.body_b = b_idx;
746 contacts.push(c);
747 }
748 }
749 (ParShapeKind::Box(_ba), ParShapeKind::Box(_bb)) => {
750 if let (ParShapeKind::Box(ba_), ParShapeKind::Box(bb_)) = (a_shape, b_shape)
751 && let Some(c) = par_box_box(a_idx, b_idx, ba_, bb_)
752 {
753 contacts.push(c);
754 }
755 }
756 }
757 }
758 contacts
759 }
760}
761#[derive(Debug, Clone, Copy)]
765#[repr(C)]
766pub struct GpuBvhNode {
767 pub min: [f32; 3],
769 pub left_or_leaf: u32,
771 pub max: [f32; 3],
773 pub right_or_handle: u32,
775}
776impl GpuBvhNode {
777 pub fn leaf(min: [f32; 3], max: [f32; 3], handle: u32) -> Self {
779 Self {
780 min,
781 max,
782 left_or_leaf: u32::MAX,
783 right_or_handle: handle,
784 }
785 }
786 pub fn internal(min: [f32; 3], max: [f32; 3], left: u32, right: u32) -> Self {
788 Self {
789 min,
790 max,
791 left_or_leaf: left,
792 right_or_handle: right,
793 }
794 }
795 pub fn is_leaf(&self) -> bool {
797 self.left_or_leaf == u32::MAX
798 }
799}
800#[derive(Debug)]
806pub struct LockFreeContactBuffer {
807 pub(super) contacts: Vec<Mutex<Option<ParContact>>>,
808 pub(super) write_idx: AtomicUsize,
809 pub(super) capacity: usize,
810}
811impl LockFreeContactBuffer {
812 pub fn new(capacity: usize) -> Self {
814 let mut contacts = Vec::with_capacity(capacity);
815 for _ in 0..capacity {
816 contacts.push(Mutex::new(None));
817 }
818 Self {
819 contacts,
820 write_idx: AtomicUsize::new(0),
821 capacity,
822 }
823 }
824 pub fn push_contact(&self, contact: ParContact) -> Option<usize> {
826 let idx = self.write_idx.fetch_add(1, Ordering::AcqRel);
827 if idx < self.capacity {
828 *self.contacts[idx].lock().unwrap_or_else(|e| e.into_inner()) = Some(contact);
829 Some(idx)
830 } else {
831 self.write_idx.fetch_sub(1, Ordering::AcqRel);
832 None
833 }
834 }
835 pub fn len(&self) -> usize {
837 self.write_idx.load(Ordering::Acquire).min(self.capacity)
838 }
839 pub fn is_empty(&self) -> bool {
841 self.len() == 0
842 }
843 pub fn reset(&self) {
845 let old = self.write_idx.swap(0, Ordering::AcqRel);
846 for i in 0..old.min(self.capacity) {
847 *self.contacts[i].lock().unwrap_or_else(|e| e.into_inner()) = None;
848 }
849 }
850 pub fn collect(&self) -> Vec<ParContact> {
852 let n = self.len();
853 let mut out = Vec::with_capacity(n);
854 for i in 0..n {
855 if let Some(c) = *self.contacts[i].lock().unwrap_or_else(|e| e.into_inner()) {
856 out.push(c);
857 }
858 }
859 out
860 }
861}
862#[derive(Debug, Clone)]
864pub struct ParCollisionConfig {
865 pub max_contacts_per_pair: usize,
867 pub aabb_margin: f64,
869 pub enable_ccd: bool,
871}
872#[derive(Debug, Clone)]
874pub struct BroadphaseWorkItem {
875 pub body_a: usize,
877 pub body_b: usize,
879}