use bevy::{
ecs::{entity::Entity, resource::Resource},
reflect::prelude::*,
};
use obvhs::{
aabb::Aabb,
bvh2::{Bvh2, insertion_removal::SiblingInsertionCandidate, reinsertion::ReinsertionOptimizer},
faststack::HeapStack,
ploc::{PlocBuilder, PlocSearchDistance, SortPrecision, rebuild::compute_rebuild_path_flags},
};
use crate::{
collider_tree::ProxyId,
data_structures::stable_vec::StableVec,
prelude::{ActiveCollisionHooks, CollisionLayers},
};
#[derive(Clone, Default)]
pub struct ColliderTree {
pub bvh: Bvh2,
pub proxies: StableVec<ColliderTreeProxy>,
pub moved_proxies: Vec<ProxyId>,
pub workspace: ColliderTreeWorkspace,
}
#[derive(Clone, Debug)]
pub struct ColliderTreeProxy {
pub collider: Entity,
pub body: Option<Entity>,
pub layers: CollisionLayers,
pub flags: ColliderTreeProxyFlags,
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub struct ColliderTreeProxyFlags(u32);
bitflags::bitflags! {
impl ColliderTreeProxyFlags: u32 {
const SENSOR = 1 << 0;
const BODY_DISABLED = 1 << 1;
const CUSTOM_FILTER = 1 << 2;
const MODIFY_CONTACTS = 1 << 3;
const CONTACT_EVENTS = 1 << 4;
}
}
impl ColliderTreeProxyFlags {
#[inline]
pub fn new(
is_sensor: bool,
is_body_disabled: bool,
events_enabled: bool,
active_hooks: ActiveCollisionHooks,
) -> Self {
let mut flags = ColliderTreeProxyFlags::empty();
if is_sensor {
flags |= ColliderTreeProxyFlags::SENSOR;
}
if is_body_disabled {
flags |= ColliderTreeProxyFlags::BODY_DISABLED;
}
if active_hooks.contains(ActiveCollisionHooks::FILTER_PAIRS) {
flags |= ColliderTreeProxyFlags::CUSTOM_FILTER;
}
if active_hooks.contains(ActiveCollisionHooks::MODIFY_CONTACTS) {
flags |= ColliderTreeProxyFlags::MODIFY_CONTACTS;
}
if events_enabled {
flags |= ColliderTreeProxyFlags::CONTACT_EVENTS;
}
flags
}
}
impl ColliderTreeProxy {
#[inline]
pub fn is_sensor(&self) -> bool {
self.flags.contains(ColliderTreeProxyFlags::SENSOR)
}
#[inline]
pub fn has_custom_filter(&self) -> bool {
self.flags.contains(ColliderTreeProxyFlags::CUSTOM_FILTER)
}
#[inline]
pub fn has_contact_modification(&self) -> bool {
self.flags.contains(ColliderTreeProxyFlags::MODIFY_CONTACTS)
}
}
#[derive(Resource)]
pub struct ColliderTreeWorkspace {
pub ploc_builder: PlocBuilder,
pub reinsertion_optimizer: ReinsertionOptimizer,
pub insertion_stack: HeapStack<SiblingInsertionCandidate>,
pub temp_flags: Vec<bool>,
}
impl Clone for ColliderTreeWorkspace {
fn clone(&self) -> Self {
Self {
ploc_builder: self.ploc_builder.clone(),
reinsertion_optimizer: ReinsertionOptimizer::default(),
insertion_stack: self.insertion_stack.clone(),
temp_flags: Vec::new(),
}
}
}
impl Default for ColliderTreeWorkspace {
fn default() -> Self {
Self {
ploc_builder: PlocBuilder::default(),
reinsertion_optimizer: ReinsertionOptimizer::default(),
insertion_stack: HeapStack::new_with_capacity(2000),
temp_flags: Vec::new(),
}
}
}
impl ColliderTree {
#[inline]
pub fn add_proxy(&mut self, aabb: Aabb, proxy: ColliderTreeProxy) -> ProxyId {
let id = self.proxies.push(proxy) as u32;
self.bvh
.insert_primitive(aabb, id, &mut self.workspace.insertion_stack);
self.moved_proxies.push(ProxyId::new(id));
ProxyId::new(id)
}
#[inline]
pub fn remove_proxy(&mut self, proxy_id: ProxyId) -> Option<ColliderTreeProxy> {
if let Some(proxy) = self.proxies.try_remove(proxy_id.index()) {
self.bvh.remove_primitive(proxy_id.id());
for i in 0..self.moved_proxies.len() {
if self.moved_proxies[i] == proxy_id {
self.moved_proxies.swap_remove(i);
break;
}
}
Some(proxy)
} else {
None
}
}
#[inline]
pub fn get_proxy(&self, proxy_id: ProxyId) -> Option<&ColliderTreeProxy> {
self.proxies.get(proxy_id.index())
}
#[inline]
pub fn get_proxy_mut(&mut self, proxy_id: ProxyId) -> Option<&mut ColliderTreeProxy> {
self.proxies.get_mut(proxy_id.index())
}
#[inline]
pub unsafe fn get_proxy_unchecked(&self, proxy_id: ProxyId) -> &ColliderTreeProxy {
unsafe { self.proxies.get_unchecked(proxy_id.index()) }
}
#[inline]
pub unsafe fn get_proxy_unchecked_mut(&mut self, proxy_id: ProxyId) -> &mut ColliderTreeProxy {
unsafe { self.proxies.get_unchecked_mut(proxy_id.index()) }
}
#[inline]
pub fn get_proxy_aabb(&self, proxy_id: ProxyId) -> Option<Aabb> {
let node_id = self.bvh.primitives_to_nodes.get(proxy_id.index())?;
self.bvh.nodes.get(*node_id as usize).map(|node| node.aabb)
}
#[inline]
pub unsafe fn get_proxy_aabb_unchecked(&self, proxy_id: ProxyId) -> Aabb {
unsafe {
let node_id = *self.bvh.primitives_to_nodes.get_unchecked(proxy_id.index()) as usize;
self.bvh.nodes.get_unchecked(node_id).aabb
}
}
#[inline]
pub fn set_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
self.bvh.nodes[node_index].set_aabb(aabb);
}
#[inline]
pub fn resize_proxy_aabb(&mut self, proxy_id: ProxyId, aabb: Aabb) {
let node_index = self.bvh.primitives_to_nodes[proxy_id.index()] as usize;
self.bvh.resize_node(node_index, aabb);
}
#[inline]
pub fn reinsert_proxy(&mut self, proxy_id: ProxyId, aabb: Aabb) {
let node_id = self.bvh.primitives_to_nodes[proxy_id.index()];
self.bvh.resize_node(node_id as usize, aabb);
self.bvh.reinsert_node(node_id as usize);
}
#[inline]
pub fn refit_all(&mut self) {
self.bvh.refit_all();
}
#[inline]
pub fn rebuild_full(&mut self) {
self.workspace.ploc_builder.full_rebuild(
&mut self.bvh,
PlocSearchDistance::Minimum,
SortPrecision::U64,
0,
);
}
#[inline]
pub fn rebuild_partial(&mut self, leaves: &[u32]) {
self.bvh.init_parents_if_uninit();
compute_rebuild_path_flags(&self.bvh, leaves, &mut self.workspace.temp_flags);
self.workspace.ploc_builder.partial_rebuild(
&mut self.bvh,
|node_id| self.workspace.temp_flags[node_id],
PlocSearchDistance::Minimum,
SortPrecision::U64,
0,
);
}
#[inline]
pub fn optimize(&mut self, batch_size_ratio: f32) {
self.workspace
.reinsertion_optimizer
.run(&mut self.bvh, batch_size_ratio, None);
}
#[inline]
pub fn optimize_candidates(&mut self, candidates: &[u32], iterations: u32) {
self.workspace.reinsertion_optimizer.run_with_candidates(
&mut self.bvh,
candidates,
iterations,
);
}
}