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