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}