pub use collision::prelude::Primitive;
pub use collision::{CollisionStrategy, ComputeBound, Contact};
pub mod broad;
pub mod narrow;
use std::collections::HashSet;
use std::fmt::Debug;
use std::hash::Hash;
use cgmath::prelude::*;
use collision::dbvt::{DynamicBoundingVolumeTree, TreeValue, TreeValueWrapped};
use collision::prelude::*;
use self::broad::{broad_collide, BroadPhase};
use self::narrow::{narrow_collide, NarrowPhase};
pub trait Collider {
fn should_generate_contacts(&self, other: &Self) -> bool;
}
impl<'a> Collider for () {
fn should_generate_contacts(&self, _: &Self) -> bool {
true
}
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CollisionMode {
Discrete,
Continuous,
}
#[derive(Debug, Clone)]
pub struct ContactEvent<ID, P>
where
P: EuclideanSpace,
P::Diff: Debug,
{
pub bodies: (ID, ID),
pub contact: Contact<P>,
}
impl<ID, P> ContactEvent<ID, P>
where
ID: Clone + Debug,
P: EuclideanSpace,
P::Diff: VectorSpace + Zero + Debug,
{
pub fn new(bodies: (ID, ID), contact: Contact<P>) -> Self {
Self { bodies, contact }
}
pub fn new_simple(strategy: CollisionStrategy, bodies: (ID, ID)) -> Self {
Self::new(bodies, Contact::new(strategy))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CollisionShape<P, T, B, Y = ()>
where
P: Primitive,
{
pub enabled: bool,
base_bound: B,
transformed_bound: B,
primitives: Vec<(P, T)>,
strategy: CollisionStrategy,
mode: CollisionMode,
ty: Y,
}
impl<P, T, B, Y> CollisionShape<P, T, B, Y>
where
P: Primitive + ComputeBound<B>,
B: Bound<Point = P::Point> + Union<B, Output = B> + Clone,
T: Transform<P::Point>,
Y: Default,
{
pub fn new_complex(
strategy: CollisionStrategy,
mode: CollisionMode,
primitives: Vec<(P, T)>,
ty: Y,
) -> Self {
let bound: B = get_bound(&primitives);
Self {
base_bound: bound.clone(),
primitives,
enabled: true,
transformed_bound: bound,
strategy,
mode,
ty,
}
}
pub fn new_simple(strategy: CollisionStrategy, mode: CollisionMode, primitive: P) -> Self {
Self::new_complex(
strategy,
mode,
vec![(primitive, T::one())],
Default::default(),
)
}
pub fn new_simple_with_type(
strategy: CollisionStrategy,
mode: CollisionMode,
primitive: P,
ty: Y,
) -> Self {
Self::new_complex(strategy, mode, vec![(primitive, T::one())], ty)
}
pub fn new_simple_offset(
strategy: CollisionStrategy,
mode: CollisionMode,
primitive: P,
transform: T,
) -> Self {
Self::new_complex(
strategy,
mode,
vec![(primitive, transform)],
Default::default(),
)
}
pub fn update(&mut self, start: &T, end: Option<&T>) {
self.transformed_bound = match end {
None => self.base_bound.transform_volume(start),
Some(end_t) => {
let base = self.base_bound.transform_volume(end_t);
if self.mode == CollisionMode::Continuous {
base.union(&self.base_bound.transform_volume(start))
} else {
base
}
}
};
}
pub fn bound(&self) -> &B {
&self.transformed_bound
}
pub fn primitives(&self) -> &Vec<(P, T)> {
&self.primitives
}
}
impl<P, T, B, Y> HasBound for CollisionShape<P, T, B, Y>
where
P: Primitive + ComputeBound<B>,
B: Bound<Point = P::Point> + Union<B, Output = B> + Clone,
T: Transform<P::Point>,
Y: Default,
{
type Bound = B;
fn bound(&self) -> &Self::Bound {
&self.transformed_bound
}
}
fn get_bound<P, T, B>(primitives: &[(P, T)]) -> B
where
P: Primitive + ComputeBound<B>,
B: Bound<Point = P::Point> + Union<B, Output = B>,
T: Transform<P::Point>,
{
primitives
.iter()
.map(|&(ref p, ref t)| p.compute_bound().transform_volume(t))
.fold(B::empty(), |bound, b| bound.union(&b))
}
pub trait CollisionData<I, P, T, B, Y, D>
where
P: Primitive,
{
fn get_broad_data(&self) -> Vec<D>;
fn get_shape(&self, id: I) -> Option<&CollisionShape<P, T, B, Y>>;
fn get_pose(&self, id: I) -> Option<&T>;
fn get_dirty_poses(&self) -> Vec<I> {
Vec::default()
}
fn get_next_pose(&self, id: I) -> Option<&T>;
}
pub trait GetId<I> {
fn id(&self) -> I;
}
impl<I, B> GetId<I> for TreeValueWrapped<I, B>
where
B: Bound,
I: Copy + Debug + Hash + Eq,
<B::Point as EuclideanSpace>::Diff: Debug,
{
fn id(&self) -> I {
self.value
}
}
pub fn basic_collide<C, I, P, T, B, Y, D>(
data: &C,
broad: &mut Box<BroadPhase<D>>,
narrow: &Option<Box<NarrowPhase<P, T, B, Y>>>,
) -> Vec<ContactEvent<I, P::Point>>
where
C: CollisionData<I, P, T, B, Y, D>,
P: Primitive,
<P::Point as EuclideanSpace>::Diff: Debug,
I: Copy + Debug,
D: HasBound<Bound = B> + GetId<I>,
B: Bound<Point = P::Point>,
{
let potentials = broad_collide(data, broad);
if potentials.is_empty() {
return Vec::default();
}
match *narrow {
Some(ref narrow) => narrow_collide(data, narrow, &potentials),
None => potentials
.iter()
.map(|&(left, right)| {
ContactEvent::new_simple(CollisionStrategy::CollisionOnly, (left, right))
}).collect::<Vec<_>>(),
}
}
pub fn tree_collide<C, I, P, T, B, Y, D>(
data: &C,
tree: &mut DynamicBoundingVolumeTree<D>,
broad: &mut Option<Box<BroadPhase<(usize, D)>>>,
narrow: &Option<Box<NarrowPhase<P, T, B, Y>>>,
) -> Vec<ContactEvent<I, P::Point>>
where
C: CollisionData<I, P, T, B, Y, D>,
P: Primitive,
<P::Point as EuclideanSpace>::Diff: Debug,
I: Copy + Debug + Hash + Eq,
D: HasBound<Bound = B> + GetId<I> + TreeValue<Bound = B>,
B: Bound<Point = P::Point>
+ Clone
+ SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>
+ Contains<B>
+ Union<B, Output = B>
+ Discrete<B>,
{
use collision::algorithm::broad_phase::DbvtBroadPhase;
let potentials = match *broad {
Some(ref mut broad) => {
let p = broad.find_potentials(tree.values_mut());
tree.reindex_values();
p
}
None => {
let dirty_entities = data.get_dirty_poses().into_iter().collect::<HashSet<I>>();
let dirty = tree
.values()
.iter()
.map(|&(_, ref v)| dirty_entities.contains(&v.id()))
.collect::<Vec<_>>();
DbvtBroadPhase.find_collider_pairs(tree, &dirty[..])
}
};
let potentials = potentials
.iter()
.map(|&(ref l, ref r)| (tree.values()[*l].1.id(), tree.values()[*r].1.id()))
.collect::<Vec<_>>();
match *narrow {
Some(ref narrow) => narrow_collide(data, narrow, &potentials),
None => potentials
.iter()
.map(|&(left, right)| {
ContactEvent::new_simple(CollisionStrategy::CollisionOnly, (left, right))
}).collect::<Vec<_>>(),
}
}