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
30pub(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 app.init_resource::<MovedProxies>()
45 .init_resource::<EnlargedProxies>()
46 .init_resource::<LastDynamicKinematicAabbUpdate>();
47
48 app.add_systems(
51 PhysicsSchedule,
52 update_moved_collider_aabbs::<C>
53 .in_set(ColliderTreeSystems::UpdateAabbs)
54 .ambiguous_with_all(),
57 );
58
59 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 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 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 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 enlarged_aabb.update(&aabb, aabb_margin.0);
112 }
113 },
114 );
115
116 app.add_observer(add_to_tree_on::<Insert<(C, ColliderOf)>, Without<ColliderDisabled>>);
132
133 app.add_observer(remove_from_tree_on::<Remove<C>, Allow<Disabled>>);
139
140 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 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 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 *proxy_key = new_proxy_key;
197
198 moved_proxies.insert(new_proxy_key);
200 },
201 );
202
203 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 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 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 break;
257 }
258
259 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 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 *proxy_key = new_proxy_key;
285
286 moved_proxies.insert(new_proxy_key);
288 }
289 },
290 );
291
292 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 if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
307 proxy.flags.insert(ColliderTreeProxyFlags::SENSOR);
308 }
309 },
310 );
311
312 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 if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
327 proxy.flags.remove(ColliderTreeProxyFlags::SENSOR);
328 }
329 },
330 );
331
332 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 if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
350 proxy.layers = layers.copied().unwrap_or_default();
351 }
352 },
353 );
354
355 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 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 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 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
413fn 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 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 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 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 let tree = trees.tree_for_type_mut(tree_type);
497 let proxy_id = tree.add_proxy(Aabb::from(enlarged_aabb.get()), proxy);
498
499 *proxy_key = ColliderTreeProxyKey::new(proxy_id, tree_type);
501
502 moved_proxies.insert(*proxy_key);
504}
505
506fn 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 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 *proxy_key = ColliderTreeProxyKey::PLACEHOLDER;
530}
531
532#[derive(Resource, Default)]
535struct LastDynamicKinematicAabbUpdate(Tick);
536
537#[derive(Resource, Default, Clone)]
544pub struct MovedProxies {
545 proxies: Vec<ColliderTreeProxyKey>,
547 indices: HashMap<ColliderTreeProxyKey, u32>,
549}
550
551impl MovedProxies {
552 #[inline]
556 pub fn proxies(&self) -> &[ColliderTreeProxyKey] {
557 &self.proxies
558 }
559
560 #[inline]
562 pub fn contains(&self, proxy_key: ColliderTreeProxyKey) -> bool {
563 self.indices.contains_key(&proxy_key)
564 }
565
566 #[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 #[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 if let Some(swapped) = self.proxies.get(index) {
594 self.indices.insert(*swapped, index as u32);
595 }
596 }
597
598 #[inline]
600 pub fn clear(&mut self) {
601 self.proxies.clear();
602 self.indices.clear();
603 }
604}
605
606#[derive(Resource, Default)]
611pub struct EnlargedProxies {
612 dynamic_proxies: EnlargedProxiesBitVec,
618 kinematic_proxies: EnlargedProxiesBitVec,
619 static_proxies: EnlargedProxiesBitVec,
620 standalone_proxies: EnlargedProxiesBitVec,
621}
622
623impl EnlargedProxies {
624 #[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 #[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#[derive(Default)]
657pub struct EnlargedProxiesBitVec {
658 global: BitVec,
659 thread_local: ThreadLocal<RefCell<BitVec>>,
660}
661
662impl EnlargedProxiesBitVec {
663 #[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 #[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
683fn 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 let cap_dynamic = trees.dynamic_tree.proxies.capacity();
736 let cap_kinematic = trees.kinematic_tree.proxies.capacity();
737
738 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 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 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 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 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 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 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 last_tick.0 = this_run;
857
858 diagnostics.update += start.elapsed();
859}
860
861pub 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 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 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 let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
910
911 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 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 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 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 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 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 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
1001fn 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 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
1056fn 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_proxy_fn(tree, proxy_id, Aabb::from(enlarged_aabb.get()));
1081
1082 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 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 #[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}