Skip to main content

avian3d/collider_tree/
update.rs

1use core::cell::RefCell;
2use core::marker::PhantomData;
3
4use crate::{
5    collider_tree::{
6        ColliderTree, ColliderTreeDiagnostics, ColliderTreeProxy, ColliderTreeProxyKey,
7        ColliderTreeSystems, ColliderTreeType, ColliderTrees, ProxyId,
8        tree::ColliderTreeProxyFlags,
9    },
10    collision::collider::{ColliderAabbMargin, EnlargedAabb},
11    data_structures::bit_vec::BitVec,
12    dynamics::solver::solver_body::SolverBodyIndex,
13    prelude::*,
14    schedule::LastPhysicsTick,
15    utils::{MIN_PAR_ITER_ENTITIES, ParallelQueryForEach},
16};
17use bevy::{
18    ecs::{
19        change_detection::Tick,
20        entity_disabling::Disabled,
21        query::QueryFilter,
22        system::{StaticSystemParam, SystemChangeTick},
23    },
24    platform::collections::HashMap,
25    prelude::*,
26};
27use obvhs::aabb::Aabb;
28use thread_local::ThreadLocal;
29
30/// A plugin for updating [`ColliderTree`]s for a collider type `C`.
31///
32/// [`ColliderTree`]: crate::collider_tree::ColliderTree
33pub(super) struct ColliderTreeUpdatePlugin<C: AnyCollider>(PhantomData<C>);
34
35impl<C: AnyCollider> Default for ColliderTreeUpdatePlugin<C> {
36    fn default() -> Self {
37        Self(PhantomData)
38    }
39}
40
41impl<C: AnyCollider> Plugin for ColliderTreeUpdatePlugin<C> {
42    fn build(&self, app: &mut App) {
43        // Initialize resources.
44        app.init_resource::<MovedProxies>()
45            .init_resource::<EnlargedProxies>()
46            .init_resource::<LastDynamicKinematicAabbUpdate>();
47
48        // Add systems for updating collider AABBs before physics step.
49        // This accounts for manually moved colliders.
50        app.add_systems(
51            PhysicsSchedule,
52            update_moved_collider_aabbs::<C>
53                .in_set(ColliderTreeSystems::UpdateAabbs)
54                // Allowing ambiguities is required so that it's possible
55                // to have multiple collision backends at the same time.
56                .ambiguous_with_all(),
57        );
58
59        // Clear moved proxies and update dynamic and kinematic collider AABBs.
60        app.add_systems(
61            PhysicsSchedule,
62            (clear_moved_proxies, update_solver_body_aabbs::<C>)
63                .chain()
64                .after(PhysicsStepSystems::Finalize)
65                .before(PhysicsStepSystems::Last),
66        );
67
68        // Initialize `ColliderAabb` for colliders.
69        app.add_observer(
70            |trigger: On<Add<C>>,
71             mut query: Query<(
72                &C,
73                &Position,
74                &Rotation,
75                Option<&CollisionMargin>,
76                &mut ColliderAabb,
77                &mut EnlargedAabb,
78                &mut ColliderAabbMargin,
79            )>,
80             narrow_phase_config: Res<NarrowPhaseConfig>,
81             length_unit: Res<PhysicsLengthUnit>,
82             collider_context: StaticSystemParam<C::Context>| {
83                let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
84
85                if let Ok((
86                    collider,
87                    pos,
88                    rot,
89                    collision_margin,
90                    mut aabb,
91                    mut enlarged_aabb,
92                    mut aabb_margin,
93                )) = query.get_mut(trigger.entity)
94                {
95                    let collision_margin = collision_margin.map_or(0.0, |m| m.0);
96
97                    // TODO: Should we instead do this in `add_to_tree_on`?
98                    // Update tight-fitting AABB.
99                    let context = ColliderContext::new(trigger.entity, &*collider_context);
100                    let growth = contact_tolerance + collision_margin;
101                    *aabb = collider.aabb_with_context(pos.0, *rot, growth, context);
102
103                    // Compute and cache the size-relative AABB margin for the collider.
104                    let context = ColliderContext::new(trigger.entity, &*collider_context);
105                    *aabb_margin = ColliderAabbMargin::from_bounding_radius(
106                        collider.bounding_radius_with_context(context),
107                        length_unit.0,
108                    );
109
110                    // Use the cached margin for the initial enlarged AABB.
111                    enlarged_aabb.update(&aabb, aabb_margin.0);
112                }
113            },
114        );
115
116        // Aside from AABB updates, we need to handle the following cases:
117        //
118        // 1. On insert `C` or `ColliderOf`, add to new tree if not already present. Remove from old tree if present.
119        // 2. On remove `C`, remove from tree.
120        // 3. On remove `ColliderOf`, move to standalone tree if `C` still exists.
121        // 4. On re-enable `C`, add to tree.
122        // 5. On disable `C`, remove from tree.
123        // 6. On replace `RigidBody`, move attached colliders to new tree.
124        // 7. On add `Sensor`, set sensor proxy flag.
125        // 8. On remove `Sensor`, unset sensor proxy flag.
126        // 9. On insert `CollisionLayers`, update proxy layers.
127        // 10. On insert `ActiveCollisionHooks`, set proxy flag.
128        // 11. On replace `RigidBodyDisabled`, set/unset proxy flag.
129
130        // Case 1
131        app.add_observer(add_to_tree_on::<Insert<(C, ColliderOf)>, Without<ColliderDisabled>>);
132
133        // Case 2
134        // Note: We also include disabled entities here for the edge case where
135        //       we despawn a disabled collider, which causes Case 4 to trigger first.
136        //       Ideally Case 4 would not trigger for despawned entities.
137        // TODO: Clean up the edge case described above.
138        app.add_observer(remove_from_tree_on::<Remove<C>, Allow<Disabled>>);
139
140        // Case 3
141        app.add_observer(
142            |trigger: On<Remove<ColliderOf>>,
143             mut collider_query: Query<
144                (
145                    &mut ColliderTreeProxyKey,
146                    &EnlargedAabb,
147                    Option<&CollisionLayers>,
148                    Has<Sensor>,
149                    Has<CollisionEventsEnabled>,
150                    Option<&ActiveCollisionHooks>,
151                ),
152                (With<C>, Without<ColliderDisabled>),
153            >,
154             mut trees: ResMut<ColliderTrees>,
155             mut moved_proxies: ResMut<MovedProxies>| {
156                let entity = trigger.entity;
157
158                let Ok((
159                    mut proxy_key,
160                    enlarged_aabb,
161                    layers,
162                    is_sensor,
163                    has_contact_events,
164                    active_hooks,
165                )) = collider_query.get_mut(entity)
166                else {
167                    return;
168                };
169
170                // Remove the proxy from its current tree.
171                let tree = trees.tree_for_type_mut(proxy_key.tree_type());
172                if tree.remove_proxy(proxy_key.id()).is_none() {
173                    return;
174                }
175                moved_proxies.remove(&proxy_key);
176
177                // If the collider still exists, move it to the standalone tree.
178                let proxy = ColliderTreeProxy {
179                    collider: entity,
180                    body: None,
181                    layers: layers.copied().unwrap_or_default(),
182                    flags: ColliderTreeProxyFlags::new(
183                        is_sensor,
184                        false,
185                        has_contact_events,
186                        active_hooks.copied().unwrap_or_default(),
187                    ),
188                };
189
190                let standalone_tree = &mut trees.standalone_tree;
191                let proxy_id = standalone_tree.add_proxy(Aabb::from(enlarged_aabb.get()), proxy);
192                let new_proxy_key =
193                    ColliderTreeProxyKey::new(proxy_id, ColliderTreeType::Standalone);
194
195                // Store the new proxy key.
196                *proxy_key = new_proxy_key;
197
198                // Mark the proxy as moved.
199                moved_proxies.insert(new_proxy_key);
200            },
201        );
202
203        // Cases 4
204        // Note: We use `Discard` here to run before Case 2.
205        app.add_observer(
206            add_to_tree_on::<Discard<Disabled>, (Without<ColliderDisabled>, Allow<Disabled>)>,
207        );
208        app.add_observer(add_to_tree_on::<Discard<ColliderDisabled>, ()>);
209
210        // Case 5
211        app.add_observer(
212            remove_from_tree_on::<Add<Disabled>, (Without<ColliderDisabled>, Allow<Disabled>)>,
213        );
214        app.add_observer(remove_from_tree_on::<Add<ColliderDisabled>, ()>);
215
216        // Case 6
217        app.add_observer(
218            |trigger: On<Insert<RigidBody>>,
219             body_query: Query<(&RigidBody, &RigidBodyColliders, Has<RigidBodyDisabled>)>,
220             mut collider_query: Query<
221                (
222                    &EnlargedAabb,
223                    &mut ColliderTreeProxyKey,
224                    Option<&CollisionLayers>,
225                    Has<Sensor>,
226                    Has<CollisionEventsEnabled>,
227                    Option<&ActiveCollisionHooks>,
228                ),
229                Without<ColliderDisabled>,
230            >,
231             mut trees: ResMut<ColliderTrees>,
232             mut moved_proxies: ResMut<MovedProxies>| {
233                let entity = trigger.entity;
234
235                let Ok((new_rb, body_colliders, is_body_disabled)) = body_query.get(entity) else {
236                    return;
237                };
238
239                for collider_entity in body_colliders.iter() {
240                    let Ok((
241                        enlarged_aabb,
242                        mut proxy_key,
243                        layers,
244                        is_sensor,
245                        has_contact_events,
246                        active_hooks,
247                    )) = collider_query.get_mut(collider_entity)
248                    else {
249                        continue;
250                    };
251
252                    let new_tree_type = ColliderTreeType::from_body(Some(*new_rb));
253
254                    if new_tree_type == proxy_key.tree_type() {
255                        // No tree change.
256                        break;
257                    }
258
259                    // Remove the old proxy from its current tree.
260                    let old_tree = trees.tree_for_type_mut(proxy_key.tree_type());
261                    old_tree.remove_proxy(proxy_key.id());
262                    moved_proxies.remove(&proxy_key);
263
264                    // Insert the proxy into the new tree.
265                    let enlarged_aabb = Aabb::from(enlarged_aabb.get());
266
267                    let proxy = ColliderTreeProxy {
268                        collider: collider_entity,
269                        body: Some(entity),
270                        layers: layers.copied().unwrap_or_default(),
271                        flags: ColliderTreeProxyFlags::new(
272                            is_sensor,
273                            is_body_disabled,
274                            has_contact_events,
275                            active_hooks.copied().unwrap_or_default(),
276                        ),
277                    };
278
279                    let new_tree = trees.tree_for_type_mut(new_tree_type);
280                    let proxy_id = new_tree.add_proxy(enlarged_aabb, proxy);
281                    let new_proxy_key = ColliderTreeProxyKey::new(proxy_id, new_tree_type);
282
283                    // Store the new proxy key.
284                    *proxy_key = new_proxy_key;
285
286                    // Mark the proxy as moved.
287                    moved_proxies.insert(new_proxy_key);
288                }
289            },
290        );
291
292        // Case 7
293        app.add_observer(
294            |trigger: On<Add<Sensor>>,
295             mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
296             mut trees: ResMut<ColliderTrees>| {
297                let entity = trigger.entity;
298
299                let Ok(proxy_key) = collider_query.get_mut(entity) else {
300                    return;
301                };
302
303                let tree = trees.tree_for_type_mut(proxy_key.tree_type());
304
305                // Set sensor flag.
306                if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
307                    proxy.flags.insert(ColliderTreeProxyFlags::SENSOR);
308                }
309            },
310        );
311
312        // Case 8
313        app.add_observer(
314            |trigger: On<Remove<Sensor>>,
315             mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
316             mut trees: ResMut<ColliderTrees>| {
317                let entity = trigger.entity;
318
319                let Ok(proxy_key) = collider_query.get_mut(entity) else {
320                    return;
321                };
322
323                let tree = trees.tree_for_type_mut(proxy_key.tree_type());
324
325                // Unset sensor flag.
326                if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
327                    proxy.flags.remove(ColliderTreeProxyFlags::SENSOR);
328                }
329            },
330        );
331
332        // Case 9
333        app.add_observer(
334            |trigger: On<Insert<CollisionLayers>>,
335             mut collider_query: Query<
336                (&ColliderTreeProxyKey, Option<&CollisionLayers>),
337                Without<ColliderDisabled>,
338            >,
339             mut trees: ResMut<ColliderTrees>| {
340                let entity = trigger.entity;
341
342                let Ok((proxy_key, layers)) = collider_query.get_mut(entity) else {
343                    return;
344                };
345
346                let tree = trees.tree_for_type_mut(proxy_key.tree_type());
347
348                // Update layers.
349                if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
350                    proxy.layers = layers.copied().unwrap_or_default();
351                }
352            },
353        );
354
355        // Case 10
356        app.add_observer(
357            |trigger: On<Insert<ActiveCollisionHooks>>,
358             mut collider_query: Query<
359                (&ColliderTreeProxyKey, Option<&ActiveCollisionHooks>),
360                Without<ColliderDisabled>,
361            >,
362             mut trees: ResMut<ColliderTrees>| {
363                let entity = trigger.entity;
364
365                let Ok((proxy_key, active_hooks)) = collider_query.get_mut(entity) else {
366                    return;
367                };
368
369                let tree = trees.tree_for_type_mut(proxy_key.tree_type());
370
371                // Update active hooks flags.
372                if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
373                    proxy.flags.set(
374                        ColliderTreeProxyFlags::CUSTOM_FILTER,
375                        active_hooks
376                            .is_some_and(|h| h.contains(ActiveCollisionHooks::FILTER_PAIRS)),
377                    );
378                }
379            },
380        );
381
382        // Case 11
383        app.add_observer(
384            |trigger: On<Discard<RigidBodyDisabled>>,
385             body_query: Query<(&RigidBodyColliders, Has<RigidBodyDisabled>)>,
386             mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
387             mut trees: ResMut<ColliderTrees>| {
388                let entity = trigger.entity;
389
390                let Ok((body_colliders, is_body_disabled)) = body_query.get(entity) else {
391                    return;
392                };
393
394                for collider_entity in body_colliders.iter() {
395                    let Ok(proxy_key) = collider_query.get_mut(collider_entity) else {
396                        continue;
397                    };
398
399                    let tree = trees.tree_for_type_mut(proxy_key.tree_type());
400
401                    // Update body disabled flag.
402                    if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
403                        proxy
404                            .flags
405                            .set(ColliderTreeProxyFlags::BODY_DISABLED, is_body_disabled);
406                    }
407                }
408            },
409        );
410    }
411}
412
413/// Adds a collider to the appropriate collider tree when the event `E` is triggered.
414fn add_to_tree_on<E: EventPattern<Event: EntityEvent>, F: QueryFilter>(
415    trigger: On<E>,
416    body_query: Query<(&RigidBody, Has<RigidBodyDisabled>), Allow<Disabled>>,
417    mut collider_query: Query<
418        (
419            Option<&ColliderOf>,
420            &EnlargedAabb,
421            &mut ColliderTreeProxyKey,
422            Option<&CollisionLayers>,
423            Has<Sensor>,
424            Has<CollisionEventsEnabled>,
425            Option<&ActiveCollisionHooks>,
426            Has<RigidBody>,
427        ),
428        F,
429    >,
430    mut trees: ResMut<ColliderTrees>,
431    mut moved_proxies: ResMut<MovedProxies>,
432) {
433    let entity = trigger.event_target();
434
435    let Ok((
436        collider_of,
437        enlarged_aabb,
438        mut proxy_key,
439        layers,
440        is_sensor,
441        has_contact_events,
442        active_hooks,
443        is_body,
444    )) = collider_query.get_mut(entity)
445    else {
446        return;
447    };
448
449    // If the collider is on the same entity as a rigid body but does not have `ColliderOf` yet,
450    // it is about to be inserted by the `ColliderHierarchyPlugin`. Adding it to the standalone tree
451    // here would just be undone, so we wait for the `ColliderOf` insertion instead.
452    if is_body && collider_of.is_none() && *proxy_key == ColliderTreeProxyKey::PLACEHOLDER {
453        return;
454    }
455
456    let (tree_type, is_body_disabled) =
457        if let Some(Ok((rb, disabled))) = collider_of.map(|c| body_query.get(c.body)) {
458            (ColliderTreeType::from_body(Some(*rb)), disabled)
459        } else {
460            (ColliderTreeType::Standalone, false)
461        };
462
463    let proxy = ColliderTreeProxy {
464        collider: entity,
465        body: collider_of.map(|c| c.body),
466        layers: layers.copied().unwrap_or_default(),
467        flags: ColliderTreeProxyFlags::new(
468            is_sensor,
469            is_body_disabled,
470            has_contact_events,
471            active_hooks.copied().unwrap_or_default(),
472        ),
473    };
474
475    // Remove the old proxy if it exists.
476    if *proxy_key != ColliderTreeProxyKey::PLACEHOLDER {
477        let old_tree_type = proxy_key.tree_type();
478        let old_tree = trees.tree_for_type_mut(old_tree_type);
479
480        // If the collider is already in the right tree, update the existing proxy in place.
481        if old_tree_type == tree_type
482            && let Some(old_proxy) = old_tree.get_proxy_mut(proxy_key.id())
483        {
484            *old_proxy = proxy;
485            old_tree.resize_proxy_aabb(proxy_key.id(), Aabb::from(enlarged_aabb.get()));
486            moved_proxies.insert(*proxy_key);
487            old_tree.moved_proxies.push(proxy_key.id());
488            return;
489        }
490
491        old_tree.remove_proxy(proxy_key.id());
492        moved_proxies.remove(&proxy_key);
493    }
494
495    // Insert the proxy into the appropriate tree.
496    let tree = trees.tree_for_type_mut(tree_type);
497    let proxy_id = tree.add_proxy(Aabb::from(enlarged_aabb.get()), proxy);
498
499    // Store the proxy key.
500    *proxy_key = ColliderTreeProxyKey::new(proxy_id, tree_type);
501
502    // Mark the proxy as moved.
503    moved_proxies.insert(*proxy_key);
504}
505
506/// Removes a collider from its collider tree when the event `E` is triggered.
507fn remove_from_tree_on<E: EventPattern<Event: EntityEvent>, F: QueryFilter>(
508    trigger: On<E>,
509    mut collider_query: Query<&mut ColliderTreeProxyKey, F>,
510    mut trees: ResMut<ColliderTrees>,
511    mut moved_proxies: ResMut<MovedProxies>,
512) {
513    let entity = trigger.event_target();
514
515    let Ok(mut proxy_key) = collider_query.get_mut(entity) else {
516        return;
517    };
518
519    if *proxy_key == ColliderTreeProxyKey::PLACEHOLDER {
520        return;
521    }
522
523    // Remove the proxy from its current tree.
524    let tree = trees.tree_for_type_mut(proxy_key.tree_type());
525    tree.remove_proxy(proxy_key.id());
526    moved_proxies.remove(&proxy_key);
527
528    // Invalidate the proxy key.
529    *proxy_key = ColliderTreeProxyKey::PLACEHOLDER;
530}
531
532/// A resource for tracking the last system change tick
533/// when dynamic or kinematic collider AABBs were updated.
534#[derive(Resource, Default)]
535struct LastDynamicKinematicAabbUpdate(Tick);
536
537/// A resource for tracking moved proxies.
538///
539/// Moved proxies are those whose [`ColliderAabb`] has moved outside of their
540/// previous [`EnlargedAabb`], or whose collider has been added to a [`ColliderTree`].
541///
542/// [`ColliderTree`]: crate::collider_tree::ColliderTree
543#[derive(Resource, Default, Clone)]
544pub struct MovedProxies {
545    /// A vector of moved proxy keys.
546    proxies: Vec<ColliderTreeProxyKey>,
547    /// Maps a moved proxy key to its index in `proxies`.
548    indices: HashMap<ColliderTreeProxyKey, u32>,
549}
550
551impl MovedProxies {
552    /// Returns the keys of the moved proxies.
553    ///
554    /// The order of the keys is the order in which they were inserted.
555    #[inline]
556    pub fn proxies(&self) -> &[ColliderTreeProxyKey] {
557        &self.proxies
558    }
559
560    /// Returns `true` if the proxy with the given key has moved.
561    #[inline]
562    pub fn contains(&self, proxy_key: ColliderTreeProxyKey) -> bool {
563        self.indices.contains_key(&proxy_key)
564    }
565
566    /// Inserts a moved proxy key.
567    ///
568    /// Returns `true` if the proxy key was not already present.
569    #[inline]
570    pub fn insert(&mut self, proxy_key: ColliderTreeProxyKey) -> bool {
571        let index = self.proxies.len() as u32;
572        if self.indices.try_insert(proxy_key, index).is_ok() {
573            self.proxies.push(proxy_key);
574            true
575        } else {
576            false
577        }
578    }
579
580    /// Removes a moved proxy key. This may change the order of the remaining keys.
581    ///
582    /// If the proxy key is not present, nothing happens.
583    #[inline]
584    pub fn remove(&mut self, proxy_key: &ColliderTreeProxyKey) {
585        let Some(index) = self.indices.remove(proxy_key) else {
586            return;
587        };
588
589        let index = index as usize;
590        self.proxies.swap_remove(index);
591
592        // The key that was swapped into the removed slot needs its index updated.
593        if let Some(swapped) = self.proxies.get(index) {
594            self.indices.insert(*swapped, index as u32);
595        }
596    }
597
598    /// Clears the moved proxies.
599    #[inline]
600    pub fn clear(&mut self) {
601        self.proxies.clear();
602        self.indices.clear();
603    }
604}
605
606/// Bit vectors for tracking dynamic and kinematic proxies whose
607/// [`ColliderAabb`] has moved outside of the previous [`EnlargedAabb`].
608///
609/// Set bits indicate [`ProxyId`]s of moved proxies.
610#[derive(Resource, Default)]
611pub struct EnlargedProxies {
612    // Note: Box2D indexes by shape ID, so it only needs one bit vector.
613    //       In our case, we would instead index by entity ID, but this would
614    //       require a potentially huge and very sparse bit vector since not
615    //       all entities are colliders. So we use separate bit vectors for
616    //       different proxy types, and index by proxy ID instead.
617    dynamic_proxies: EnlargedProxiesBitVec,
618    kinematic_proxies: EnlargedProxiesBitVec,
619    static_proxies: EnlargedProxiesBitVec,
620    standalone_proxies: EnlargedProxiesBitVec,
621}
622
623impl EnlargedProxies {
624    /// Returns the bit vector for the given [`ColliderTreeType`].
625    #[inline]
626    pub const fn bit_vec_for_type(&self, tree_type: ColliderTreeType) -> &EnlargedProxiesBitVec {
627        match tree_type {
628            ColliderTreeType::Dynamic => &self.dynamic_proxies,
629            ColliderTreeType::Kinematic => &self.kinematic_proxies,
630            ColliderTreeType::Static => &self.static_proxies,
631            ColliderTreeType::Standalone => &self.standalone_proxies,
632        }
633    }
634
635    /// Returns a mutable reference to the bit vector for the given [`ColliderTreeType`].
636    #[inline]
637    pub fn bit_vec_for_type_mut(
638        &mut self,
639        tree_type: ColliderTreeType,
640    ) -> &mut EnlargedProxiesBitVec {
641        match tree_type {
642            ColliderTreeType::Dynamic => &mut self.dynamic_proxies,
643            ColliderTreeType::Kinematic => &mut self.kinematic_proxies,
644            ColliderTreeType::Static => &mut self.static_proxies,
645            ColliderTreeType::Standalone => &mut self.standalone_proxies,
646        }
647    }
648}
649
650/// Bit vectors for tracking proxies whose [`ColliderAabb`] has moved outside of the previous [`EnlargedAabb`].
651///
652/// Set bits indicate [`ProxyId`]s of moved proxies.
653///
654/// [`ProxyId`]: crate::collider_tree::ProxyId
655// TODO: We have a few of these now. We should maybe abstract this into a reusable structure.
656#[derive(Default)]
657pub struct EnlargedProxiesBitVec {
658    global: BitVec,
659    thread_local: ThreadLocal<RefCell<BitVec>>,
660}
661
662impl EnlargedProxiesBitVec {
663    /// Clears the enlarged proxies and sets the capacity of the internal structures.
664    #[inline]
665    pub fn clear_and_set_capacity(&mut self, capacity: usize) {
666        self.global.set_bit_count_and_clear(capacity);
667        self.thread_local.iter_mut().for_each(|context| {
668            let bit_vec_mut = &mut context.borrow_mut();
669            bit_vec_mut.set_bit_count_and_clear(capacity);
670        });
671    }
672
673    /// Combines the thread-local enlarged proxy bit vectors into the global one.
674    #[inline]
675    pub fn combine_thread_local(&mut self) {
676        self.thread_local.iter_mut().for_each(|context| {
677            let thread_local = context.borrow();
678            self.global.or(&thread_local);
679        });
680    }
681}
682
683/// Updates the AABBs of the colliders of each [`SolverBody`] (awake dynamic and kinematic bodies)
684/// after the physics step.
685///
686/// [`SolverBody`]: crate::dynamics::solver::solver_body::SolverBody
687// TODO: Once dynamic an kinematic bodies have their own marker components,
688//       we should use those instead of `SolverBody`. Solver bodies should
689//       be an implementation detail of the solver.
690fn update_solver_body_aabbs<C: AnyCollider>(
691    body_query: Query<
692        (
693            &Position,
694            &ComputedCenterOfMass,
695            &LinearVelocity,
696            &AngularVelocity,
697            &RigidBodyColliders,
698            Option<&SpeculativeCcd>,
699        ),
700        With<SolverBodyIndex>,
701    >,
702    mut colliders: ParamSet<(
703        Query<
704            (
705                Ref<C>,
706                &mut ColliderAabb,
707                &mut EnlargedAabb,
708                &mut ColliderAabbMargin,
709                &ColliderTreeProxyKey,
710                &Position,
711                &Rotation,
712                Option<&CollisionMargin>,
713            ),
714            Without<ColliderDisabled>,
715        >,
716        Query<&EnlargedAabb, Without<ColliderDisabled>>,
717    )>,
718    narrow_phase_config: Res<NarrowPhaseConfig>,
719    length_unit: Res<PhysicsLengthUnit>,
720    time: Res<Time>,
721    mut trees: ResMut<ColliderTrees>,
722    mut moved_proxies: ResMut<MovedProxies>,
723    mut enlarged_proxies: ResMut<EnlargedProxies>,
724    collider_context: StaticSystemParam<C::Context>,
725    mut diagnostics: ResMut<ColliderTreeDiagnostics>,
726    mut last_tick: ResMut<LastDynamicKinematicAabbUpdate>,
727    system_tick: SystemChangeTick,
728) {
729    let start = crate::utils::Instant::now();
730
731    let this_run = system_tick.this_run();
732
733    // An upper bound on the number of proxies, for sizing the bit vectors.
734    // TODO: Use a better way to track the number of proxies.
735    let cap_dynamic = trees.dynamic_tree.proxies.capacity();
736    let cap_kinematic = trees.kinematic_tree.proxies.capacity();
737
738    // Clear and resize the enlarged proxy structures.
739    let e = &mut enlarged_proxies;
740    e.dynamic_proxies.clear_and_set_capacity(cap_dynamic);
741    e.kinematic_proxies.clear_and_set_capacity(cap_kinematic);
742
743    // A small, fixed contact tolerance is added to each AABB so the narrow phase
744    // can predict slightly separated contacts.
745    let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
746
747    let delta_secs = time.delta_secs();
748
749    let collider_query = colliders.p0();
750
751    body_query.par_for_each(
752        MIN_PAR_ITER_ENTITIES,
753        |(rb_pos, center_of_mass, lin_vel, ang_vel, body_colliders, speculative_ccd)| {
754            for collider_entity in body_colliders.iter() {
755                let Ok((
756                    collider,
757                    mut aabb,
758                    mut enlarged_aabb,
759                    mut aabb_margin,
760                    proxy_key,
761                    pos,
762                    rot,
763                    collision_margin,
764                )) = (unsafe { collider_query.get_unchecked(collider_entity) })
765                else {
766                    continue;
767                };
768
769                let collision_margin = collision_margin.map_or(0.0, |margin| margin.0);
770
771                let context = ColliderContext::new(collider_entity, &*collider_context);
772                let growth = contact_tolerance + collision_margin;
773
774                *aabb = if let Some(max_distance) = speculative_ccd.map(|s| s.max_distance) {
775                    // Opt-in velocity-expanded AABB: sweep the collider from its current pose to
776                    // where it would be next frame, so the broad phase and swept CCD can predict
777                    // contacts ahead. Note that this is only really needed by CCD for cases like
778                    // two fast-moving objects coming into contact from different directions,
779                    // because with tight AABBs, sweeping the shapes would not find any AABB intersection.
780
781                    // Velocity of this collider. For off-center (child) colliders on a rotating
782                    // body, the collider orbits the center of mass, which adds to its velocity.
783                    let offset = (pos.0 - rb_pos.0).f32() - center_of_mass.0;
784                    #[cfg(feature = "2d")]
785                    let vel = lin_vel.0 + Vec2::new(-ang_vel.0 * offset.y, ang_vel.0 * offset.x);
786                    #[cfg(feature = "3d")]
787                    let vel = lin_vel.0 + ang_vel.0.cross(offset);
788
789                    // Expand the AABB along the velocity, but no further than the speculative margin.
790                    let movement = (vel * delta_secs).clamp_length_max(max_distance);
791
792                    #[cfg(feature = "2d")]
793                    let end_rot = *rot * Rotation::radians(ang_vel.0 * delta_secs);
794                    #[cfg(feature = "3d")]
795                    let end_rot =
796                        (Quat::from_scaled_axis(ang_vel.0 * delta_secs) * rot.0)
797                            .fast_renormalize();
798
799                    collider
800                        .swept_aabb_with_context(pos.0, *rot, pos.0 + movement.real(), end_rot,growth, context)
801                } else {
802                    collider
803                        .aabb_with_context(pos.0, *rot, growth, context)
804                };
805
806                // Recompute the cached AABB margin if the collider shape changed.
807                if collider.is_changed() {
808                    let context = ColliderContext::new(collider_entity, &*collider_context);
809                    *aabb_margin = ColliderAabbMargin::from_bounding_radius(
810                        collider.bounding_radius_with_context(context),
811                        length_unit.0,
812                    );
813                }
814
815                // Solver bodies are always dynamic or kinematic, so they use the size-relative
816                // AABB margin to allow some movement without triggering tree updates.
817                let moved = enlarged_aabb.update(&aabb, aabb_margin.0);
818
819                if moved {
820                    let tree_type = proxy_key.tree_type();
821                    let mut thread_local_bit_vec = enlarged_proxies
822                        .bit_vec_for_type(tree_type)
823                        .thread_local
824                        .get_or(|| {
825                            let capacity = match tree_type {
826                                ColliderTreeType::Dynamic => cap_dynamic,
827                                ColliderTreeType::Kinematic => cap_kinematic,
828                                _ => unreachable!("Static or standalone proxy {proxy_key:?} moved in dynamic AABB update"),
829                            };
830                            let mut bit_vec = BitVec::new(capacity);
831                            bit_vec.set_bit_count_and_clear(capacity);
832                            RefCell::new(bit_vec)
833                        })
834                        .borrow_mut();
835
836                    thread_local_bit_vec.set(proxy_key.id().index());
837                }
838            }
839        },
840    );
841
842    // Update the AABBs of moved proxies in the dynamic and kinematic trees.
843    let aabb_query = colliders.p1();
844    for &tree_type in &[ColliderTreeType::Dynamic, ColliderTreeType::Kinematic] {
845        update_tree_for_moved_proxies(
846            tree_type,
847            trees.tree_for_type_mut(tree_type),
848            enlarged_proxies.bit_vec_for_type_mut(tree_type),
849            &aabb_query,
850            &mut moved_proxies,
851        );
852    }
853
854    // Update the last update tick.
855    // TODO: Remove this
856    last_tick.0 = this_run;
857
858    diagnostics.update += start.elapsed();
859}
860
861/// Updates the AABBs of colliders that have been manually moved after the previous physics step.
862pub fn update_moved_collider_aabbs<C: AnyCollider>(
863    mut colliders: ParamSet<(
864        Query<
865            (
866                Entity,
867                Ref<Position>,
868                Ref<Rotation>,
869                &mut ColliderAabb,
870                &mut EnlargedAabb,
871                &mut ColliderAabbMargin,
872                Ref<C>,
873                Option<&CollisionMargin>,
874                &ColliderTreeProxyKey,
875            ),
876            Without<ColliderDisabled>,
877        >,
878        Query<&EnlargedAabb, Without<ColliderDisabled>>,
879    )>,
880    narrow_phase_config: Res<NarrowPhaseConfig>,
881    length_unit: Res<PhysicsLengthUnit>,
882    mut trees: ResMut<ColliderTrees>,
883    mut moved_proxies: ResMut<MovedProxies>,
884    mut enlarged_proxies: ResMut<EnlargedProxies>,
885    collider_context: StaticSystemParam<C::Context>,
886    mut diagnostics: ResMut<ColliderTreeDiagnostics>,
887    last_tick: Res<LastPhysicsTick>,
888    system_tick: SystemChangeTick,
889) {
890    let start = crate::utils::Instant::now();
891
892    let this_run = system_tick.this_run();
893
894    // An upper bound on the number of proxies, for sizing the bit vectors.
895    let cap_dynamic = trees.dynamic_tree.proxies.capacity();
896    let cap_kinematic = trees.kinematic_tree.proxies.capacity();
897    let cap_static = trees.static_tree.proxies.capacity();
898    let cap_standalone = trees.standalone_tree.proxies.capacity();
899
900    // Clear and resize the enlarged proxy structures.
901    let e = &mut enlarged_proxies;
902    e.dynamic_proxies.clear_and_set_capacity(cap_dynamic);
903    e.kinematic_proxies.clear_and_set_capacity(cap_kinematic);
904    e.static_proxies.clear_and_set_capacity(cap_static);
905    e.standalone_proxies.clear_and_set_capacity(cap_standalone);
906
907    // A small, fixed contact tolerance is added to each AABB so the narrow phase
908    // can predict slightly separated contacts.
909    let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
910
911    // TODO: This doesn't do velocity-based enlargement like the dynamic/kinematic AABB update.
912    //       We should overall rework CCD to not rely on velocity-based AABB enlargement for all bodies.
913    // TODO: par-iter over all colliders, check if they have actually changed since the `LastPhysicsTick`
914    let mut collider_query = colliders.p0();
915    collider_query.par_for_each_mut(
916        MIN_PAR_ITER_ENTITIES,
917        |(
918            entity,
919            pos,
920            rot,
921            mut aabb,
922            mut enlarged_aabb,
923            mut aabb_margin,
924            collider,
925            collision_margin,
926            proxy_key,
927        )| {
928            // Skip if the collider's AABB can't have changed since the last physics tick.
929            let collider_changed = collider.last_changed().is_newer_than(last_tick.0, this_run);
930            if !pos.last_changed().is_newer_than(last_tick.0, this_run)
931                && !rot.last_changed().is_newer_than(last_tick.0, this_run)
932                && !collider_changed
933            {
934                return;
935            }
936
937            let collision_margin = collision_margin.map_or(0.0, |margin| margin.0);
938
939            // Update tight-fitting AABB.
940            let context = ColliderContext::new(entity, &*collider_context);
941            let growth = contact_tolerance + collision_margin;
942            *aabb = collider.aabb_with_context(pos.0, *rot, growth, context);
943
944            // Recompute the cached AABB margin if the collider shape changed.
945            if collider_changed {
946                let context = ColliderContext::new(entity, &*collider_context);
947                *aabb_margin = ColliderAabbMargin::from_bounding_radius(
948                    collider.bounding_radius_with_context(context),
949                    length_unit.0,
950                );
951            }
952
953            // Dynamic and kinematic colliders use the size-relative AABB margin, while static and
954            // standalone colliders only use the smaller contact tolerance to keep their AABBs tight.
955            let margin = match proxy_key.tree_type() {
956                ColliderTreeType::Dynamic | ColliderTreeType::Kinematic => aabb_margin.0,
957                ColliderTreeType::Static | ColliderTreeType::Standalone => contact_tolerance,
958            };
959
960            // Try to update the enlarged AABB, and if it changed, mark the proxy as moved.
961            let moved = enlarged_aabb.update(&aabb, margin);
962
963            if moved {
964                let tree_type = proxy_key.tree_type();
965                let mut thread_local_bit_vec = enlarged_proxies
966                    .bit_vec_for_type(tree_type)
967                    .thread_local
968                    .get_or(|| {
969                        let capacity = match tree_type {
970                            ColliderTreeType::Dynamic => cap_dynamic,
971                            ColliderTreeType::Kinematic => cap_kinematic,
972                            ColliderTreeType::Static => cap_static,
973                            ColliderTreeType::Standalone => cap_standalone,
974                        };
975                        let mut bit_vec = BitVec::new(capacity);
976                        bit_vec.set_bit_count_and_clear(capacity);
977                        RefCell::new(bit_vec)
978                    })
979                    .borrow_mut();
980
981                thread_local_bit_vec.set(proxy_key.id().index());
982            }
983        },
984    );
985
986    // Reinsert moved proxies in each tree.
987    let aabb_query = colliders.p1();
988    for tree_type in ColliderTreeType::ALL {
989        update_tree_for_moved_proxies(
990            tree_type,
991            trees.tree_for_type_mut(tree_type),
992            enlarged_proxies.bit_vec_for_type_mut(tree_type),
993            &aabb_query,
994            &mut moved_proxies,
995        );
996    }
997
998    diagnostics.update += start.elapsed();
999}
1000
1001/// Applies the [`EnlargedAabb`]s of the proxies flagged in `bit_vec` to the tree,
1002/// and refits the BVH to account for the changes.
1003fn update_tree_for_moved_proxies(
1004    tree_type: ColliderTreeType,
1005    tree: &mut ColliderTree,
1006    bit_vec: &mut EnlargedProxiesBitVec,
1007    aabb_query: &Query<&EnlargedAabb, Without<ColliderDisabled>>,
1008    moved_proxies: &mut MovedProxies,
1009) {
1010    tree.bvh.init_primitives_to_nodes_if_uninit();
1011    bit_vec.combine_thread_local();
1012
1013    let moved_count = bit_vec.global.count_ones();
1014    if moved_count == 0 {
1015        return;
1016    }
1017    let moved_ratio = moved_count as f32 / tree.proxies.len().max(1) as f32;
1018
1019    // For a small number of moved proxies, it's more efficient to refit up from just those leaves.
1020    // Otherwise, it's better to refit the entire tree once after updating all moved proxies.
1021    //
1022    // The crossover depends on whether the BVH nodes are currently ordered with children after parents.
1023    // If they are, `refit_all` is significantly cheaper. This is the case after full rebuilds.
1024    let full_refit_threshold = if tree.bvh.children_are_ordered_after_parents {
1025        0.06
1026    } else {
1027        0.3
1028    };
1029
1030    if moved_ratio < full_refit_threshold {
1031        update_tree(
1032            tree_type,
1033            tree,
1034            &bit_vec.global,
1035            aabb_query,
1036            moved_proxies,
1037            |tree, proxy_id, enlarged_aabb| {
1038                tree.resize_proxy_aabb(proxy_id, enlarged_aabb);
1039            },
1040        );
1041    } else {
1042        update_tree(
1043            tree_type,
1044            tree,
1045            &bit_vec.global,
1046            aabb_query,
1047            moved_proxies,
1048            |tree, proxy_id, enlarged_aabb| {
1049                tree.set_proxy_aabb(proxy_id, enlarged_aabb);
1050            },
1051        );
1052        tree.refit_all();
1053    }
1054}
1055
1056/// Updates the collider tree for the moved proxies indicated in the given bit vector.
1057fn update_tree(
1058    tree_type: ColliderTreeType,
1059    tree: &mut ColliderTree,
1060    bit_vec: &BitVec,
1061    aabbs: &Query<&EnlargedAabb, Without<ColliderDisabled>>,
1062    moved_proxies: &mut MovedProxies,
1063    update_proxy_fn: impl Fn(&mut ColliderTree, ProxyId, Aabb),
1064) {
1065    for (i, mut bits) in bit_vec.blocks().enumerate() {
1066        while bits != 0 {
1067            let trailing_zeros = bits.trailing_zeros();
1068            let proxy_id = ProxyId::new(i as u32 * 64 + trailing_zeros);
1069            let proxy = &mut tree.proxies[proxy_id.index()];
1070            let entity = proxy.collider;
1071
1072            let enlarged_aabb = aabbs.get(entity).unwrap_or_else(|_| {
1073                panic!(
1074                    "EnlargedAabb missing for moved collider entity {:?}",
1075                    entity
1076                )
1077            });
1078
1079            // Update the proxy's AABB.
1080            update_proxy_fn(tree, proxy_id, Aabb::from(enlarged_aabb.get()));
1081
1082            // Record the moved proxy.
1083            let proxy_key = ColliderTreeProxyKey::new(proxy_id, tree_type);
1084            if moved_proxies.insert(proxy_key) {
1085                tree.moved_proxies.push(proxy_id);
1086            }
1087
1088            // Clear the least significant set bit
1089            bits &= bits - 1;
1090        }
1091    }
1092}
1093
1094fn clear_moved_proxies(mut moved_proxies: ResMut<MovedProxies>, mut trees: ResMut<ColliderTrees>) {
1095    moved_proxies.clear();
1096    trees.iter_trees_mut().for_each(|t| t.moved_proxies.clear());
1097}
1098
1099#[cfg(test)]
1100#[cfg(feature = "default-collider")]
1101mod tests {
1102    use super::*;
1103    use bevy::time::TimeUpdateStrategy;
1104    use core::time::Duration;
1105
1106    /// Removing a collider's rigid body (Case 3) moves its proxy to the
1107    /// standalone tree. The `ColliderTreeProxyKey` on the entity has to follow,
1108    /// or the entity is left addressing a slot in the old tree that it no
1109    /// longer owns.
1110    #[test]
1111    fn removing_rigid_body_updates_proxy_key() {
1112        let mut app = App::new();
1113        app.add_plugins((
1114            MinimalPlugins,
1115            TransformPlugin,
1116            PhysicsPlugins::default(),
1117            bevy::asset::AssetPlugin::default(),
1118            #[cfg(feature = "collider-from-mesh")]
1119            bevy::mesh::MeshPlugin,
1120        ))
1121        .insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f64(
1122            1.0 / 60.0,
1123        )));
1124        app.finish();
1125
1126        fn proxy_key(app: &App, entity: Entity) -> ColliderTreeProxyKey {
1127            *app.world().get::<ColliderTreeProxyKey>(entity).unwrap()
1128        }
1129
1130        let collider = app
1131            .world_mut()
1132            .spawn((
1133                RigidBody::Static,
1134                Collider::capsule(0.5, 2.0),
1135                Transform::default(),
1136            ))
1137            .id();
1138        app.update();
1139
1140        assert!(proxy_key(&app, collider).is_static());
1141
1142        app.world_mut().entity_mut(collider).remove::<RigidBody>();
1143        app.update();
1144
1145        let key = proxy_key(&app, collider);
1146        let trees = app.world().resource::<ColliderTrees>();
1147        assert!(
1148            key.is_standalone(),
1149            "proxy key should point to the standalone tree after the body is removed, got {key:?}"
1150        );
1151        assert_eq!(
1152            trees.get_proxy(key).map(|p| p.collider),
1153            Some(collider),
1154            "proxy key should resolve to this collider's own proxy"
1155        );
1156        assert_eq!(trees.static_tree.proxies.len(), 0);
1157        assert_eq!(trees.standalone_tree.proxies.len(), 1);
1158    }
1159}