use std::marker::PhantomData;
use std::ops::{Add, Mul, Neg, Sub};
use hibitset::BitSet;
use mat32;
use mat4;
use num_traits::Float;
use specs::prelude::{
ComponentEvent, Entities, Join, ReadExpect, ReadStorage, ReaderId, System, World, WriteStorage,
};
use specs_scene_graph::{Event, SceneGraph};
use type_name;
use super::{GlobalTransform2D, GlobalTransform3D, Parent, Transform2D, Transform3D};
pub struct TransformSystem<T> {
needs_global_2d: BitSet,
needs_global_3d: BitSet,
local_modified_2d: BitSet,
global_modified_2d: BitSet,
local_reader_id_2d: Option<ReaderId<ComponentEvent>>,
global_reader_id_2d: Option<ReaderId<ComponentEvent>>,
local_modified_3d: BitSet,
global_modified_3d: BitSet,
local_reader_id_3d: Option<ReaderId<ComponentEvent>>,
global_reader_id_3d: Option<ReaderId<ComponentEvent>>,
parent_events_id: Option<ReaderId<Event>>,
_marker: PhantomData<T>,
}
impl<T> Default for TransformSystem<T> {
#[inline]
fn default() -> Self {
TransformSystem {
needs_global_2d: BitSet::default(),
needs_global_3d: BitSet::default(),
local_modified_2d: BitSet::default(),
global_modified_2d: BitSet::default(),
local_reader_id_2d: None,
global_reader_id_2d: None,
local_modified_3d: BitSet::default(),
global_modified_3d: BitSet::default(),
local_reader_id_3d: None,
global_reader_id_3d: None,
parent_events_id: None,
_marker: PhantomData,
}
}
}
impl<T> TransformSystem<T> {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn name() -> &'static str {
type_name::get::<Self>()
}
}
impl<'system, T> System<'system> for TransformSystem<T>
where
T: 'static + Sync + Send + Float,
for<'a, 'b> &'a T:
Mul<&'b T, Output = T> + Neg<Output = T> + Add<&'b T, Output = T> + Sub<&'b T, Output = T>,
{
type SystemData = (
Entities<'system>,
ReadExpect<'system, SceneGraph<Parent>>,
ReadStorage<'system, Parent>,
ReadStorage<'system, Transform2D<T>>,
ReadStorage<'system, Transform3D<T>>,
WriteStorage<'system, GlobalTransform2D<T>>,
WriteStorage<'system, GlobalTransform3D<T>>,
);
#[inline]
fn run(
&mut self,
(entities, hierarchy, parents, locals_2d, locals_3d, mut globals_2d, mut globals_3d): Self::SystemData,
) {
if let Some(local_reader_id_2d) = self.local_reader_id_2d.as_mut() {
self.local_modified_2d.clear();
for event in locals_2d.channel().read(local_reader_id_2d) {
if let ComponentEvent::Modified(id) = event {
self.local_modified_2d.add(*id);
}
}
}
if let Some(global_reader_id_2d) = self.global_reader_id_2d.as_mut() {
self.global_modified_2d.clear();
for event in globals_2d.channel().read(global_reader_id_2d) {
if let ComponentEvent::Modified(id) = event {
self.global_modified_2d.add(*id);
}
}
}
if let Some(local_reader_id_3d) = self.local_reader_id_3d.as_mut() {
self.local_modified_3d.clear();
for event in locals_3d.channel().read(local_reader_id_3d) {
if let ComponentEvent::Modified(id) = event {
self.local_modified_3d.add(*id);
}
}
}
if let Some(global_reader_id_3d) = self.global_reader_id_3d.as_mut() {
self.global_modified_3d.clear();
for event in globals_3d.channel().read(global_reader_id_3d) {
if let ComponentEvent::Modified(id) = event {
self.global_modified_3d.add(*id);
}
}
}
for event in hierarchy
.changed()
.read(self.parent_events_id.as_mut().unwrap())
{
match *event {
Event::Removed(entity) => {
let _ = entities.delete(entity);
}
Event::Modified(entity) => {
if locals_2d.contains(entity) {
self.local_modified_2d.add(entity.id());
}
if locals_3d.contains(entity) {
self.local_modified_3d.add(entity.id());
}
}
}
}
self.needs_global_2d.clear();
for (entity, _, _) in (&*entities, &locals_2d, !&globals_2d).join() {
self.needs_global_2d.add(entity.id());
}
for (entity, local_2d, _) in (&*entities, &locals_2d, &self.needs_global_2d).join() {
self.global_modified_2d.add(entity.id());
let _ = globals_2d.insert(entity, GlobalTransform2D::from(local_2d.matrix()));
}
self.needs_global_3d.clear();
for (entity, _, _) in (&*entities, &locals_3d, !&globals_3d).join() {
self.needs_global_3d.add(entity.id());
}
for (entity, local_3d, _) in (&*entities, &locals_3d, &self.needs_global_3d).join() {
self.global_modified_3d.add(entity.id());
let _ = globals_3d.insert(entity, GlobalTransform3D::from(local_3d.matrix()));
}
for (entity, _, local_2d, _) in
(&*entities, &self.local_modified_2d, &locals_2d, !&parents).join()
{
self.global_modified_2d.add(entity.id());
let global_2d = globals_2d
.entry(entity)
.unwrap()
.or_insert_with(GlobalTransform2D::default);
global_2d.0 = local_2d.matrix();
}
for (entity, _, local_3d, _) in
(&*entities, &self.local_modified_3d, &locals_3d, !&parents).join()
{
self.global_modified_3d.add(entity.id());
let global_3d = globals_3d
.entry(entity)
.unwrap()
.or_insert_with(GlobalTransform3D::default);
global_3d.0 = local_3d.matrix();
}
for entity in hierarchy.all() {
match (
parents.get(*entity),
locals_2d.get(*entity),
locals_3d.get(*entity),
) {
(Some(parent), _, Some(local_3d)) => {
let self_dirty = self.local_modified_3d.contains(entity.id());
let parent_dirty = self.global_modified_3d.contains(parent.entity.id())
|| self.global_modified_2d.contains(parent.entity.id());
if parent_dirty || self_dirty {
let mut global_3d_matrix = local_3d.matrix();
if locals_3d.contains(parent.entity) {
let parent_global_3d = globals_3d
.entry(parent.entity)
.unwrap()
.or_insert_with(GlobalTransform3D::default);
let local_3d_matrix = global_3d_matrix.clone();
mat4::mul(&mut global_3d_matrix, &parent_global_3d.0, &local_3d_matrix);
} else if locals_2d.contains(parent.entity) {
let parent_global_2d = globals_2d
.entry(parent.entity)
.unwrap()
.or_insert_with(GlobalTransform2D::default);
let local_3d_matrix = global_3d_matrix.clone();
let mut parent_global_3d_matrix = mat4::new_identity();
mat4::set_mat32(&mut parent_global_3d_matrix, &parent_global_2d.0);
mat4::mul(
&mut global_3d_matrix,
&parent_global_3d_matrix,
&local_3d_matrix,
);
}
let global_3d = globals_3d
.entry(*entity)
.unwrap()
.or_insert_with(GlobalTransform3D::default);
self.global_modified_3d.add(entity.id());
global_3d.0 = global_3d_matrix;
}
}
(Some(parent), Some(local_2d), _) => {
let self_dirty = self.local_modified_2d.contains(entity.id());
let parent_dirty = self.global_modified_3d.contains(parent.entity.id())
|| self.global_modified_2d.contains(parent.entity.id());
if parent_dirty || self_dirty {
let mut global_2d_matrix = local_2d.matrix();
if locals_2d.contains(parent.entity) {
let parent_global_2d = globals_2d
.entry(parent.entity)
.unwrap()
.or_insert_with(GlobalTransform2D::default);
let local_2d_matrix = global_2d_matrix.clone();
mat32::mul(
&mut global_2d_matrix,
&parent_global_2d.0,
&local_2d_matrix,
);
} else if locals_3d.contains(parent.entity) {
let parent_global_3d = globals_3d
.entry(parent.entity)
.unwrap()
.or_insert_with(GlobalTransform3D::default);
let local_2d_matrix = global_2d_matrix.clone();
let mut parent_global_3d_matrix = mat32::new_identity();
mat32::set_mat4(&mut parent_global_3d_matrix, &parent_global_3d.0);
mat32::mul(
&mut global_2d_matrix,
&parent_global_3d_matrix,
&local_2d_matrix,
);
}
let global_2d = globals_2d
.entry(*entity)
.unwrap()
.or_insert_with(GlobalTransform2D::default);
self.global_modified_2d.add(entity.id());
global_2d.0 = global_2d_matrix;
}
}
_ => (),
}
}
}
#[inline]
fn setup(&mut self, world: &mut World) {
use specs::prelude::SystemData;
Self::SystemData::setup(world);
let mut hierarchy = world.fetch_mut::<SceneGraph<Parent>>();
let mut locals_2d = WriteStorage::<Transform2D<T>>::fetch(world);
let mut globals_2d = WriteStorage::<GlobalTransform2D<T>>::fetch(world);
let mut locals_3d = WriteStorage::<Transform3D<T>>::fetch(world);
let mut globals_3d = WriteStorage::<GlobalTransform3D<T>>::fetch(world);
self.parent_events_id = Some(hierarchy.track());
self.local_reader_id_2d = Some(locals_2d.register_reader());
self.global_reader_id_2d = Some(globals_2d.register_reader());
self.local_reader_id_3d = Some(locals_3d.register_reader());
self.global_reader_id_3d = Some(globals_3d.register_reader());
}
}
#[test]
fn test_transform_system_name() {
assert_eq!(
TransformSystem::<f32>::name(),
"specs_transform::transform_system::TransformSystem<f32>"
);
struct Example;
assert_eq!(
TransformSystem::<Example>::name(),
"specs_transform::transform_system::TransformSystem<specs_transform::transform_system::test_transform_system_name::Example>"
);
}