1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::cmp::Ordering;
use amethyst_core::GlobalTransform;
use amethyst_core::cgmath::{EuclideanSpace, InnerSpace, MetricSpace, Point3, Transform, Vector3};
use amethyst_core::specs::prelude::{Entities, Entity, Join, Read, ReadStorage, System, Write};
use hibitset::BitSet;
use cam::{ActiveCamera, Camera};
use transparent::Transparent;
#[derive(Default)]
pub struct Visibility {
pub visible_unordered: BitSet,
pub visible_ordered: Vec<Entity>,
}
pub struct VisibilitySortingSystem {
centroids: Vec<Internals>,
transparent: Vec<Internals>,
}
#[derive(Clone)]
struct Internals {
entity: Entity,
transparent: bool,
centroid: Point3<f32>,
camera_distance: f32,
from_camera: Vector3<f32>,
}
impl VisibilitySortingSystem {
pub fn new() -> Self {
VisibilitySortingSystem {
centroids: Vec::default(),
transparent: Vec::default(),
}
}
}
impl<'a> System<'a> for VisibilitySortingSystem {
type SystemData = (
Entities<'a>,
Write<'a, Visibility>,
Option<Read<'a, ActiveCamera>>,
ReadStorage<'a, Camera>,
ReadStorage<'a, Transparent>,
ReadStorage<'a, GlobalTransform>,
);
fn run(
&mut self,
(entities, mut visibility, active, camera, transparent, global): Self::SystemData,
) {
let origin = Point3::origin();
let camera: Option<&GlobalTransform> = active
.and_then(|a| global.get(a.entity))
.or_else(|| (&camera, &global).join().map(|cg| cg.1).next());
let camera_backward = camera
.map(|c| c.0.z.truncate())
.unwrap_or(Vector3::unit_z());
let camera_centroid = camera
.map(|g| g.0.transform_point(origin))
.unwrap_or(origin.clone());
self.centroids.clear();
self.centroids.extend(
(&*entities, &global)
.join()
.map(|(entity, global)| (entity, global.0.transform_point(origin)))
.map(|(entity, centroid)| Internals {
entity,
transparent: transparent.contains(entity),
centroid,
camera_distance: centroid.distance2(camera_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| {
b.camera_distance
.partial_cmp(&a.camera_distance)
.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));
}
}