use std::collections::HashMap;
use std::fmt::Debug;
use std::marker::PhantomData;
use cgmath::prelude::*;
use cgmath::BaseFloat;
use collision::dbvt::{DynamicBoundingVolumeTree, TreeValue};
use collision::prelude::*;
use specs::prelude::{
BitSet, Component, ComponentEvent, Entities, Entity, Join, ReadStorage, ReaderId, Resources,
System, Tracked, Write, WriteStorage,
};
use core::{CollisionShape, NextFrame, Primitive};
#[derive(Debug)]
pub struct SpatialSortingSystem<P, T, D, B, Y = ()> {
entities: HashMap<Entity, usize>,
dead: Vec<Entity>,
updated: BitSet,
removed: BitSet,
pose_reader: Option<ReaderId<ComponentEvent>>,
next_pose_reader: Option<ReaderId<ComponentEvent>>,
marker: PhantomData<(P, T, Y, B, D)>,
}
impl<P, T, D, B, Y> SpatialSortingSystem<P, T, D, B, Y> {
pub fn new() -> Self {
SpatialSortingSystem {
entities: HashMap::default(),
marker: PhantomData,
updated: BitSet::default(),
removed: BitSet::default(),
pose_reader: None,
next_pose_reader: None,
dead: Vec::default(),
}
}
}
impl<'a, P, T, Y, D, B> System<'a> for SpatialSortingSystem<P, T, D, B, Y>
where
P: Primitive + ComputeBound<B> + Send + Sync + 'static,
B: Clone
+ Debug
+ Send
+ Sync
+ Union<B, Output = B>
+ Bound<Point = P::Point>
+ Contains<B>
+ SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>
+ Send
+ Sync
+ 'static,
P::Point: Debug,
<P::Point as EuclideanSpace>::Scalar: BaseFloat + Send + Sync + 'static,
<P::Point as EuclideanSpace>::Diff: Debug + Send + Sync,
T: Component + Clone + Debug + Transform<P::Point> + Send + Sync,
T::Storage: Tracked,
Y: Default + Send + Sync + 'static,
D: Send + Sync + 'static + TreeValue<Bound = B> + From<(Entity, B)>,
{
type SystemData = (
Entities<'a>,
ReadStorage<'a, T>,
ReadStorage<'a, NextFrame<T>>,
WriteStorage<'a, CollisionShape<P, T, B, Y>>,
Write<'a, DynamicBoundingVolumeTree<D>>,
);
fn run(&mut self, (entities, poses, next_poses, mut shapes, mut tree): Self::SystemData) {
self.updated.clear();
self.removed.clear();
for event in poses.channel().read(self.pose_reader.as_mut().unwrap()) {
match event {
ComponentEvent::Inserted(index) => {
self.updated.add(*index);
}
ComponentEvent::Modified(index) => {
self.updated.add(*index);
}
ComponentEvent::Removed(index) => {
self.updated.remove(*index);
self.removed.add(*index);
}
}
}
self.dead.clear();
for (entity, node_index) in &self.entities {
if self.removed.contains(entity.id()) {
tree.remove(*node_index);
self.dead.push(*entity);
}
}
for entity in &self.dead {
self.entities.remove(entity);
}
for (entity, pose, shape, _) in (&*entities, &poses, &mut shapes, &self.updated).join() {
shape.update(pose, None);
match self.entities.get(&entity).cloned() {
Some(node_index) => {
tree.update_node(node_index, (entity, shape.bound().clone()).into());
}
None => {
let node_index = tree.insert((entity, shape.bound().clone()).into());
self.entities.insert(entity, node_index);
}
}
}
self.updated.clear();
for event in next_poses
.channel()
.read(self.next_pose_reader.as_mut().unwrap())
{
match event {
ComponentEvent::Inserted(index) => {
self.updated.add(*index);
}
ComponentEvent::Modified(index) => {
self.updated.add(*index);
}
ComponentEvent::Removed(index) => {
self.updated.remove(*index);
}
}
}
for (entity, pose, next_pose, shape, _) in
(&*entities, &poses, &next_poses, &mut shapes, &self.updated).join()
{
shape.update(pose, Some(&next_pose.value));
if let Some(node_index) = self.entities.get(&entity).cloned() {
tree.update_node(node_index, (entity, shape.bound().clone()).into());
}
}
tree.update();
tree.do_refit();
}
fn setup(&mut self, res: &mut Resources) {
use specs::prelude::SystemData;
Self::SystemData::setup(res);
let mut poses = WriteStorage::<T>::fetch(res);
self.pose_reader = Some(poses.register_reader());
let mut next_poses = WriteStorage::<NextFrame<T>>::fetch(res);
self.next_pose_reader = Some(next_poses.register_reader());
}
}