1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
use crate::dynamics::RigidBody;
use crate::geometry::{Collider, ColliderHandle, ContactManifold, SolverContact, SolverFlags};
use crate::math::{Real, Vector};
use na::ComplexField;

/// Context given to custom collision filters to filter-out collisions.
pub struct PairFilterContext<'a> {
    /// The first collider involved in the potential collision.
    pub rigid_body1: &'a RigidBody,
    /// The first collider involved in the potential collision.
    pub rigid_body2: &'a RigidBody,
    /// The first collider involved in the potential collision.
    pub collider_handle1: ColliderHandle,
    /// The first collider involved in the potential collision.
    pub collider_handle2: ColliderHandle,
    /// The first collider involved in the potential collision.
    pub collider1: &'a Collider,
    /// The first collider involved in the potential collision.
    pub collider2: &'a Collider,
}

/// Context given to custom contact modifiers to modify the contacts seen by the constraints solver.
pub struct ContactModificationContext<'a> {
    /// The first collider involved in the potential collision.
    pub rigid_body1: &'a RigidBody,
    /// The first collider involved in the potential collision.
    pub rigid_body2: &'a RigidBody,
    /// The first collider involved in the potential collision.
    pub collider_handle1: ColliderHandle,
    /// The first collider involved in the potential collision.
    pub collider_handle2: ColliderHandle,
    /// The first collider involved in the potential collision.
    pub collider1: &'a Collider,
    /// The first collider involved in the potential collision.
    pub collider2: &'a Collider,
    /// The contact manifold.
    pub manifold: &'a ContactManifold,
    /// The solver contacts that can be modified.
    pub solver_contacts: &'a mut Vec<SolverContact>,
    /// The contact normal that can be modified.
    pub normal: &'a mut Vector<Real>,
    /// User-defined data attached to the manifold.
    // NOTE: we keep this a &'a mut u32 to emphasize the
    // fact that this can be modified.
    pub user_data: &'a mut u32,
}

impl<'a> ContactModificationContext<'a> {
    /// Helper function to update `self` to emulate a oneway-platform.
    ///
    /// The "oneway" behavior will only allow contacts between two colliders
    /// if the local contact normal of the first collider involved in the contact
    /// is almost aligned with the provided `allowed_local_n1` direction.
    ///
    /// To make this method work properly it must be called as part of the
    /// `PhysicsHooks::modify_solver_contacts` method at each timestep, for each
    /// contact manifold involving a one-way platform. The `self.user_data` field
    /// must not be modified from the outside of this method.
    pub fn update_as_oneway_platform(
        &mut self,
        allowed_local_n1: &Vector<Real>,
        allowed_angle: Real,
    ) {
        const CONTACT_CONFIGURATION_UNKNOWN: u32 = 0;
        const CONTACT_CURRENTLY_ALLOWED: u32 = 1;
        const CONTACT_CURRENTLY_FORBIDDEN: u32 = 2;

        let cang = ComplexField::cos(allowed_angle);

        // Test the allowed normal with the local-space contact normal that
        // points towards the exterior of context.collider1.
        let contact_is_ok = self.manifold.local_n1.dot(&allowed_local_n1) >= cang;

        match *self.user_data {
            CONTACT_CONFIGURATION_UNKNOWN => {
                if contact_is_ok {
                    // The contact is close enough to the allowed normal.
                    *self.user_data = CONTACT_CURRENTLY_ALLOWED;
                } else {
                    // The contact normal isn't close enough to the allowed
                    // normal, so remove all the contacts and mark further contacts
                    // as forbidden.
                    self.solver_contacts.clear();

                    // NOTE: in some very rare cases `local_n1` will be
                    // zero if the objects are exactly touching at one point.
                    // So in this case we can't really conclude.
                    // If the norm is non-zero, then we can tell we need to forbid
                    // further contacts. Otherwise we have to wait for the next frame.
                    if self.manifold.local_n1.norm_squared() > 0.1 {
                        *self.user_data = CONTACT_CURRENTLY_FORBIDDEN;
                    }
                }
            }
            CONTACT_CURRENTLY_FORBIDDEN => {
                // Contacts are forbidden so we need to continue forbidding contacts
                // until all the contacts are non-penetrating again. In that case, if
                // the contacts are OK wrt. the contact normal, then we can mark them as allowed.
                if contact_is_ok && self.solver_contacts.iter().all(|c| c.dist > 0.0) {
                    *self.user_data = CONTACT_CURRENTLY_ALLOWED;
                } else {
                    // Discard all the contacts.
                    self.solver_contacts.clear();
                }
            }
            CONTACT_CURRENTLY_ALLOWED => {
                // We allow all the contacts right now. The configuration becomes
                // uncertain again when the contact manifold no longer contains any contact.
                if self.solver_contacts.is_empty() {
                    *self.user_data = CONTACT_CONFIGURATION_UNKNOWN;
                }
            }
            _ => unreachable!(),
        }
    }
}

bitflags::bitflags! {
    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
    /// Flags affecting the behavior of the constraints solver for a given contact manifold.
    pub struct PhysicsHooksFlags: u32 {
        /// If set, Rapier will call `PhysicsHooks::filter_contact_pair` whenever relevant.
        const FILTER_CONTACT_PAIR = 0b0001;
        /// If set, Rapier will call `PhysicsHooks::filter_intersection_pair` whenever relevant.
        const FILTER_INTERSECTION_PAIR = 0b0010;
        /// If set, Rapier will call `PhysicsHooks::modify_solver_contact` whenever relevant.
        const MODIFY_SOLVER_CONTACTS = 0b0100;
    }
}

/// User-defined functions called by the physics engines during one timestep in order to customize its behavior.
pub trait PhysicsHooks: Send + Sync {
    /// The sets of hooks that must be taken into account.
    fn active_hooks(&self) -> PhysicsHooksFlags;

    /// Applies the contact pair filter.
    ///
    /// Note that this method will only be called if `self.active_hooks()`
    /// contains the `PhysicsHooksFlags::FILTER_CONTACT_PAIR` flags.
    ///
    /// User-defined filter for potential contact pairs detected by the broad-phase.
    /// This can be used to apply custom logic in order to decide whether two colliders
    /// should have their contact computed by the narrow-phase, and if these contact
    /// should be solved by the constraints solver
    ///
    /// Note that using a contact pair filter will replace the default contact filtering
    /// which consists of preventing contact computation between two non-dynamic bodies.
    ///
    /// This filtering method is called after taking into account the colliders collision groups.
    ///
    /// If this returns `None`, then the narrow-phase will ignore this contact pair and
    /// not compute any contact manifolds for it.
    /// If this returns `Some`, then the narrow-phase will compute contact manifolds for
    /// this pair of colliders, and configure them with the returned solver flags. For
    /// example, if this returns `Some(SolverFlags::COMPUTE_IMPULSES)` then the contacts
    /// will be taken into account by the constraints solver. If this returns
    /// `Some(SolverFlags::empty())` then the constraints solver will ignore these
    /// contacts.
    fn filter_contact_pair(&self, _context: &PairFilterContext) -> Option<SolverFlags> {
        None
    }

    /// Applies the intersection pair filter.
    ///
    /// Note that this method will only be called if `self.active_hooks()`
    /// contains the `PhysicsHooksFlags::FILTER_INTERSECTION_PAIR` flags.
    ///
    /// User-defined filter for potential intersection pairs detected by the broad-phase.
    ///
    /// This can be used to apply custom logic in order to decide whether two colliders
    /// should have their intersection computed by the narrow-phase.
    ///
    /// Note that using an intersection pair filter will replace the default intersection filtering
    /// which consists of preventing intersection computation between two non-dynamic bodies.
    ///
    /// This filtering method is called after taking into account the colliders collision groups.
    ///
    /// If this returns `false`, then the narrow-phase will ignore this pair and
    /// not compute any intersection information for it.
    /// If this return `true` then the narrow-phase will compute intersection
    /// information for this pair.
    fn filter_intersection_pair(&self, _context: &PairFilterContext) -> bool {
        false
    }

    /// Modifies the set of contacts seen by the constraints solver.
    ///
    /// Note that this method will only be called if `self.active_hooks()`
    /// contains the `PhysicsHooksFlags::MODIFY_SOLVER_CONTACTS` flags.
    ///
    /// By default, the content of `solver_contacts` is computed from `manifold.points`.
    /// This method will be called on each contact manifold which have the flag `SolverFlags::modify_solver_contacts` set.
    /// This method can be used to modify the set of solver contacts seen by the constraints solver: contacts
    /// can be removed and modified.
    ///
    /// Note that if all the contacts have to be ignored by the constraint solver, you may simply
    /// do `context.solver_contacts.clear()`.
    ///
    /// Modifying the solver contacts allow you to achieve various effects, including:
    /// - Simulating conveyor belts by setting the `surface_velocity` of a solver contact.
    /// - Simulating shapes with multiply materials by modifying the friction and restitution
    ///   coefficient depending of the features in contacts.
    /// - Simulating one-way platforms depending on the contact normal.
    ///
    /// Each contact manifold is given a `u32` user-defined data that is persistent between
    /// timesteps (as long as the contact manifold exists). This user-defined data is initialized
    /// as 0 and can be modified in `context.user_data`.
    ///
    /// The world-space contact normal can be modified in `context.normal`.
    fn modify_solver_contacts(&self, _context: &mut ContactModificationContext) {}
}

impl PhysicsHooks for () {
    fn active_hooks(&self) -> PhysicsHooksFlags {
        PhysicsHooksFlags::empty()
    }

    fn filter_contact_pair(&self, _: &PairFilterContext) -> Option<SolverFlags> {
        None
    }

    fn filter_intersection_pair(&self, _: &PairFilterContext) -> bool {
        false
    }

    fn modify_solver_contacts(&self, _: &mut ContactModificationContext) {}
}