bevy_rapier2d/pipeline/
physics_hooks.rs

1use bevy::{ecs::system::SystemParam, prelude::*};
2use rapier::{
3    pipeline::{ContactModificationContext, PairFilterContext},
4    prelude::{PhysicsHooks, SolverFlags},
5};
6
7/// Read-only access to the properties of a collision pair filter context.
8pub struct PairFilterContextView<'a> {
9    /// The raw context from Rapier.
10    pub raw: &'a PairFilterContext<'a>,
11}
12
13impl PairFilterContextView<'_> {
14    /// The entity of the first collider involved in the potential collision.
15    pub fn collider1(&self) -> Entity {
16        let co1 = &self.raw.colliders[self.raw.collider1];
17        Entity::from_bits(co1.user_data as u64)
18    }
19
20    /// The entity of the second collider involved in the potential collision.
21    pub fn collider2(&self) -> Entity {
22        let co2 = &self.raw.colliders[self.raw.collider2];
23        Entity::from_bits(co2.user_data as u64)
24    }
25
26    /// The entity of the first rigid-body (if `self.collider1()` is attached to a rigid-body)
27    /// involved in the potential collision.
28    pub fn rigid_body1(&self) -> Option<Entity> {
29        self.raw.rigid_body1.map(|h| {
30            let co1 = &self.raw.bodies[h];
31            Entity::from_bits(co1.user_data as u64)
32        })
33    }
34
35    /// The entity of the second rigid-body (if `self.collider1()` is attached to a rigid-body)
36    /// involved in the potential collision.
37    pub fn rigid_body2(&self) -> Option<Entity> {
38        self.raw.rigid_body2.map(|h| {
39            let co2 = &self.raw.bodies[h];
40            Entity::from_bits(co2.user_data as u64)
41        })
42    }
43}
44
45/// Read-write access to the properties of a contact modification context.
46pub struct ContactModificationContextView<'a, 'b> {
47    /// The raw context from Rapier.
48    pub raw: &'a mut ContactModificationContext<'b>,
49}
50
51impl ContactModificationContextView<'_, '_> {
52    /// The entity of the first collider involved in the potential collision.
53    pub fn collider1(&self) -> Entity {
54        let co1 = &self.raw.colliders[self.raw.collider1];
55        Entity::from_bits(co1.user_data as u64)
56    }
57
58    /// The entity of the second collider involved in the potential collision.
59    pub fn collider2(&self) -> Entity {
60        let co2 = &self.raw.colliders[self.raw.collider2];
61        Entity::from_bits(co2.user_data as u64)
62    }
63
64    /// The entity of the first rigid-body (if `self.collider1()` is attached to a rigid-body)
65    /// involved in the potential collision.
66    pub fn rigid_body1(&self) -> Option<Entity> {
67        self.raw.rigid_body1.map(|h| {
68            let co1 = &self.raw.bodies[h];
69            Entity::from_bits(co1.user_data as u64)
70        })
71    }
72
73    /// The entity of the second rigid-body (if `self.collider1()` is attached to a rigid-body)
74    /// involved in the potential collision.
75    pub fn rigid_body2(&self) -> Option<Entity> {
76        self.raw.rigid_body2.map(|h| {
77            let co2 = &self.raw.bodies[h];
78            Entity::from_bits(co2.user_data as u64)
79        })
80    }
81}
82
83/// User-defined functions called by the physics engines during one timestep in order to customize its behavior.
84pub trait BevyPhysicsHooks: SystemParam + Send + Sync {
85    /// Applies the contact pair filter.
86    ///
87    /// Note that this method will only be called if at least one of the colliders
88    /// involved in the contact contains the `ActiveHooks::FILTER_CONTACT_PAIRS` flags
89    /// in its physics hooks flags.
90    ///
91    /// User-defined filter for potential contact pairs detected by the broad-phase.
92    /// This can be used to apply custom logic in order to decide whether two colliders
93    /// should have their contact computed by the narrow-phase, and if these contact
94    /// should be solved by the constraints solver
95    ///
96    /// Note that using a contact pair filter will replace the default contact filtering
97    /// which consists of preventing contact computation between two non-dynamic bodies.
98    ///
99    /// This filtering method is called after taking into account the colliders collision groups.
100    ///
101    /// If this returns `None`, then the narrow-phase will ignore this contact pair and
102    /// not compute any contact manifolds for it.
103    /// If this returns `Some`, then the narrow-phase will compute contact manifolds for
104    /// this pair of colliders, and configure them with the returned solver flags. For
105    /// example, if this returns `Some(SolverFlags::COMPUTE_IMPULSES)` then the contacts
106    /// will be taken into account by the constraints solver. If this returns
107    /// `Some(SolverFlags::empty())` then the constraints solver will ignore these
108    /// contacts.
109    fn filter_contact_pair(&self, _context: PairFilterContextView) -> Option<SolverFlags> {
110        None
111    }
112
113    /// Applies the intersection pair filter.
114    ///
115    /// Note that this method will only be called if at least one of the colliders
116    /// involved in the contact contains the `ActiveHooks::FILTER_INTERSECTION_PAIR` flags
117    /// in its physics hooks flags.
118    ///
119    /// User-defined filter for potential intersection pairs detected by the broad-phase.
120    ///
121    /// This can be used to apply custom logic in order to decide whether two colliders
122    /// should have their intersection computed by the narrow-phase.
123    ///
124    /// Note that using an intersection pair filter will replace the default intersection filtering
125    /// which consists of preventing intersection computation between two non-dynamic bodies.
126    ///
127    /// This filtering method is called after taking into account the colliders collision groups.
128    ///
129    /// If this returns `false`, then the narrow-phase will ignore this pair and
130    /// not compute any intersection information for it.
131    /// If this return `true` then the narrow-phase will compute intersection
132    /// information for this pair.
133    fn filter_intersection_pair(&self, _context: PairFilterContextView) -> bool {
134        false
135    }
136
137    /// Modifies the set of contacts seen by the constraints solver.
138    ///
139    /// Note that this method will only be called if at least one of the colliders
140    /// involved in the contact contains the `ActiveHooks::MODIFY_SOLVER_CONTACTS` flags
141    /// in its physics hooks flags.
142    ///
143    /// By default, the content of `solver_contacts` is computed from `manifold.points`.
144    /// This method will be called on each contact manifold which have the flag `SolverFlags::modify_solver_contacts` set.
145    /// This method can be used to modify the set of solver contacts seen by the constraints solver: contacts
146    /// can be removed and modified.
147    ///
148    /// Note that if all the contacts have to be ignored by the constraint solver, you may simply
149    /// do `context.solver_contacts.clear()`.
150    ///
151    /// Modifying the solver contacts allow you to achieve various effects, including:
152    /// - Simulating conveyor belts by setting the `surface_velocity` of a solver contact.
153    /// - Simulating shapes with multiply materials by modifying the friction and restitution
154    ///   coefficient depending of the features in contacts.
155    /// - Simulating one-way platforms depending on the contact normal.
156    ///
157    /// Each contact manifold is given a `u32` user-defined data that is persistent between
158    /// timesteps (as long as the contact manifold exists). This user-defined data is initialized
159    /// as 0 and can be modified in `context.user_data`.
160    ///
161    /// The world-space contact normal can be modified in `context.normal`.
162    fn modify_solver_contacts(&self, _context: ContactModificationContextView) {}
163}
164
165impl<T> BevyPhysicsHooks for T
166where
167    T: 'static + PhysicsHooks + SystemParam + Send + Sync,
168    for<'w, 's> T: SystemParam<Item<'w, 's> = T>,
169{
170    fn filter_contact_pair(&self, context: PairFilterContextView) -> Option<SolverFlags> {
171        PhysicsHooks::filter_contact_pair(self, context.raw)
172    }
173
174    fn filter_intersection_pair(&self, context: PairFilterContextView) -> bool {
175        PhysicsHooks::filter_intersection_pair(self, context.raw)
176    }
177
178    fn modify_solver_contacts(&self, context: ContactModificationContextView) {
179        PhysicsHooks::modify_solver_contacts(self, context.raw)
180    }
181}
182
183/// Adapts a type implementing `BevyPhysicsHooks` so that it implements `PhysicsHooks`.
184pub(crate) struct BevyPhysicsHooksAdapter<Hooks>
185where
186    Hooks: BevyPhysicsHooks,
187{
188    hooks: Hooks,
189}
190
191impl<Hooks> BevyPhysicsHooksAdapter<Hooks>
192where
193    Hooks: BevyPhysicsHooks,
194{
195    pub(crate) fn new(hooks: Hooks) -> Self {
196        Self { hooks }
197    }
198}
199
200impl<Hooks> PhysicsHooks for BevyPhysicsHooksAdapter<Hooks>
201where
202    Hooks: BevyPhysicsHooks,
203{
204    fn filter_contact_pair(&self, context: &PairFilterContext) -> Option<SolverFlags> {
205        let context_view = PairFilterContextView { raw: context };
206        self.hooks.filter_contact_pair(context_view)
207    }
208
209    fn filter_intersection_pair(&self, context: &PairFilterContext) -> bool {
210        let context_view = PairFilterContextView { raw: context };
211        self.hooks.filter_intersection_pair(context_view)
212    }
213
214    fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
215        let context_view = ContactModificationContextView { raw: context };
216        self.hooks.modify_solver_contacts(context_view)
217    }
218}