rhusics_ecs/collide/systems/
spatial_sort.rs

1use std::collections::HashMap;
2use std::fmt::Debug;
3use std::marker::PhantomData;
4
5use cgmath::prelude::*;
6use cgmath::BaseFloat;
7use collision::dbvt::{DynamicBoundingVolumeTree, TreeValue};
8use collision::prelude::*;
9use specs::prelude::{
10    BitSet, Component, ComponentEvent, Entities, Entity, Join, ReadStorage, ReaderId, Resources,
11    System, Tracked, Write, WriteStorage,
12};
13
14use core::{CollisionShape, NextFrame, Primitive};
15
16/// Spatial sorting [system](https://docs.rs/specs/0.9.5/specs/trait.System.html) for use with
17/// [`specs`](https://docs.rs/specs/0.9.5/specs/).
18///
19/// Will perform spatial sorting of the collision world. Uses a Dynamic Bounding Volume Tree for
20/// sorting. Will update entries in the tree where the pose is dirty.
21///
22/// Can handle any transform component type, as long as the type implements
23/// [`Transform`](https://docs.rs/cgmath/0.15.0/cgmath/trait.Transform.html), and as long as the
24/// storage is wrapped in
25/// [`FlaggedStorage`](https://docs.rs/specs/0.9.5/specs/struct.FlaggedStorage.html)
26///
27/// ## Type parameters:
28///
29/// - `P`: Primitive type, needs to implement `Primitive`.
30/// - `T`: Transform type, needs to implement `Transform` and have `FlaggedStorage`.
31/// - `D`: Type of values stored in the DBVT, needs to implement `TreeValue` and
32///        `From<(Entity, CollisionShape)>`
33/// - `B`: Bounding volume
34/// - `Y`: Shape type, see `Collider`
35///
36/// ### System Function:
37///
38/// `fn(Entities, T, NextFrame<T>, CollisionShape) -> (CollisionShape, DynamicBoundingVolumeTree<D>)`
39#[derive(Debug)]
40pub struct SpatialSortingSystem<P, T, D, B, Y = ()> {
41    entities: HashMap<Entity, usize>,
42    dead: Vec<Entity>,
43    updated: BitSet,
44    removed: BitSet,
45    pose_reader: Option<ReaderId<ComponentEvent>>,
46    next_pose_reader: Option<ReaderId<ComponentEvent>>,
47    marker: PhantomData<(P, T, Y, B, D)>,
48}
49
50impl<P, T, D, B, Y> SpatialSortingSystem<P, T, D, B, Y> {
51    /// Create a new sorting system.
52    pub fn new() -> Self {
53        SpatialSortingSystem {
54            entities: HashMap::default(),
55            marker: PhantomData,
56            updated: BitSet::default(),
57            removed: BitSet::default(),
58            pose_reader: None,
59            next_pose_reader: None,
60            dead: Vec::default(),
61        }
62    }
63}
64
65impl<'a, P, T, Y, D, B> System<'a> for SpatialSortingSystem<P, T, D, B, Y>
66where
67    P: Primitive + ComputeBound<B> + Send + Sync + 'static,
68    B: Clone
69        + Debug
70        + Send
71        + Sync
72        + Union<B, Output = B>
73        + Bound<Point = P::Point>
74        + Contains<B>
75        + SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>
76        + Send
77        + Sync
78        + 'static,
79    P::Point: Debug,
80    <P::Point as EuclideanSpace>::Scalar: BaseFloat + Send + Sync + 'static,
81    <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync,
82    T: Component + Clone + Debug + Transform<P::Point> + Send + Sync,
83    T::Storage: Tracked,
84    Y: Default + Send + Sync + 'static,
85    D: Send + Sync + 'static + TreeValue<Bound = B> + From<(Entity, B)>,
86{
87    type SystemData = (
88        Entities<'a>,
89        ReadStorage<'a, T>,
90        ReadStorage<'a, NextFrame<T>>,
91        WriteStorage<'a, CollisionShape<P, T, B, Y>>,
92        Write<'a, DynamicBoundingVolumeTree<D>>,
93    );
94
95    fn run(&mut self, (entities, poses, next_poses, mut shapes, mut tree): Self::SystemData) {
96        self.updated.clear();
97        self.removed.clear();
98
99        for event in poses.channel().read(self.pose_reader.as_mut().unwrap()) {
100            match event {
101                ComponentEvent::Inserted(index) => {
102                    self.updated.add(*index);
103                }
104                ComponentEvent::Modified(index) => {
105                    self.updated.add(*index);
106                }
107                ComponentEvent::Removed(index) => {
108                    self.updated.remove(*index);
109                    self.removed.add(*index);
110                }
111            }
112        }
113
114        // remove entities that are missing from the tree
115        self.dead.clear();
116        for (entity, node_index) in &self.entities {
117            if self.removed.contains(entity.id()) {
118                tree.remove(*node_index);
119                self.dead.push(*entity);
120            }
121        }
122        for entity in &self.dead {
123            self.entities.remove(entity);
124        }
125
126        // Check for updated poses
127        // Uses FlaggedStorage
128        for (entity, pose, shape, _) in (&*entities, &poses, &mut shapes, &self.updated).join() {
129            shape.update(pose, None);
130
131            // Update the wrapper in the tree for the shape
132            match self.entities.get(&entity).cloned() {
133                // Update existing
134                Some(node_index) => {
135                    tree.update_node(node_index, (entity, shape.bound().clone()).into());
136                }
137                // Insert new
138                None => {
139                    let node_index = tree.insert((entity, shape.bound().clone()).into());
140                    self.entities.insert(entity, node_index);
141                }
142            }
143        }
144
145        self.updated.clear();
146
147        for event in next_poses
148            .channel()
149            .read(self.next_pose_reader.as_mut().unwrap())
150        {
151            match event {
152                ComponentEvent::Inserted(index) => {
153                    self.updated.add(*index);
154                }
155                ComponentEvent::Modified(index) => {
156                    self.updated.add(*index);
157                }
158                ComponentEvent::Removed(index) => {
159                    self.updated.remove(*index);
160                }
161            }
162        }
163
164        // Check for updated next frame poses
165        // Uses FlaggedStorage
166        for (entity, pose, next_pose, shape, _) in
167            (&*entities, &poses, &next_poses, &mut shapes, &self.updated).join()
168        {
169            shape.update(pose, Some(&next_pose.value));
170
171            // Update the wrapper in the tree for the shape
172            if let Some(node_index) = self.entities.get(&entity).cloned() {
173                tree.update_node(node_index, (entity, shape.bound().clone()).into());
174            }
175        }
176
177        // process possibly updated values
178        tree.update();
179
180        // do refitting
181        tree.do_refit();
182    }
183
184    fn setup(&mut self, res: &mut Resources) {
185        use specs::prelude::SystemData;
186        Self::SystemData::setup(res);
187        let mut poses = WriteStorage::<T>::fetch(res);
188        self.pose_reader = Some(poses.register_reader());
189        let mut next_poses = WriteStorage::<NextFrame<T>>::fetch(res);
190        self.next_pose_reader = Some(next_poses.register_reader());
191    }
192}