use std::cmp::Ordering;
use hibitset::BitSet;
use amethyst_core::{
nalgebra::{Point3, Vector3},
specs::prelude::{Entities, Entity, Join, Read, ReadStorage, System, Write},
GlobalTransform,
};
use crate::{
cam::{ActiveCamera, Camera},
hidden::{Hidden, HiddenPropagate},
transparent::Transparent,
};
#[derive(Default)]
pub struct SpriteVisibility {
pub visible_unordered: BitSet,
pub visible_ordered: Vec<Entity>,
}
#[derive(Default)]
pub struct SpriteVisibilitySortingSystem {
centroids: Vec<Internals>,
transparent: Vec<Internals>,
}
#[derive(Clone)]
struct Internals {
entity: Entity,
transparent: bool,
centroid: Point3<f32>,
from_camera: Vector3<f32>,
}
impl SpriteVisibilitySortingSystem {
pub fn new() -> Self {
Default::default()
}
}
impl<'a> System<'a> for SpriteVisibilitySortingSystem {
type SystemData = (
Entities<'a>,
Write<'a, SpriteVisibility>,
ReadStorage<'a, Hidden>,
ReadStorage<'a, HiddenPropagate>,
Read<'a, ActiveCamera>,
ReadStorage<'a, Camera>,
ReadStorage<'a, Transparent>,
ReadStorage<'a, GlobalTransform>,
);
fn run(
&mut self,
(entities, mut visibility, hidden, hidden_prop, active, camera, transparent, global): Self::SystemData,
) {
let origin = Point3::origin();
let camera: Option<&GlobalTransform> = active
.entity
.and_then(|entity| global.get(entity))
.or_else(|| (&camera, &global).join().map(|cg| cg.1).next());
let camera_backward = camera
.map(|c| c.0.column(2).xyz().into())
.unwrap_or_else(Vector3::z);
let camera_centroid = camera
.map(|g| g.0.transform_point(&origin))
.unwrap_or_else(|| origin);
self.centroids.clear();
self.centroids.extend(
(&*entities, &global, !&hidden, !&hidden_prop)
.join()
.map(|(entity, global, _, _)| (entity, global.0.transform_point(&origin)))
.map(|(entity, centroid)| Internals {
entity,
transparent: transparent.contains(entity),
centroid,
from_camera: centroid - camera_centroid,
})
.filter(|c| c.from_camera.dot(&camera_backward) < 0.),
);
self.transparent.clear();
self.transparent
.extend(self.centroids.iter().filter(|c| c.transparent).cloned());
self.transparent.sort_by(|a, b| {
a.centroid
.z
.partial_cmp(&b.centroid.z)
.unwrap_or(Ordering::Equal)
});
visibility.visible_unordered.clear();
for c in &self.centroids {
if !c.transparent {
visibility.visible_unordered.add(c.entity.id());
}
}
visibility.visible_ordered.clear();
visibility
.visible_ordered
.extend(self.transparent.iter().map(|c| c.entity));
}
}