use alloc::vec::Vec;
use alloc::btree_map::BTreeMap;
use alloc::btree_set::BTreeSet;
use core::marker::PhantomData;
use core::cmp::Ordering;
use mat4;
use mat32;
use hibitset::BitSet;
use number_traits::Float;
use specs_guided_join::GuidedJoin;
use specs::{FetchMut, Entities, Entity, Join, ReadStorage, System, WriteStorage};
use super::{TransformGuide, Child, Init, LocalTransform2D, Transform2D, LocalTransform3D, Transform3D};
pub struct TransformSystem<T: 'static + Sync + Send + Copy + Float> {
depth: BTreeMap<Entity, usize>,
children: BTreeMap<Entity, Entity>,
guide: BTreeSet<Entity>,
new: Vec<Entity>,
dead: BitSet,
dirty: BitSet,
_marker: PhantomData<T>,
}
impl<T> Default for TransformSystem<T>
where T: 'static + Sync + Send + Copy + Float,
{
#[inline(always)]
fn default() -> Self {
TransformSystem {
depth: BTreeMap::default(),
children: BTreeMap::default(),
guide: BTreeSet::default(),
new: Vec::default(),
dead: BitSet::default(),
dirty: BitSet::default(),
_marker: PhantomData,
}
}
}
impl<T: 'static + Sync + Send + Copy + Float> TransformSystem<T> {
#[inline(always)]
pub fn new() -> Self {
Self::default()
}
}
impl<'a, T: 'static + Sync + Send + Copy + Float> System<'a> for TransformSystem<T> {
type SystemData = (
Entities<'a>,
FetchMut<'a, TransformGuide>,
ReadStorage<'a, Child>,
WriteStorage<'a, Init>,
ReadStorage<'a, LocalTransform2D<T>>,
ReadStorage<'a, LocalTransform3D<T>>,
WriteStorage<'a, Transform2D<T>>,
WriteStorage<'a, Transform3D<T>>
);
fn run(&mut self, (
entities, mut guide, children, mut inits, locals_2d, locals_3d, mut globals_2d, mut globals_3d
): Self::SystemData) {
for (entity, _, child, _) in (&*entities, &locals_2d, &children, !&inits).join() {
self.children.insert(entity, child.parent());
self.new.push(entity);
self.guide.insert(entity);
}
for (entity, _, child, _) in (&*entities, &locals_3d, &children, !&inits).join() {
self.children.insert(entity, child.parent());
self.new.push(entity);
self.guide.insert(entity);
}
for (entity, _, _) in (&*entities, &locals_2d, !&inits).join() {
self.guide.insert(entity);
}
for (entity, _, _) in (&*entities, &locals_3d, !&inits).join() {
self.guide.insert(entity);
}
for (entity, _) in &self.children {
if let Some(child) = children.get(*entity) {
if !entities.is_alive(child.parent()) || self.dead.contains(child.parent().id()) {
let _ = entities.delete(*entity);
self.dead.add(entity.id());
self.guide.remove(entity);
}
}
}
let needs_sort = self.new.len() != 0;
for entity in self.new.drain(..) {
get_depth(
&mut self.depth, &self.children,
self.children.get(&entity).map(|p| *p),
entity, 0
);
inits.insert(entity, Init);
}
for (entity, local, global, _) in (&*entities, &locals_2d, &mut globals_2d, !&children).join() {
if local.is_dirty() {
self.dirty.add(entity.id());
global.0 = local.matrix();
local.flag(false);
}
}
for (entity, local, global, _) in (&*entities, &locals_3d, &mut globals_3d, !&children).join() {
if local.is_dirty() {
self.dirty.add(entity.id());
global.0 = local.matrix();
local.flag(false);
}
}
if needs_sort {
guide.set(self.guide.iter().map(|e| *e).collect());
guide.as_slice_mut().sort_by(|a, b| {
if let Some(a) = self.depth.get(a) {
if let Some(b) = self.depth.get(b) {
a.cmp(b)
} else {
Ordering::Equal
}
} else {
Ordering::Equal
}
});
}
for (entity, local, child) in (&*entities, &locals_2d, &children).guided_join(guide.as_slice()) {
let mut global_matrix = local.matrix();
if let Some(parent_global) = globals_2d.get(child.parent()) {
let local_matrix = global_matrix.clone();
mat32::mul(&mut global_matrix, &parent_global.0, &local_matrix);
} else if let Some(parent_global) = globals_3d.get(child.parent()) {
let local_matrix = global_matrix.clone();
let mut parent_global_2d = mat32::new_identity();
mat32::from_mat4(&mut parent_global_2d, &parent_global.0);
mat32::mul(&mut global_matrix, &parent_global_2d, &local_matrix);
}
if let Some(global_2d) = globals_2d.get_mut(entity) {
global_2d.0 = global_matrix;
}
local.flag(false);
child.flag(false);
}
for (entity, local, child) in (&*entities, &locals_3d, &children).guided_join(guide.as_slice()) {
let mut global_matrix = local.matrix();
if let Some(parent_global) = globals_3d.get(child.parent()) {
let local_matrix = global_matrix.clone();
mat4::mul(&mut global_matrix, &parent_global.0, &local_matrix);
} else if let Some(parent_global) = globals_2d.get(child.parent()) {
let local_matrix = global_matrix.clone();
let mut parent_global_3d = mat4::new_identity();
mat4::from_mat32(&mut parent_global_3d, &parent_global.0);
mat4::mul(&mut global_matrix, &parent_global_3d, &local_matrix);
}
if let Some(global_3d) = globals_3d.get_mut(entity) {
global_3d.0 = global_matrix;
}
local.flag(false);
child.flag(false);
}
self.dead.clear();
self.dirty.clear();
}
}
#[inline]
fn get_depth<'a>(
depth: &'a mut BTreeMap<Entity, usize>,
children: &'a BTreeMap<Entity, Entity>,
parent: Option<Entity>,
entity: Entity,
current: usize
) -> usize {
if let Some(depth) = depth.get(&entity) {
return *depth;
}
if let Some(parent) = parent {
depth.insert(parent, current);
get_depth(depth, children, children.get(&parent).map(|p| *p), entity, current + 1)
} else {
depth.insert(entity, current);
current
}
}