Skip to main content

box2d_rust/
broad_phase.rs

1// Port of box2d-cpp-reference/src/broad_phase.h and broad_phase.c: the
2// b2BroadPhase storage, proxy operations, move buffering, overlap testing,
3// and the pair update that drives contact creation.
4//
5// The single-threaded port turns the C parallel find-pairs task into a serial
6// loop and the arena b2MoveResult/b2MovePair scratch (a prepended linked list
7// per moved proxy) into a Vec of shape-id pairs per moved proxy. The C list
8// is iterated head-first, i.e. reverse discovery order, so contact creation
9// iterates each Vec in reverse to preserve the exact contact creation order.
10//
11// SPDX-FileCopyrightText: 2023 Erin Catto
12// SPDX-License-Identifier: MIT
13
14use crate::bitset::BitSet;
15use crate::core::NULL_INDEX;
16use crate::dynamic_tree::{DynamicTree, DEFAULT_MASK_BITS};
17use crate::id::ShapeId;
18use crate::math_functions::Aabb;
19use crate::table::{shape_pair_key, HashSet};
20use crate::types::{BodyType, Capacity, BODY_TYPE_COUNT};
21use crate::world::World;
22
23// Store the proxy type in the lower 2 bits of the proxy key. This leaves 30
24// bits for the id.
25
26/// (B2_PROXY_TYPE)
27pub fn proxy_type(key: i32) -> BodyType {
28    match key & 3 {
29        0 => BodyType::Static,
30        1 => BodyType::Kinematic,
31        _ => BodyType::Dynamic,
32    }
33}
34
35/// (B2_PROXY_ID)
36pub fn proxy_id(key: i32) -> i32 {
37    key >> 2
38}
39
40/// (B2_PROXY_KEY)
41pub fn proxy_key(id: i32, type_: BodyType) -> i32 {
42    (id << 2) | (type_ as i32)
43}
44
45/// The broad-phase is used for computing pairs and performing volume queries
46/// and ray casts. It does not persist pairs; it reports potentially new pairs.
47/// It is up to the client to consume the new pairs and to track subsequent
48/// overlap. (b2BroadPhase)
49#[derive(Debug)]
50pub struct BroadPhase {
51    pub trees: [DynamicTree; BODY_TYPE_COUNT],
52
53    /// Per body-type bit sets indexed by proxyId, marking proxies moved this
54    /// step. Paired with move_array which preserves deterministic insertion
55    /// order for pair queries.
56    pub moved_proxies: [BitSet; BODY_TYPE_COUNT],
57    pub move_array: Vec<i32>,
58
59    /// Tracks shape pairs that have a Contact.
60    pub pair_set: HashSet,
61}
62
63impl BroadPhase {
64    /// (b2CreateBroadPhase)
65    pub fn new(capacity: &Capacity) -> BroadPhase {
66        // The C code sizes the static tree by staticShapeCount and the
67        // kinematic/dynamic trees by dynamicShapeCount, and the pair set by
68        // contactCount (16 minimum inside the containers).
69        BroadPhase {
70            trees: [
71                DynamicTree::new(capacity.static_shape_count),
72                DynamicTree::new(capacity.dynamic_shape_count),
73                DynamicTree::new(capacity.dynamic_shape_count),
74            ],
75            moved_proxies: [BitSet::new(16), BitSet::new(16), BitSet::new(16)],
76            move_array: Vec::new(),
77            pair_set: HashSet::new(crate::math_functions::max_int(16, capacity.contact_count)),
78        }
79    }
80
81    /// (b2DestroyBroadPhase)
82    pub fn destroy(&mut self) {
83        *self = BroadPhase {
84            trees: [
85                DynamicTree::new(0),
86                DynamicTree::new(0),
87                DynamicTree::new(0),
88            ],
89            moved_proxies: Default::default(),
90            move_array: Vec::new(),
91            pair_set: HashSet::new(16),
92        };
93    }
94
95    /// This triggers new contact pairs to be created. Must be called in
96    /// deterministic order. (static inline b2BufferMove)
97    pub fn buffer_move(&mut self, query_proxy: i32) {
98        let proxy_type_ = proxy_type(query_proxy);
99        let proxy_id_ = proxy_id(query_proxy);
100        let set = &mut self.moved_proxies[proxy_type_ as usize];
101        if !set.get_bit(proxy_id_ as u32) {
102            set.set_bit_grow(proxy_id_ as u32);
103            self.move_array.push(query_proxy);
104        }
105    }
106
107    /// (b2BroadPhase_CreateProxy)
108    pub fn create_proxy(
109        &mut self,
110        proxy_type_: BodyType,
111        aabb: Aabb,
112        category_bits: u64,
113        shape_index: i32,
114        force_pair_creation: bool,
115    ) -> i32 {
116        let proxy_id_ =
117            self.trees[proxy_type_ as usize].create_proxy(aabb, category_bits, shape_index as u64);
118        let proxy_key_ = proxy_key(proxy_id_, proxy_type_);
119        if proxy_type_ != BodyType::Static || force_pair_creation {
120            self.buffer_move(proxy_key_);
121        }
122        proxy_key_
123    }
124
125    /// (static inline b2UnBufferMove)
126    fn unbuffer_move(&mut self, proxy_key: i32) {
127        let proxy_type_ = proxy_type(proxy_key);
128        let proxy_id_ = proxy_id(proxy_key);
129        let set = &mut self.moved_proxies[proxy_type_ as usize];
130
131        if set.get_bit(proxy_id_ as u32) {
132            set.clear_bit(proxy_id_ as u32);
133
134            // Purge from move buffer. Linear search.
135            if let Some(index) = self.move_array.iter().position(|&k| k == proxy_key) {
136                // C: b2Array_RemoveSwap
137                self.move_array.swap_remove(index);
138            }
139        }
140    }
141
142    /// (b2BroadPhase_DestroyProxy)
143    pub fn destroy_proxy(&mut self, proxy_key: i32) {
144        self.unbuffer_move(proxy_key);
145
146        let proxy_type_ = proxy_type(proxy_key);
147        let proxy_id_ = proxy_id(proxy_key);
148
149        self.trees[proxy_type_ as usize].destroy_proxy(proxy_id_);
150    }
151
152    /// (b2BroadPhase_MoveProxy)
153    pub fn move_proxy(&mut self, proxy_key: i32, aabb: Aabb) {
154        let proxy_type_ = proxy_type(proxy_key);
155        let proxy_id_ = proxy_id(proxy_key);
156
157        self.trees[proxy_type_ as usize].move_proxy(proxy_id_, aabb);
158        self.buffer_move(proxy_key);
159    }
160
161    /// (b2BroadPhase_EnlargeProxy)
162    pub fn enlarge_proxy(&mut self, proxy_key: i32, aabb: Aabb) {
163        debug_assert!(proxy_key != crate::core::NULL_INDEX);
164        let proxy_type_ = proxy_type(proxy_key);
165        let proxy_id_ = proxy_id(proxy_key);
166
167        // Static bodies do not have enlarged proxies
168        debug_assert!(proxy_type_ != BodyType::Static);
169
170        self.trees[proxy_type_ as usize].enlarge_proxy(proxy_id_, aabb);
171        self.buffer_move(proxy_key);
172    }
173
174    /// (b2BroadPhase_GetShapeIndex)
175    pub fn shape_index(&self, proxy_key: i32) -> i32 {
176        let proxy_type_ = proxy_type(proxy_key);
177        let proxy_id_ = proxy_id(proxy_key);
178
179        self.trees[proxy_type_ as usize].user_data(proxy_id_) as i32
180    }
181
182    /// (b2BroadPhase_TestOverlap)
183    pub fn test_overlap(&self, proxy_key_a: i32, proxy_key_b: i32) -> bool {
184        let type_a = proxy_type(proxy_key_a);
185        let id_a = proxy_id(proxy_key_a);
186        let type_b = proxy_type(proxy_key_b);
187        let id_b = proxy_id(proxy_key_b);
188
189        let aabb_a = self.trees[type_a as usize].aabb(id_a);
190        let aabb_b = self.trees[type_b as usize].aabb(id_b);
191        crate::math_functions::aabb_overlaps(aabb_a, aabb_b)
192    }
193
194    /// (b2ValidateBroadphase)
195    pub fn validate(&self) {
196        self.trees[BodyType::Dynamic as usize].validate();
197        self.trees[BodyType::Kinematic as usize].validate();
198    }
199
200    /// (b2ValidateNoEnlarged — C compiles the body under B2_ENABLE_VALIDATION;
201    /// here the check runs in debug builds only)
202    pub fn validate_no_enlarged(&self) {
203        if cfg!(debug_assertions) {
204            for tree in &self.trees {
205                tree.validate_no_enlarged();
206            }
207        }
208    }
209
210    /// (b2ValidateMovedProxies — C compiles the body under
211    /// B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)
212    pub fn validate_moved_proxies(&self) {
213        if cfg!(debug_assertions) {
214            // Invariant: bit set in movedProxies[type] iff proxyKey is present
215            // in moveArray.
216            for &proxy_key_ in &self.move_array {
217                let proxy_type_ = proxy_type(proxy_key_);
218                let proxy_id_ = proxy_id(proxy_key_);
219                debug_assert!(self.moved_proxies[proxy_type_ as usize].get_bit(proxy_id_ as u32));
220            }
221
222            let mut total_set_bits = 0;
223            for i in 0..BODY_TYPE_COUNT {
224                total_set_bits += self.moved_proxies[i].count_set_bits();
225            }
226            debug_assert!(total_set_bits == self.move_array.len() as i32);
227        }
228    }
229}
230
231/// Query one tree for new pairs against a moved proxy, appending them to
232/// `pair_list` in discovery order. (b2PairQueryCallback — the C callback
233/// context struct becomes closure captures)
234fn query_tree_for_pairs(
235    world: &World,
236    tree_type: BodyType,
237    query_proxy_key: i32,
238    query_shape_index: i32,
239    fat_aabb: Aabb,
240    pair_list: &mut Vec<(i32, i32)>,
241) {
242    let bp = &world.broad_phase;
243    let query_proxy_type = proxy_type(query_proxy_key);
244
245    bp.trees[tree_type as usize].query(fat_aabb, DEFAULT_MASK_BITS, |tree_proxy_id, user_data| {
246        let shape_id = user_data as i32;
247        let proxy_key_ = proxy_key(tree_proxy_id, tree_type);
248
249        // A proxy cannot form a pair with itself.
250        if proxy_key_ == query_proxy_key {
251            return true;
252        }
253
254        // De-duplication
255        // It is important to prevent duplicate contacts from being created.
256        // Ideally I can prevent duplicates early and in the worker. Most of
257        // the time the movedProxies bit sets contain dynamic and kinematic
258        // proxies, but sometimes static proxies are in there too
259        // (b2ShapeDef::invokeContactCreation or a modified static shape), so
260        // we always have to check.
261
262        // Is this proxy also moving?
263        if query_proxy_type == BodyType::Dynamic {
264            if tree_type == BodyType::Dynamic && proxy_key_ < query_proxy_key {
265                let moved = bp.moved_proxies[tree_type as usize].get_bit(tree_proxy_id as u32);
266                if moved {
267                    // Both proxies are moving. Avoid duplicate pairs.
268                    return true;
269                }
270            }
271        } else {
272            debug_assert!(tree_type == BodyType::Dynamic);
273            let moved = bp.moved_proxies[tree_type as usize].get_bit(tree_proxy_id as u32);
274            if moved {
275                // Both proxies are moving. Avoid duplicate pairs.
276                return true;
277            }
278        }
279
280        let pair_key = shape_pair_key(shape_id, query_shape_index);
281        if bp.pair_set.contains_key(pair_key) {
282            // contact exists
283            return true;
284        }
285
286        let (shape_id_a, shape_id_b) = if proxy_key_ < query_proxy_key {
287            (shape_id, query_shape_index)
288        } else {
289            (query_shape_index, shape_id)
290        };
291
292        let shape_a = &world.shapes[shape_id_a as usize];
293        let shape_b = &world.shapes[shape_id_b as usize];
294
295        let body_id_a = shape_a.body_id;
296        let body_id_b = shape_b.body_id;
297
298        // Are the shapes on the same body?
299        if body_id_a == body_id_b {
300            return true;
301        }
302
303        // Sensors are handled elsewhere
304        if shape_a.sensor_index != NULL_INDEX || shape_b.sensor_index != NULL_INDEX {
305            return true;
306        }
307
308        if !crate::shape::should_shapes_collide(shape_a.filter, shape_b.filter) {
309            return true;
310        }
311
312        if !crate::contact::can_collide(shape_a.shape_type(), shape_b.shape_type()) {
313            // For example, no segment vs segment collision
314            return true;
315        }
316
317        // Does a joint override collision?
318        if !crate::body::should_bodies_collide(world, body_id_a, body_id_b) {
319            return true;
320        }
321
322        // Custom user filter
323        if shape_a.enable_custom_filtering || shape_b.enable_custom_filtering {
324            if let Some(custom_filter_fcn) = world.custom_filter_fcn {
325                let id_a = ShapeId {
326                    index1: shape_id_a + 1,
327                    world0: world.world_id,
328                    generation: shape_a.generation,
329                };
330                let id_b = ShapeId {
331                    index1: shape_id_b + 1,
332                    world0: world.world_id,
333                    generation: shape_b.generation,
334                };
335                let should_collide = custom_filter_fcn(id_a, id_b, world.custom_filter_context);
336                if !should_collide {
337                    return true;
338                }
339            }
340        }
341
342        pair_list.push((shape_id_a, shape_id_b));
343
344        // continue the query
345        true
346    });
347}
348
349/// Find new proxy pairs for everything in the move buffer and create their
350/// contacts, in deterministic move-array order. Also rebuilds the dynamic and
351/// kinematic trees and clears the move buffer. (b2UpdateBroadPhasePairs —
352/// serial port of b2FindPairsTask/b2UpdateTreesTask)
353pub fn update_broad_phase_pairs(world: &mut World) {
354    world.broad_phase.validate_moved_proxies();
355
356    let move_count = world.broad_phase.move_array.len();
357
358    if move_count == 0 {
359        return;
360    }
361
362    // Find pairs. (b2FindPairsTask over [0, moveCount) on one worker)
363    let mut move_results: Vec<Vec<(i32, i32)>> = Vec::with_capacity(move_count);
364    for i in 0..move_count {
365        let mut pair_list: Vec<(i32, i32)> = Vec::new();
366
367        let proxy_key_ = world.broad_phase.move_array[i];
368        if proxy_key_ == NULL_INDEX {
369            // proxy was destroyed after it moved
370            move_results.push(pair_list);
371            continue;
372        }
373
374        let proxy_type_ = proxy_type(proxy_key_);
375        let proxy_id_ = proxy_id(proxy_key_);
376
377        // We have to query the tree with the fat AABB so that
378        // we don't fail to create a contact that may touch later.
379        let base_tree = &world.broad_phase.trees[proxy_type_ as usize];
380        let fat_aabb = base_tree.aabb(proxy_id_);
381        let query_shape_index = base_tree.user_data(proxy_id_) as i32;
382
383        // Query trees. Only dynamic proxies collide with kinematic and static
384        // proxies. Using B2_DEFAULT_MASK_BITS so that b2Filter::groupIndex
385        // works.
386        if proxy_type_ == BodyType::Dynamic {
387            query_tree_for_pairs(
388                world,
389                BodyType::Kinematic,
390                proxy_key_,
391                query_shape_index,
392                fat_aabb,
393                &mut pair_list,
394            );
395            query_tree_for_pairs(
396                world,
397                BodyType::Static,
398                proxy_key_,
399                query_shape_index,
400                fat_aabb,
401                &mut pair_list,
402            );
403        }
404
405        // All proxies collide with dynamic proxies
406        query_tree_for_pairs(
407            world,
408            BodyType::Dynamic,
409            proxy_key_,
410            query_shape_index,
411            fat_aabb,
412            &mut pair_list,
413        );
414
415        move_results.push(pair_list);
416    }
417
418    // Rebuild the collision tree for dynamic and kinematic bodies to keep
419    // their query performance good. In C this runs as a task in parallel with
420    // the narrow-phase; the serial fallback runs it here. (b2UpdateTreesTask)
421    world.broad_phase.trees[BodyType::Dynamic as usize].rebuild(false);
422    world.broad_phase.trees[BodyType::Kinematic as usize].rebuild(false);
423
424    // Single-threaded work
425    // - Create contacts in deterministic order
426    // This is deterministic because the results follow the order of
427    // b2BroadPhase::moveArray. Each C pair list is iterated head-first, which
428    // is reverse discovery order, hence .rev().
429    for pair_list in &move_results {
430        for &(shape_id_a, shape_id_b) in pair_list.iter().rev() {
431            crate::contact::create_contact(world, shape_id_a, shape_id_b);
432        }
433    }
434
435    // Reset move buffer: clear only the bits that were set this step.
436    // Invariant: bit set in movedProxies[type] iff proxyKey is present in
437    // moveArray.
438    for i in 0..world.broad_phase.move_array.len() {
439        let proxy_key_ = world.broad_phase.move_array[i];
440        let proxy_type_ = proxy_type(proxy_key_);
441        let proxy_id_ = proxy_id(proxy_key_);
442        world.broad_phase.moved_proxies[proxy_type_ as usize].clear_bit(proxy_id_ as u32);
443    }
444    world.broad_phase.move_array.clear();
445
446    world.validate_solver_sets();
447}
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452    use crate::math_functions::Vec2;
453
454    fn aabb(lx: f32, ly: f32, ux: f32, uy: f32) -> Aabb {
455        Aabb {
456            lower_bound: Vec2 { x: lx, y: ly },
457            upper_bound: Vec2 { x: ux, y: uy },
458        }
459    }
460
461    #[test]
462    fn proxy_key_round_trip() {
463        for (id, t) in [
464            (0, BodyType::Static),
465            (7, BodyType::Kinematic),
466            (123456, BodyType::Dynamic),
467        ] {
468            let key = proxy_key(id, t);
469            assert_eq!(proxy_id(key), id);
470            assert_eq!(proxy_type(key), t);
471        }
472    }
473
474    #[test]
475    fn create_move_destroy_and_overlap() {
476        let mut bp = BroadPhase::new(&Capacity::default());
477
478        // Static proxies don't buffer a move unless forced.
479        let k_static = bp.create_proxy(BodyType::Static, aabb(0.0, 0.0, 1.0, 1.0), 1, 10, false);
480        assert!(bp.move_array.is_empty());
481
482        let k_dyn = bp.create_proxy(BodyType::Dynamic, aabb(0.5, 0.5, 1.5, 1.5), 1, 11, false);
483        assert_eq!(bp.move_array.len(), 1);
484
485        // Buffering the same proxy twice only records it once.
486        bp.buffer_move(k_dyn);
487        assert_eq!(bp.move_array.len(), 1);
488
489        assert!(bp.test_overlap(k_static, k_dyn));
490        assert_eq!(bp.shape_index(k_static), 10);
491        assert_eq!(bp.shape_index(k_dyn), 11);
492
493        // Move the dynamic proxy away; overlap ends.
494        bp.move_proxy(k_dyn, aabb(10.0, 10.0, 11.0, 11.0));
495        assert!(!bp.test_overlap(k_static, k_dyn));
496
497        // Destroy removes from the move buffer and the tree.
498        bp.destroy_proxy(k_dyn);
499        assert!(bp.move_array.is_empty());
500        assert_eq!(bp.trees[BodyType::Dynamic as usize].proxy_count(), 0);
501    }
502}