use core::marker::PhantomData;
use crate::{
collider_tree::{
ColliderTree, ColliderTreeProxy, ColliderTreeProxyFlags, ColliderTreeProxyKey,
ColliderTreeType, ColliderTrees, MovedProxies, ProxyId,
},
collision::{CollisionDiagnostics, contact_types::ContactEdgeFlags},
data_structures::pair_key::PairKey,
dynamics::solver::joint_graph::JointGraph,
prelude::*,
};
use bevy::{
ecs::system::{StaticSystemParam, SystemParamItem},
prelude::*,
tasks::{ComputeTaskPool, ParallelSlice},
};
use obvhs::aabb::Aabb;
pub struct BvhBroadPhasePlugin<H: CollisionHooks = ()>(PhantomData<H>);
impl<H: CollisionHooks> Default for BvhBroadPhasePlugin<H> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<H: CollisionHooks + 'static> Plugin for BvhBroadPhasePlugin<H>
where
for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
{
fn build(&self, app: &mut App) {
app.add_systems(
PhysicsSchedule,
collect_collision_pairs::<H>.in_set(BroadPhaseSystems::CollectCollisions),
);
}
}
fn collect_collision_pairs<H: CollisionHooks>(
trees: ResMut<ColliderTrees>,
moved_proxies: Res<MovedProxies>,
hooks: StaticSystemParam<H>,
par_commands: ParallelCommands,
mut contact_graph: ResMut<ContactGraph>,
joint_graph: Res<JointGraph>,
mut diagnostics: ResMut<CollisionDiagnostics>,
) where
for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
{
let start = crate::utils::Instant::now();
let hooks = hooks.into_inner();
let mut broad_collision_pairs = Vec::<(ColliderTreeProxyKey, ColliderTreeProxyKey)>::new();
let pairs = moved_proxies.proxies().par_splat_map(
ComputeTaskPool::get(),
None,
|_chunk_index, proxies| {
let mut pairs = Vec::new();
par_commands.command_scope(|mut commands| {
for proxy_key1 in proxies {
let proxy_id1 = proxy_key1.id();
let proxy_type1 = proxy_key1.tree_type();
let tree = trees.tree_for_type(proxy_type1);
let proxy1 = tree.get_proxy(proxy_key1.id()).unwrap();
let Some(proxy_aabb1) = tree.get_proxy_aabb(proxy_id1) else {
continue;
};
query_tree(
&trees.dynamic_tree,
ColliderTreeType::Dynamic,
*proxy_key1,
proxy_id1,
proxy_type1,
proxy_aabb1,
proxy1,
&moved_proxies,
&hooks,
&mut commands,
&contact_graph,
&joint_graph,
&mut pairs,
);
query_tree(
&trees.kinematic_tree,
ColliderTreeType::Kinematic,
*proxy_key1,
proxy_id1,
proxy_type1,
proxy_aabb1,
proxy1,
&moved_proxies,
&hooks,
&mut commands,
&contact_graph,
&joint_graph,
&mut pairs,
);
if proxy_type1 != ColliderTreeType::Static || proxy1.is_sensor() {
query_tree(
&trees.static_tree,
ColliderTreeType::Static,
*proxy_key1,
proxy_id1,
proxy_type1,
proxy_aabb1,
proxy1,
&moved_proxies,
&hooks,
&mut commands,
&contact_graph,
&joint_graph,
&mut pairs,
);
}
query_tree(
&trees.standalone_tree,
ColliderTreeType::Standalone,
*proxy_key1,
proxy_id1,
proxy_type1,
proxy_aabb1,
proxy1,
&moved_proxies,
&hooks,
&mut commands,
&contact_graph,
&joint_graph,
&mut pairs,
);
}
});
pairs
},
);
for mut chunk in pairs {
broad_collision_pairs.append(&mut chunk);
}
for (proxy_key1, proxy_key2) in broad_collision_pairs {
let proxy1 = trees.get_proxy(proxy_key1).unwrap();
let proxy2 = trees.get_proxy(proxy_key2).unwrap();
let mut contact_edge = ContactEdge::new(proxy1.collider, proxy2.collider);
contact_edge.body1 = proxy1.body;
contact_edge.body2 = proxy2.body;
let flags_union = proxy1.flags.union(proxy2.flags);
contact_edge.flags.set(
ContactEdgeFlags::CONTACT_EVENTS,
flags_union.contains(ColliderTreeProxyFlags::CONTACT_EVENTS),
);
contact_graph.add_edge_with(contact_edge, |contact_pair| {
contact_pair.body1 = proxy1.body;
contact_pair.body2 = proxy2.body;
contact_pair.flags.set(
ContactPairFlags::MODIFY_CONTACTS,
flags_union.contains(ColliderTreeProxyFlags::MODIFY_CONTACTS),
);
contact_pair.flags.set(
ContactPairFlags::GENERATE_CONSTRAINTS,
!flags_union.contains(ColliderTreeProxyFlags::BODY_DISABLED)
&& !flags_union.contains(ColliderTreeProxyFlags::SENSOR),
);
});
}
diagnostics.broad_phase += start.elapsed();
}
#[inline]
fn query_tree(
tree: &ColliderTree,
tree_type: ColliderTreeType,
proxy_key1: ColliderTreeProxyKey,
proxy_id1: ProxyId,
proxy_type1: ColliderTreeType,
proxy_aabb1: Aabb,
proxy1: &ColliderTreeProxy,
moved_proxies: &MovedProxies,
hooks: &impl CollisionHooks,
commands: &mut Commands,
contact_graph: &ContactGraph,
joint_graph: &JointGraph,
pairs: &mut Vec<(ColliderTreeProxyKey, ColliderTreeProxyKey)>,
) {
tree.bvh.aabb_traverse(proxy_aabb1, |bvh, node_index| {
let node = &bvh.nodes[node_index as usize];
let start = node.first_index as usize;
let end = start + node.prim_count as usize;
for node_primitive_index in start..end {
let proxy_id2 = ProxyId::new(tree.bvh.primitive_indices[node_primitive_index]);
let proxy_key2 = ColliderTreeProxyKey::new(proxy_id2, tree_type);
if proxy_key1 == proxy_key2 {
continue;
}
let proxy2 = tree.get_proxy(proxy_id2).unwrap();
let proxy1_greater = ((tree_type as u8) < (proxy_type1 as u8))
|| (tree_type == proxy_type1 && proxy_id2.id() < proxy_id1.id());
if proxy1_greater && moved_proxies.contains(proxy_key2) && !proxy1.is_sensor() {
continue;
}
if !proxy1.layers.interacts_with(proxy2.layers) {
continue;
}
if proxy1.body == proxy2.body {
continue;
}
let entity1 = proxy1.collider;
let entity2 = proxy2.collider;
let pair_key = PairKey::new(entity1.index_u32(), entity2.index_u32());
if contact_graph.contains_key(&pair_key) {
continue;
}
if let Some(body1) = proxy1.body
&& let Some(body2) = proxy2.body
&& joint_graph
.joints_between(body1, body2)
.any(|edge| edge.collision_disabled)
{
continue;
}
if proxy1
.flags
.union(proxy2.flags)
.contains(ColliderTreeProxyFlags::CUSTOM_FILTER)
{
let should_collide = hooks.filter_pairs(entity1, entity2, commands);
if !should_collide {
continue;
}
}
pairs.push((proxy_key1, proxy_key2));
}
true
});
}