1use std::fmt::Debug;
2
3use cgmath::prelude::*;
4use cgmath::BaseFloat;
5use collision::dbvt::{DynamicBoundingVolumeTree, TreeValue};
6use collision::prelude::*;
7use shrev::EventChannel;
8use specs::prelude::{
9 BitSet, Component, ComponentEvent, Entities, Entity, Join, ReadStorage, ReaderId, Resources,
10 System, Tracked, Write,
11};
12
13use core::{
14 tree_collide, BroadPhase, CollisionData, CollisionShape, ContactEvent, GetId, NarrowPhase,
15 NextFrame, Primitive,
16};
17
18pub struct SpatialCollisionSystem<P, T, D, B, Y = ()>
44where
45 P: Primitive,
46 B: Bound,
47{
48 narrow: Option<Box<NarrowPhase<P, T, B, Y>>>,
49 broad: Option<Box<BroadPhase<D>>>,
50 dirty: BitSet,
51 pose_reader: Option<ReaderId<ComponentEvent>>,
52 next_pose_reader: Option<ReaderId<ComponentEvent>>,
53}
54
55impl<P, T, D, B, Y> SpatialCollisionSystem<P, T, D, B, Y>
56where
57 P: Primitive + Send + Sync + 'static,
58 <P::Point as EuclideanSpace>::Diff: Debug,
59 <P::Point as EuclideanSpace>::Scalar: BaseFloat,
60 B: Clone
61 + Debug
62 + Send
63 + Sync
64 + 'static
65 + Bound<Point = P::Point>
66 + Union<B, Output = B>
67 + Contains<B>
68 + SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>,
69 T: Transform<P::Point> + Component,
70 D: HasBound<Bound = B>,
71{
72 pub fn new() -> Self {
74 SpatialCollisionSystem {
75 narrow: None,
76 broad: None,
77 dirty: BitSet::default(),
78 pose_reader: None,
79 next_pose_reader: None,
80 }
81 }
82
83 pub fn with_narrow_phase<N: NarrowPhase<P, T, B, Y> + 'static>(mut self, narrow: N) -> Self {
85 self.narrow = Some(Box::new(narrow));
86 self
87 }
88
89 pub fn with_broad_phase<V: BroadPhase<D> + 'static>(mut self, broad: V) -> Self {
91 self.broad = Some(Box::new(broad));
92 self
93 }
94}
95
96impl<'a, P, T, Y, B, D> System<'a> for SpatialCollisionSystem<P, T, (usize, D), B, Y>
97where
98 P: Primitive + ComputeBound<B> + Send + Sync + 'static,
99 P::Point: EuclideanSpace,
100 <P::Point as EuclideanSpace>::Scalar: BaseFloat + Send + Sync + 'static,
101 B: Clone
102 + Debug
103 + Send
104 + Sync
105 + 'static
106 + Bound<Point = P::Point>
107 + Union<B, Output = B>
108 + Discrete<B>
109 + Contains<B>
110 + SurfaceArea<Scalar = <P::Point as EuclideanSpace>::Scalar>,
111 <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
112 P::Point: Debug + Send + Sync + 'static,
113 T: Component + Clone + Debug + Transform<P::Point> + Send + Sync + 'static,
114 T::Storage: Tracked,
115 Y: Default + Send + Sync + 'static,
116 D: Send + Sync + 'static + TreeValue<Bound = B> + HasBound<Bound = B> + GetId<Entity>,
117{
118 type SystemData = (
119 Entities<'a>,
120 ReadStorage<'a, T>,
121 ReadStorage<'a, NextFrame<T>>,
122 ReadStorage<'a, CollisionShape<P, T, B, Y>>,
123 Write<'a, EventChannel<ContactEvent<Entity, P::Point>>>,
124 Write<'a, DynamicBoundingVolumeTree<D>>,
125 );
126
127 fn run(&mut self, system_data: Self::SystemData) {
128 let (entities, poses, next_poses, shapes, mut event_channel, mut tree) = system_data;
129 self.dirty.clear();
130
131 for event in poses.channel().read(self.pose_reader.as_mut().unwrap()) {
132 match event {
133 ComponentEvent::Inserted(index) => {
134 self.dirty.add(*index);
135 }
136 ComponentEvent::Modified(index) => {
137 self.dirty.add(*index);
138 }
139 ComponentEvent::Removed(index) => {
140 self.dirty.remove(*index);
141 }
142 }
143 }
144 for event in next_poses
145 .channel()
146 .read(self.next_pose_reader.as_mut().unwrap())
147 {
148 match event {
149 ComponentEvent::Inserted(index) => {
150 self.dirty.add(*index);
151 }
152 ComponentEvent::Modified(index) => {
153 self.dirty.add(*index);
154 }
155 ComponentEvent::Removed(index) => {
156 self.dirty.remove(*index);
157 }
158 }
159 }
160
161 event_channel.iter_write(tree_collide(
162 &SpatialCollisionData {
163 poses,
164 shapes,
165 next_poses,
166 entities,
167 dirty: &self.dirty,
168 },
169 &mut *tree,
170 &mut self.broad,
171 &self.narrow,
172 ));
173 }
174
175 fn setup(&mut self, res: &mut Resources) {
176 use specs::prelude::{SystemData, WriteStorage};
177 Self::SystemData::setup(res);
178 let mut poses = WriteStorage::<T>::fetch(res);
179 self.pose_reader = Some(poses.register_reader());
180 let mut next_poses = WriteStorage::<NextFrame<T>>::fetch(res);
181 self.next_pose_reader = Some(next_poses.register_reader());
182 }
183}
184
185pub struct SpatialCollisionData<'a, P, T, B, Y>
187where
188 P: Primitive + ComputeBound<B> + Send + Sync + 'static,
189 P::Point: Debug + Send + Sync + 'static,
190 <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
191 <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
192 T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
193 Y: Default + Send + Sync + 'static,
194 B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
195{
196 pub shapes: ReadStorage<'a, CollisionShape<P, T, B, Y>>,
198 pub poses: ReadStorage<'a, T>,
200 pub next_poses: ReadStorage<'a, NextFrame<T>>,
202 pub entities: Entities<'a>,
204 pub dirty: &'a BitSet,
206}
207
208impl<'a, P, T, B, Y, D> CollisionData<Entity, P, T, B, Y, D>
209 for SpatialCollisionData<'a, P, T, B, Y>
210where
211 P: Primitive + ComputeBound<B> + Send + Sync + 'static,
212 P::Point: Debug + Send + Sync + 'static,
213 <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
214 <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
215 T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
216 Y: Default + Send + Sync + 'static,
217 B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
218{
219 fn get_broad_data(&self) -> Vec<D> {
220 Vec::default()
221 }
222
223 fn get_shape(&self, id: Entity) -> Option<&CollisionShape<P, T, B, Y>> {
224 self.shapes.get(id)
225 }
226
227 fn get_pose(&self, id: Entity) -> Option<&T> {
228 self.poses.get(id)
229 }
230
231 fn get_dirty_poses(&self) -> Vec<Entity> {
232 (&*self.entities, self.dirty, &self.shapes)
233 .join()
234 .map(|(entity, _, _)| entity)
235 .collect()
236 }
237
238 fn get_next_pose(&self, id: Entity) -> Option<&T> {
239 self.next_poses.get(id).as_ref().map(|p| &p.value)
240 }
241}