use core::cell::RefCell;
use core::marker::PhantomData;
use crate::{
collider_tree::{
ColliderTree, ColliderTreeDiagnostics, ColliderTreeProxy, ColliderTreeProxyKey,
ColliderTreeSystems, ColliderTreeType, ColliderTrees, ProxyId,
tree::ColliderTreeProxyFlags,
},
collision::collider::EnlargedAabb,
data_structures::bit_vec::BitVec,
dynamics::solver::solver_body::SolverBody,
prelude::*,
schedule::LastPhysicsTick,
};
use bevy::{
ecs::{
change_detection::Tick,
entity_disabling::Disabled,
query::QueryFilter,
system::{StaticSystemParam, SystemChangeTick},
},
platform::collections::HashSet,
prelude::*,
};
use obvhs::aabb::Aabb;
use thread_local::ThreadLocal;
const AABB_MARGIN: Scalar = 0.05;
pub(super) struct ColliderTreeUpdatePlugin<C: AnyCollider>(PhantomData<C>);
impl<C: AnyCollider> Default for ColliderTreeUpdatePlugin<C> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<C: AnyCollider> Plugin for ColliderTreeUpdatePlugin<C> {
fn build(&self, app: &mut App) {
app.init_resource::<MovedProxies>()
.init_resource::<EnlargedProxies>()
.init_resource::<LastDynamicKinematicAabbUpdate>();
app.add_systems(
PhysicsSchedule,
update_moved_collider_aabbs::<C>
.in_set(ColliderTreeSystems::UpdateAabbs)
.ambiguous_with_all(),
);
app.add_systems(
PhysicsSchedule,
(clear_moved_proxies, update_solver_body_aabbs::<C>)
.chain()
.after(PhysicsStepSystems::Finalize)
.before(PhysicsStepSystems::Last),
);
app.add_observer(
|trigger: On<Add, C>,
mut query: Query<(
&C,
&Position,
&Rotation,
Option<&CollisionMargin>,
&mut ColliderAabb,
&mut EnlargedAabb,
)>,
narrow_phase_config: Res<NarrowPhaseConfig>,
length_unit: Res<PhysicsLengthUnit>,
collider_context: StaticSystemParam<C::Context>| {
let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
let margin = length_unit.0 * AABB_MARGIN;
if let Ok((collider, pos, rot, collision_margin, mut aabb, mut enlarged_aabb)) =
query.get_mut(trigger.entity)
{
let collision_margin = collision_margin.map_or(0.0, |m| m.0);
let context = AabbContext::new(trigger.entity, &*collider_context);
let growth = Vector::splat(contact_tolerance + collision_margin);
*aabb = collider
.aabb_with_context(pos.0, *rot, context)
.grow(growth);
enlarged_aabb.update(&aabb, margin);
}
},
);
app.add_observer(add_to_tree_on::<Insert, (C, ColliderOf), Without<ColliderDisabled>>);
app.add_observer(remove_from_tree_on::<Remove, C, Allow<Disabled>>);
app.add_observer(
|trigger: On<Remove, ColliderOf>,
mut collider_query: Query<
(
&ColliderTreeProxyKey,
&EnlargedAabb,
Option<&CollisionLayers>,
Has<Sensor>,
Has<CollisionEventsEnabled>,
Option<&ActiveCollisionHooks>,
),
(With<C>, Without<ColliderDisabled>),
>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>| {
let entity = trigger.entity;
let Ok((
proxy_key,
enlarged_aabb,
layers,
is_sensor,
has_contact_events,
active_hooks,
)) = collider_query.get_mut(entity)
else {
return;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if tree.remove_proxy(proxy_key.id()).is_none() {
return;
}
moved_proxies.remove(proxy_key);
let proxy = ColliderTreeProxy {
collider: entity,
body: None,
layers: layers.copied().unwrap_or_default(),
flags: ColliderTreeProxyFlags::new(
is_sensor,
false,
has_contact_events,
active_hooks.copied().unwrap_or_default(),
),
};
let standalone_tree = &mut trees.standalone_tree;
let proxy_id = standalone_tree.add_proxy(Aabb::from(enlarged_aabb.get()), proxy);
let new_proxy_key =
ColliderTreeProxyKey::new(proxy_id, ColliderTreeType::Standalone);
moved_proxies.insert(new_proxy_key);
},
);
app.add_observer(
add_to_tree_on::<Replace, Disabled, (Without<ColliderDisabled>, Allow<Disabled>)>,
);
app.add_observer(add_to_tree_on::<Replace, ColliderDisabled, ()>);
app.add_observer(
remove_from_tree_on::<Add, Disabled, (Without<ColliderDisabled>, Allow<Disabled>)>,
);
app.add_observer(remove_from_tree_on::<Add, ColliderDisabled, ()>);
app.add_observer(
|trigger: On<Insert, RigidBody>,
body_query: Query<(&RigidBody, &RigidBodyColliders, Has<RigidBodyDisabled>)>,
mut collider_query: Query<
(
&EnlargedAabb,
&mut ColliderTreeProxyKey,
Option<&CollisionLayers>,
Has<Sensor>,
Has<CollisionEventsEnabled>,
Option<&ActiveCollisionHooks>,
),
Without<ColliderDisabled>,
>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>| {
let entity = trigger.entity;
let Ok((new_rb, body_colliders, is_body_disabled)) = body_query.get(entity) else {
return;
};
for collider_entity in body_colliders.iter() {
let Ok((
enlarged_aabb,
mut proxy_key,
layers,
is_sensor,
has_contact_events,
active_hooks,
)) = collider_query.get_mut(collider_entity)
else {
continue;
};
let new_tree_type = ColliderTreeType::from_body(Some(*new_rb));
if new_tree_type == proxy_key.tree_type() {
break;
}
let old_tree = trees.tree_for_type_mut(proxy_key.tree_type());
old_tree.remove_proxy(proxy_key.id());
moved_proxies.remove(&proxy_key);
let enlarged_aabb = Aabb::from(enlarged_aabb.get());
let proxy = ColliderTreeProxy {
collider: collider_entity,
body: Some(entity),
layers: layers.copied().unwrap_or_default(),
flags: ColliderTreeProxyFlags::new(
is_sensor,
is_body_disabled,
has_contact_events,
active_hooks.copied().unwrap_or_default(),
),
};
let new_tree = trees.tree_for_type_mut(new_tree_type);
let proxy_id = new_tree.add_proxy(enlarged_aabb, proxy);
let new_proxy_key = ColliderTreeProxyKey::new(proxy_id, new_tree_type);
*proxy_key = new_proxy_key;
moved_proxies.insert(new_proxy_key);
}
},
);
app.add_observer(
|trigger: On<Add, Sensor>,
mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
mut trees: ResMut<ColliderTrees>| {
let entity = trigger.entity;
let Ok(proxy_key) = collider_query.get_mut(entity) else {
return;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
proxy.flags.insert(ColliderTreeProxyFlags::SENSOR);
}
},
);
app.add_observer(
|trigger: On<Remove, Sensor>,
mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
mut trees: ResMut<ColliderTrees>| {
let entity = trigger.entity;
let Ok(proxy_key) = collider_query.get_mut(entity) else {
return;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
proxy.flags.remove(ColliderTreeProxyFlags::SENSOR);
}
},
);
app.add_observer(
|trigger: On<Insert, CollisionLayers>,
mut collider_query: Query<
(&ColliderTreeProxyKey, Option<&CollisionLayers>),
Without<ColliderDisabled>,
>,
mut trees: ResMut<ColliderTrees>| {
let entity = trigger.entity;
let Ok((proxy_key, layers)) = collider_query.get_mut(entity) else {
return;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
proxy.layers = layers.copied().unwrap_or_default();
}
},
);
app.add_observer(
|trigger: On<Insert, ActiveCollisionHooks>,
mut collider_query: Query<
(&ColliderTreeProxyKey, Option<&ActiveCollisionHooks>),
Without<ColliderDisabled>,
>,
mut trees: ResMut<ColliderTrees>| {
let entity = trigger.entity;
let Ok((proxy_key, active_hooks)) = collider_query.get_mut(entity) else {
return;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
proxy.flags.set(
ColliderTreeProxyFlags::CUSTOM_FILTER,
active_hooks
.is_some_and(|h| h.contains(ActiveCollisionHooks::FILTER_PAIRS)),
);
}
},
);
app.add_observer(
|trigger: On<Replace, RigidBodyDisabled>,
body_query: Query<(&RigidBodyColliders, Has<RigidBodyDisabled>)>,
mut collider_query: Query<&ColliderTreeProxyKey, Without<ColliderDisabled>>,
mut trees: ResMut<ColliderTrees>| {
let entity = trigger.entity;
let Ok((body_colliders, is_body_disabled)) = body_query.get(entity) else {
return;
};
for collider_entity in body_colliders.iter() {
let Ok(proxy_key) = collider_query.get_mut(collider_entity) else {
continue;
};
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
if let Some(proxy) = tree.get_proxy_mut(proxy_key.id()) {
proxy
.flags
.set(ColliderTreeProxyFlags::BODY_DISABLED, is_body_disabled);
}
}
},
);
}
}
fn add_to_tree_on<E: EntityEvent, B: Bundle, F: QueryFilter>(
trigger: On<E, B>,
body_query: Query<(&RigidBody, Has<RigidBodyDisabled>), Allow<Disabled>>,
mut collider_query: Query<
(
Option<&ColliderOf>,
&EnlargedAabb,
&mut ColliderTreeProxyKey,
Option<&CollisionLayers>,
Has<Sensor>,
Has<CollisionEventsEnabled>,
Option<&ActiveCollisionHooks>,
),
F,
>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>,
) {
let entity = trigger.event_target();
let Ok((
collider_of,
enlarged_aabb,
mut proxy_key,
layers,
is_sensor,
has_contact_events,
active_hooks,
)) = collider_query.get_mut(entity)
else {
return;
};
let (tree_type, is_body_disabled) =
if let Some(Ok((rb, disabled))) = collider_of.map(|c| body_query.get(c.body)) {
(ColliderTreeType::from_body(Some(*rb)), disabled)
} else {
(ColliderTreeType::Standalone, false)
};
let proxy = ColliderTreeProxy {
collider: entity,
body: collider_of.map(|c| c.body),
layers: layers.copied().unwrap_or_default(),
flags: ColliderTreeProxyFlags::new(
is_sensor,
is_body_disabled,
has_contact_events,
active_hooks.copied().unwrap_or_default(),
),
};
if *proxy_key != ColliderTreeProxyKey::PLACEHOLDER {
let old_tree_type = proxy_key.tree_type();
let old_tree = trees.tree_for_type_mut(old_tree_type);
old_tree.remove_proxy(proxy_key.id());
moved_proxies.remove(&proxy_key);
}
let tree = trees.tree_for_type_mut(tree_type);
let proxy_id = tree.add_proxy(Aabb::from(enlarged_aabb.get()), proxy);
*proxy_key = ColliderTreeProxyKey::new(proxy_id, tree_type);
moved_proxies.insert(*proxy_key);
}
fn remove_from_tree_on<E: EntityEvent, B: Bundle, F: QueryFilter>(
trigger: On<E, B>,
mut collider_query: Query<&mut ColliderTreeProxyKey, F>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>,
) {
let entity = trigger.event_target();
let Ok(mut proxy_key) = collider_query.get_mut(entity) else {
return;
};
if *proxy_key == ColliderTreeProxyKey::PLACEHOLDER {
return;
}
let tree = trees.tree_for_type_mut(proxy_key.tree_type());
tree.remove_proxy(proxy_key.id());
moved_proxies.remove(&proxy_key);
*proxy_key = ColliderTreeProxyKey::PLACEHOLDER;
}
#[derive(Resource, Default)]
struct LastDynamicKinematicAabbUpdate(Tick);
#[derive(Resource, Default)]
pub struct MovedProxies {
proxies: Vec<ColliderTreeProxyKey>,
set: HashSet<ColliderTreeProxyKey>,
}
impl MovedProxies {
#[inline]
pub fn proxies(&self) -> &[ColliderTreeProxyKey] {
&self.proxies
}
#[inline]
pub fn contains(&self, proxy_key: ColliderTreeProxyKey) -> bool {
self.set.contains(&proxy_key)
}
#[inline]
pub fn insert(&mut self, proxy_key: ColliderTreeProxyKey) -> bool {
if self.set.insert(proxy_key) {
self.proxies.push(proxy_key);
true
} else {
false
}
}
#[inline]
pub fn remove(&mut self, proxy_key: &ColliderTreeProxyKey) {
if self.set.remove(proxy_key)
&& let Some(pos) = self.proxies.iter().position(|k| k == proxy_key)
{
self.proxies.swap_remove(pos);
}
}
#[inline]
pub fn clear(&mut self) {
self.proxies.clear();
self.set.clear();
}
}
#[derive(Resource, Default)]
pub struct EnlargedProxies {
dynamic_proxies: EnlargedProxiesBitVec,
kinematic_proxies: EnlargedProxiesBitVec,
static_proxies: EnlargedProxiesBitVec,
standalone_proxies: EnlargedProxiesBitVec,
}
impl EnlargedProxies {
#[inline]
pub const fn bit_vec_for_type(&self, tree_type: ColliderTreeType) -> &EnlargedProxiesBitVec {
match tree_type {
ColliderTreeType::Dynamic => &self.dynamic_proxies,
ColliderTreeType::Kinematic => &self.kinematic_proxies,
ColliderTreeType::Static => &self.static_proxies,
ColliderTreeType::Standalone => &self.standalone_proxies,
}
}
#[inline]
pub fn bit_vec_for_type_mut(
&mut self,
tree_type: ColliderTreeType,
) -> &mut EnlargedProxiesBitVec {
match tree_type {
ColliderTreeType::Dynamic => &mut self.dynamic_proxies,
ColliderTreeType::Kinematic => &mut self.kinematic_proxies,
ColliderTreeType::Static => &mut self.static_proxies,
ColliderTreeType::Standalone => &mut self.standalone_proxies,
}
}
}
#[derive(Default)]
pub struct EnlargedProxiesBitVec {
global: BitVec,
thread_local: ThreadLocal<RefCell<BitVec>>,
}
impl EnlargedProxiesBitVec {
#[inline]
pub fn clear_and_set_capacity(&mut self, capacity: usize) {
self.global.set_bit_count_and_clear(capacity);
self.thread_local.iter_mut().for_each(|context| {
let bit_vec_mut = &mut context.borrow_mut();
bit_vec_mut.set_bit_count_and_clear(capacity);
});
}
#[inline]
pub fn combine_thread_local(&mut self) {
self.thread_local.iter_mut().for_each(|context| {
let thread_local = context.borrow();
self.global.or(&thread_local);
});
}
}
fn update_solver_body_aabbs<C: AnyCollider>(
body_query: Query<
(
&Position,
&ComputedCenterOfMass,
&LinearVelocity,
&AngularVelocity,
&RigidBodyColliders,
Has<SweptCcd>,
),
With<SolverBody>,
>,
mut colliders: ParamSet<(
Query<
(
Ref<C>,
&mut ColliderAabb,
&mut EnlargedAabb,
&ColliderTreeProxyKey,
&Position,
&Rotation,
Option<&CollisionMargin>,
Option<&SpeculativeMargin>,
),
Without<ColliderDisabled>,
>,
Query<&EnlargedAabb, Without<ColliderDisabled>>,
)>,
narrow_phase_config: Res<NarrowPhaseConfig>,
length_unit: Res<PhysicsLengthUnit>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>,
mut enlarged_proxies: ResMut<EnlargedProxies>,
time: Res<Time>,
collider_context: StaticSystemParam<C::Context>,
mut diagnostics: ResMut<ColliderTreeDiagnostics>,
mut last_tick: ResMut<LastDynamicKinematicAabbUpdate>,
system_tick: SystemChangeTick,
) {
let start = crate::utils::Instant::now();
let this_run = system_tick.this_run();
let cap_dynamic = trees.dynamic_tree.proxies.capacity();
let cap_kinematic = trees.kinematic_tree.proxies.capacity();
let e = &mut enlarged_proxies;
e.dynamic_proxies.clear_and_set_capacity(cap_dynamic);
e.kinematic_proxies.clear_and_set_capacity(cap_kinematic);
let delta_secs = time.delta_seconds_adjusted();
let default_speculative_margin = length_unit.0 * narrow_phase_config.default_speculative_margin;
let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
let margin = length_unit.0 * AABB_MARGIN;
let collider_query = colliders.p0();
body_query.par_iter().for_each(
|(rb_pos, center_of_mass, lin_vel, ang_vel, body_colliders, has_swept_ccd)| {
for collider_entity in body_colliders.iter() {
let Ok((
collider,
mut aabb,
mut enlarged_aabb,
proxy_key,
pos,
rot,
collision_margin,
speculative_margin,
)) = (unsafe { collider_query.get_unchecked(collider_entity) })
else {
continue;
};
let collision_margin = collision_margin.map_or(0.0, |margin| margin.0);
let speculative_margin = if has_swept_ccd {
Scalar::MAX
} else {
speculative_margin.map_or(default_speculative_margin, |margin| margin.0)
};
let context = AabbContext::new(collider_entity, &*collider_context);
let growth = Vector::splat(contact_tolerance + collision_margin);
if speculative_margin <= 0.0 {
*aabb = collider
.aabb_with_context(pos.0, *rot, context)
.grow(growth);
} else {
let offset = pos.0 - rb_pos.0 - center_of_mass.0;
#[cfg(feature = "2d")]
let vel = lin_vel.0 + Vector::new(-ang_vel.0 * offset.y, ang_vel.0 * offset.x);
#[cfg(feature = "3d")]
let vel = lin_vel.0 + ang_vel.cross(offset);
let movement = (vel * delta_secs)
.clamp_length_max(speculative_margin.max(contact_tolerance));
#[cfg(feature = "2d")]
let (end_pos, end_rot) = (
pos.0 + movement,
*rot * Rotation::radians(ang_vel.0 * delta_secs),
);
#[cfg(feature = "3d")]
let (end_pos, end_rot) = (
pos.0 + movement,
Rotation(Quaternion::from_scaled_axis(ang_vel.0 * delta_secs) * rot.0)
.fast_renormalize(),
);
*aabb = collider
.swept_aabb_with_context(pos.0, *rot, end_pos, end_rot, context)
.grow(growth);
}
let moved = enlarged_aabb.update(&aabb, margin);
if moved {
let tree_type = proxy_key.tree_type();
let mut thread_local_bit_vec = enlarged_proxies
.bit_vec_for_type(tree_type)
.thread_local
.get_or(|| {
let capacity = match tree_type {
ColliderTreeType::Dynamic => cap_dynamic,
ColliderTreeType::Kinematic => cap_kinematic,
_ => unreachable!("Static or standalone proxy {proxy_key:?} moved in dynamic AABB update"),
};
let mut bit_vec = BitVec::new(capacity);
bit_vec.set_bit_count_and_clear(capacity);
RefCell::new(bit_vec)
})
.borrow_mut();
thread_local_bit_vec.set(proxy_key.id().index());
}
}
},
);
let aabb_query = colliders.p1();
for &tree_type in &[ColliderTreeType::Dynamic, ColliderTreeType::Kinematic] {
let tree = trees.tree_for_type_mut(tree_type);
let bit_vec = enlarged_proxies.bit_vec_for_type_mut(tree_type);
tree.bvh.init_primitives_to_nodes_if_uninit();
bit_vec.combine_thread_local();
update_tree(
tree_type,
tree,
&bit_vec.global,
&aabb_query,
&mut moved_proxies,
|tree, proxy_id, enlarged_aabb| {
tree.set_proxy_aabb(proxy_id, enlarged_aabb);
},
);
tree.refit_all();
}
last_tick.0 = this_run;
diagnostics.update += start.elapsed();
}
pub fn update_moved_collider_aabbs<C: AnyCollider>(
mut colliders: ParamSet<(
Query<
(
Entity,
Ref<Position>,
Ref<Rotation>,
&mut ColliderAabb,
&mut EnlargedAabb,
Ref<C>,
Option<&CollisionMargin>,
&ColliderTreeProxyKey,
),
Without<ColliderDisabled>,
>,
Query<&EnlargedAabb, Without<ColliderDisabled>>,
)>,
narrow_phase_config: Res<NarrowPhaseConfig>,
length_unit: Res<PhysicsLengthUnit>,
mut trees: ResMut<ColliderTrees>,
mut moved_proxies: ResMut<MovedProxies>,
mut enlarged_proxies: ResMut<EnlargedProxies>,
collider_context: StaticSystemParam<C::Context>,
mut diagnostics: ResMut<ColliderTreeDiagnostics>,
last_tick: Res<LastPhysicsTick>,
system_tick: SystemChangeTick,
) {
let start = crate::utils::Instant::now();
let this_run = system_tick.this_run();
let cap_dynamic = trees.dynamic_tree.proxies.capacity();
let cap_kinematic = trees.kinematic_tree.proxies.capacity();
let cap_static = trees.static_tree.proxies.capacity();
let cap_standalone = trees.standalone_tree.proxies.capacity();
let e = &mut enlarged_proxies;
e.dynamic_proxies.clear_and_set_capacity(cap_dynamic);
e.kinematic_proxies.clear_and_set_capacity(cap_kinematic);
e.static_proxies.clear_and_set_capacity(cap_static);
e.standalone_proxies.clear_and_set_capacity(cap_standalone);
let contact_tolerance = length_unit.0 * narrow_phase_config.contact_tolerance;
let margin = length_unit.0 * AABB_MARGIN;
let mut collider_query = colliders.p0();
collider_query.par_iter_mut().for_each(
|(entity, pos, rot, mut aabb, mut enlarged_aabb, collider, collision_margin, proxy_key)| {
if !pos.last_changed().is_newer_than(last_tick.0, this_run)
&& !rot.last_changed().is_newer_than(last_tick.0, this_run)
&& !collider.last_changed().is_newer_than(last_tick.0, this_run)
{
return;
}
let collision_margin = collision_margin.map_or(0.0, |margin| margin.0);
let context = AabbContext::new(entity, &*collider_context);
let growth = Vector::splat(contact_tolerance + collision_margin);
*aabb = collider
.aabb_with_context(pos.0, *rot, context)
.grow(growth);
let moved = enlarged_aabb.update(&aabb, margin);
if moved {
let tree_type = proxy_key.tree_type();
let mut thread_local_bit_vec = enlarged_proxies
.bit_vec_for_type(tree_type)
.thread_local
.get_or(|| {
let capacity = match tree_type {
ColliderTreeType::Dynamic => cap_dynamic,
ColliderTreeType::Kinematic => cap_kinematic,
ColliderTreeType::Static => cap_static,
ColliderTreeType::Standalone => cap_standalone,
};
let mut bit_vec = BitVec::new(capacity);
bit_vec.set_bit_count_and_clear(capacity);
RefCell::new(bit_vec)
})
.borrow_mut();
thread_local_bit_vec.set(proxy_key.id().index());
}
},
);
let aabb_query = colliders.p1();
for tree_type in ColliderTreeType::ALL {
let tree = trees.tree_for_type_mut(tree_type);
let bit_vec = enlarged_proxies.bit_vec_for_type_mut(tree_type);
tree.bvh.init_primitives_to_nodes_if_uninit();
bit_vec.combine_thread_local();
let moved_count = bit_vec.global.count_ones();
let moved_ratio = if tree.proxies.is_empty() {
0.0
} else {
moved_count as f32 / tree.proxies.len() as f32
};
if moved_ratio < 0.1 {
update_tree(
tree_type,
tree,
&bit_vec.global,
&aabb_query,
&mut moved_proxies,
|tree, proxy_id, enlarged_aabb| {
tree.resize_proxy_aabb(proxy_id, enlarged_aabb);
},
);
} else {
update_tree(
tree_type,
tree,
&bit_vec.global,
&aabb_query,
&mut moved_proxies,
|tree, proxy_id, enlarged_aabb| {
tree.set_proxy_aabb(proxy_id, enlarged_aabb);
},
);
tree.refit_all();
}
}
diagnostics.update += start.elapsed();
}
fn update_tree(
tree_type: ColliderTreeType,
tree: &mut ColliderTree,
bit_vec: &BitVec,
aabbs: &Query<&EnlargedAabb, Without<ColliderDisabled>>,
moved_proxies: &mut MovedProxies,
update_proxy_fn: impl Fn(&mut ColliderTree, ProxyId, Aabb),
) {
for (i, mut bits) in bit_vec.blocks().enumerate() {
while bits != 0 {
let trailing_zeros = bits.trailing_zeros();
let proxy_id = ProxyId::new(i as u32 * 64 + trailing_zeros);
let proxy = &mut tree.proxies[proxy_id.index()];
let entity = proxy.collider;
let enlarged_aabb = aabbs.get(entity).unwrap_or_else(|_| {
panic!(
"EnlargedAabb missing for moved collider entity {:?}",
entity
)
});
update_proxy_fn(tree, proxy_id, Aabb::from(enlarged_aabb.get()));
let proxy_key = ColliderTreeProxyKey::new(proxy_id, tree_type);
if moved_proxies.insert(proxy_key) {
tree.moved_proxies.push(proxy_id);
}
bits &= bits - 1;
}
}
}
fn clear_moved_proxies(mut moved_proxies: ResMut<MovedProxies>, mut trees: ResMut<ColliderTrees>) {
moved_proxies.clear();
trees.iter_trees_mut().for_each(|t| t.moved_proxies.clear());
}