rhusics_ecs/collide/systems/
basic.rs

1use std::fmt::Debug;
2
3use cgmath::prelude::*;
4use collision::prelude::*;
5use shrev::EventChannel;
6use specs::prelude::{Component, Entities, Entity, Join, ReadStorage, System, Write, WriteStorage};
7
8use core::{
9    basic_collide, BroadPhase, CollisionData, CollisionShape, ContactEvent, GetId, NarrowPhase,
10    NextFrame, Primitive,
11};
12
13/// Collision detection [system](https://docs.rs/specs/0.9.5/specs/trait.System.html) for use with
14/// [`specs`](https://docs.rs/specs/0.9.5/specs/).
15///
16/// Has support for both broad phase and narrow phase collision detection. Will only do narrow phase
17/// if both broad and narrow phase is activated.
18///
19/// Can handle any transform component type, as long as the type implements
20/// [`Transform`](https://docs.rs/cgmath/0.15.0/cgmath/trait.Transform.html), and as long as the
21/// storage is wrapped in a
22/// [`FlaggedStorage`](https://docs.rs/specs/0.9.5/specs/struct.FlaggedStorage.html).
23///
24/// ### Type parameters:
25///
26/// - `P`: Shape primitive
27/// - `T`: Transform
28/// - `D`: Data accepted by broad phase
29/// - `B`: Bounding volume
30/// - `Y`: Shape type, see `Collider`
31///
32/// ### System Function:
33///
34/// `fn(Entities, T, NextFrame<T>, CollisionShape) -> (CollisionShape, EventChannel<ContactEvent>)`
35pub struct BasicCollisionSystem<P, T, D, B, Y = ()>
36where
37    P: Primitive,
38    B: Bound,
39{
40    narrow: Option<Box<NarrowPhase<P, T, B, Y>>>,
41    broad: Option<Box<BroadPhase<D>>>,
42}
43
44impl<P, T, D, B, Y> BasicCollisionSystem<P, T, D, B, Y>
45where
46    P: Primitive + ComputeBound<B> + Send + Sync + 'static,
47    P::Point: Debug + Send + Sync + 'static,
48    <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
49    <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
50    T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
51    Y: Default + Send + Sync + 'static,
52    B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
53    D: HasBound<Bound = B> + From<(Entity, B)> + GetId<Entity>,
54{
55    /// Create a new collision detection system, with no broad or narrow phase activated.
56    pub fn new() -> Self {
57        Self {
58            narrow: None,
59            broad: None,
60        }
61    }
62
63    /// Specify what narrow phase algorithm to use
64    pub fn with_narrow_phase<N: NarrowPhase<P, T, B, Y> + 'static>(mut self, narrow: N) -> Self {
65        self.narrow = Some(Box::new(narrow));
66        self
67    }
68
69    /// Specify what broad phase algorithm to use
70    pub fn with_broad_phase<V: BroadPhase<D> + 'static>(mut self, broad: V) -> Self {
71        self.broad = Some(Box::new(broad));
72        self
73    }
74}
75
76impl<'a, P, T, Y, D, B> System<'a> for BasicCollisionSystem<P, T, D, B, Y>
77where
78    P: Primitive + ComputeBound<B> + Send + Sync + 'static,
79    P::Point: Debug + Send + Sync + 'static,
80    <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
81    <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
82    T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
83    Y: Default + Send + Sync + 'static,
84    B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
85    D: HasBound<Bound = B> + From<(Entity, B)> + GetId<Entity>,
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, EventChannel<ContactEvent<Entity, P::Point>>>,
93    );
94
95    fn run(&mut self, system_data: Self::SystemData) {
96        let (entities, poses, next_poses, mut shapes, mut event_channel) = system_data;
97
98        if let Some(ref mut broad) = self.broad {
99            for (entity, pose, shape) in (&*entities, &poses, &mut shapes).join() {
100                shape.update(pose, next_poses.get(entity).map(|p| &p.value));
101            }
102            event_channel.iter_write(basic_collide(
103                &BasicCollisionData {
104                    poses,
105                    shapes,
106                    next_poses,
107                    entities,
108                },
109                broad,
110                &self.narrow,
111            ));
112        }
113    }
114}
115
116/// Collision data used by ECS systems
117struct BasicCollisionData<'a, P, T, B, Y>
118where
119    P: Primitive + ComputeBound<B> + Send + Sync + 'static,
120    P::Point: Debug + Send + Sync + 'static,
121    <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
122    <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
123    T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
124    Y: Default + Send + Sync + 'static,
125    B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
126{
127    /// collision shapes
128    pub shapes: WriteStorage<'a, CollisionShape<P, T, B, Y>>,
129    /// current frame poses
130    pub poses: ReadStorage<'a, T>,
131    /// next frame poses
132    pub next_poses: ReadStorage<'a, NextFrame<T>>,
133    /// entities
134    pub entities: Entities<'a>,
135}
136
137impl<'a, P, T, B, Y, D> CollisionData<Entity, P, T, B, Y, D> for BasicCollisionData<'a, P, T, B, Y>
138where
139    P: Primitive + ComputeBound<B> + Send + Sync + 'static,
140    P::Point: Debug + Send + Sync + 'static,
141    <P::Point as EuclideanSpace>::Scalar: Send + Sync + 'static,
142    <P::Point as EuclideanSpace>::Diff: Debug + Send + Sync + 'static,
143    T: Component + Transform<P::Point> + Send + Sync + Clone + 'static,
144    Y: Default + Send + Sync + 'static,
145    B: Bound<Point = P::Point> + Send + Sync + 'static + Union<B, Output = B> + Clone,
146    D: HasBound<Bound = B> + From<(Entity, B)> + GetId<Entity>,
147{
148    fn get_broad_data(&self) -> Vec<D> {
149        (&*self.entities, &self.shapes)
150            .join()
151            .map(|(entity, shape)| (entity, shape.bound().clone()).into())
152            .collect::<Vec<_>>()
153    }
154
155    fn get_shape(&self, id: Entity) -> Option<&CollisionShape<P, T, B, Y>> {
156        self.shapes.get(id)
157    }
158
159    fn get_pose(&self, id: Entity) -> Option<&T> {
160        self.poses.get(id)
161    }
162
163    fn get_next_pose(&self, id: Entity) -> Option<&T> {
164        self.next_poses.get(id).as_ref().map(|p| &p.value)
165    }
166}