use std::fmt::Debug;
use cgmath::BaseFloat;
use cgmath::prelude::*;
use collision::dbvt::{DiscreteVisitor, DynamicBoundingVolumeTree, TreeValue};
use collision::prelude::*;
use shrev::EventChannel;
use specs::{Component, Entities, Entity, FetchMut, Join, ReadStorage, System};
use NextFrame;
use collide::{CollisionShape, CollisionStrategy, ContactEvent, Primitive};
use collide::broad::BroadPhase;
use collide::narrow::NarrowPhase;
use ecs::collide::resources::GetEntity;
pub struct SpatialCollisionSystem<P, T, D, B, Y = ()>
where
P: Primitive,
B: Bound,
{
narrow: Option<Box<NarrowPhase<P, T, B, Y>>>,
broad: Option<Box<BroadPhase<D>>>,
}
impl<P, T, D, B, Y> SpatialCollisionSystem<P, T, D, B, Y>
where
P: Primitive + Send + Sync + 'static,
<P::Point as EuclideanSpace>::Diff: Debug,
<P::Point as EuclideanSpace>::Scalar: BaseFloat,
B: Clone
+ Debug
+ Send
+ Sync
+ 'static
+ Bound<Point = P::Point>
+ Union<B, Output = B>
+ Contains<B>
+ SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>,
T: Transform<P::Point> + Component,
D: HasBound<Bound = B>,
{
pub fn new() -> Self {
Self {
narrow: None,
broad: None,
}
}
pub fn with_narrow_phase<N: NarrowPhase<P, T, B, Y> + 'static>(mut self, narrow: N) -> Self {
self.narrow = Some(Box::new(narrow));
self
}
pub fn with_broad_phase<V: BroadPhase<D> + 'static>(mut self, broad: V) -> Self {
self.broad = Some(Box::new(broad));
self
}
}
fn discrete_visitor<P, D, B>(bound: &B) -> DiscreteVisitor<B, D>
where
P: Primitive,
B: Bound<Point = P::Point> + Debug + Discrete<B>,
P::Point: Debug,
<P::Point as EuclideanSpace>::Diff: Debug,
D: TreeValue<Bound = B>,
{
DiscreteVisitor::<B, D>::new(bound)
}
impl<'a, P, T, Y, B, D> System<'a> for SpatialCollisionSystem<P, T, (usize, D), B, Y>
where
P: Primitive + ComputeBound<B> + Send + Sync + 'static,
P::Point: EuclideanSpace,
<P::Point as EuclideanSpace>::Scalar: BaseFloat + Send + Sync + 'static,
B: Clone
+ Debug
+ Send
+ Sync
+ 'static
+ Bound<Point = P::Point>
+ Union<B, Output = B>
+ Discrete<B>
+ Contains<B>
+ SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>,
<P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
P::Point: Debug + Send + Sync + 'static,
T: Component + Clone + Debug + Transform<P::Point> + Send + Sync + 'static,
Y: Default + Send + Sync + 'static,
for<'b: 'a> &'b T::Storage: Join<Type = &'b T>,
D: Send + Sync + 'static + TreeValue<Bound = B> + HasBound<Bound = B> + GetEntity,
{
type SystemData = (
Entities<'a>,
ReadStorage<'a, T>,
ReadStorage<'a, NextFrame<T>>,
ReadStorage<'a, CollisionShape<P, T, B, Y>>,
FetchMut<'a, EventChannel<ContactEvent<Entity, P::Point>>>,
FetchMut<'a, DynamicBoundingVolumeTree<D>>,
);
fn run(&mut self, system_data: Self::SystemData) {
let (entities, poses, next_poses, shapes, mut event_channel, mut tree) = system_data;
let potentials = if let Some(ref mut broad) = self.broad {
let potentials = broad.find_potentials(tree.values_mut());
tree.reindex_values();
potentials
.iter()
.map(|&(ref l, ref r)| {
(
tree.values()[*l].1.entity().clone(),
tree.values()[*r].1.entity().clone(),
)
})
.collect()
} else {
let mut potentials = Vec::default();
for (entity, _, shape) in (&*entities, (&poses).open().1, &shapes).join() {
for (v, _) in tree.query(&mut discrete_visitor::<P, D, B>(shape.bound())) {
let e = v.entity();
if entity != e {
let n = if entity < e {
(entity, e.clone())
} else {
(e.clone(), entity)
};
if let Err(pos) = potentials.binary_search(&n) {
potentials.insert(pos, n);
}
}
}
}
for (entity, _, shape) in (&*entities, (&next_poses).open().1, &shapes).join() {
for (v, _) in tree.query(&mut discrete_visitor::<P, D, B>(shape.bound())) {
let e = v.entity();
if entity != e {
let n = if entity < e {
(entity, e.clone())
} else {
(e.clone(), entity)
};
if let Err(pos) = potentials.binary_search(&n) {
potentials.insert(pos, n);
}
}
}
}
potentials
};
match self.narrow {
Some(ref narrow) => for (left_entity, right_entity) in potentials {
let left_shape = shapes.get(left_entity).unwrap();
let right_shape = shapes.get(right_entity).unwrap();
let left_pose = poses.get(left_entity).unwrap();
let right_pose = poses.get(right_entity).unwrap();
let left_next_pose = next_poses.get(left_entity).as_ref().map(|p| &p.value);
let right_next_pose = next_poses.get(right_entity).as_ref().map(|p| &p.value);
match narrow.collide_continuous(
left_shape,
left_pose,
left_next_pose,
right_shape,
right_pose,
right_next_pose,
) {
Some(contact) => {
event_channel.single_write(ContactEvent::new(
(left_entity.clone(), right_entity.clone()),
contact,
));
}
None => (),
};
},
None => {
for (left_entity, right_entity) in potentials {
event_channel.single_write(ContactEvent::new_simple(
CollisionStrategy::CollisionOnly,
(left_entity, right_entity),
));
}
}
}
}
}