parry3d/query/contact_manifolds/
normals_constraint.rs

1use crate::math::{Pose, Vector};
2
3// NOTE: the 'static requirement is only needed for the following impl to work:
4//       impl<'a> TypedCompositeShape for dyn CompositeShape
5//       We can probably work something out if that becomes too restrictive in
6//       the future.
7/// Constraints of contact normals, generally for internal edges resolution.
8///
9/// This trait used for applying constraints on normal direction for contact manifolds calculation.
10/// Non-convex shapes will generally simplify collision-detection as a collection of simpler
11/// convex-based collision-detection problems. However, that partial convex formulation allows
12/// some contact normals that are theoretically impossible (in a convex analysis sense). The normal
13/// constraints aims to correct/remove invalid normals, avoiding some artifacts in physics
14/// simulations. In particular, this addresses the well-known "internal edges" problem on triangle
15/// meshes and heightfields.
16pub trait NormalConstraints: 'static {
17    /// Corrects in-place or discards the specified normal (assumed to be unit-sized) based on the
18    /// constraints encoded by `Self`.
19    ///
20    /// If this method returns `false` then the contacts associated to that normal should be
21    /// considered invalid and be ignored by the collision-detection pipeline.
22    fn project_local_normal_mut(&self, normal: &mut Vector) -> bool;
23    /// Corrects or discards the specified normal (assumed to be unit-sized) based on the constraints
24    /// encoded by `Self`.
25    ///
26    /// If this method returns `None` then the contacts associated to that normal should be
27    /// considered invalid and be ignored by the collision-detection pipeline.
28    fn project_local_normal(&self, mut normal: Vector) -> Option<Vector> {
29        self.project_local_normal_mut(&mut normal).then_some(normal)
30    }
31
32    // NOTE: the normal is assumed to be unit-sized.
33    /// Applies normal correction to the unit vectors `normal1` and `normal2` based on the
34    /// assumption that `normal1` is in the same coordinates space as `Self`.
35    ///
36    /// The `normal2` will be modified to be equal to `-normal1` expressed in the local coordinate
37    /// space of the second shape.
38    ///
39    /// If this method returns `false` then the contacts associated to that normal should be
40    /// considered invalid and be ignored by the collision-detection pipeline.
41    fn project_local_normal1(
42        &self,
43        pos12: &Pose,
44        normal1: &mut Vector,
45        normal2: &mut Vector,
46    ) -> bool {
47        if !self.project_local_normal_mut(normal1) {
48            return false;
49        }
50
51        *normal2 = pos12.rotation.inverse() * -*normal1;
52
53        true
54    }
55
56    /// Applies normal correction to the unit vectors `normal1` and `normal2` based on the
57    /// assumption that `normal2` is in the same coordinates space as `Self`.
58    ///
59    /// The `normal1` will be modified to be equal to `-normal2` expressed in the local coordinate
60    /// space of the first shape.
61    ///
62    /// If this method returns `false` then the contacts associated to that normal should be
63    /// considered invalid and be ignored by the collision-detection pipeline.
64    fn project_local_normal2(
65        &self,
66        pos12: &Pose,
67        normal1: &mut Vector,
68        normal2: &mut Vector,
69    ) -> bool {
70        if !self.project_local_normal_mut(normal2) {
71            return false;
72        }
73
74        *normal1 = pos12.rotation * (-*normal2);
75
76        true
77    }
78}
79
80impl NormalConstraints for () {
81    fn project_local_normal_mut(&self, _: &mut Vector) -> bool {
82        true
83    }
84}
85
86/// A pair of [`NormalConstraints`].
87pub trait NormalConstraintsPair {
88    /// Applies the normal constraints to `normal1` and `normal2`.
89    ///
90    /// This trait is mostly used internally to combine two [`NormalConstraints`] conveniently.
91    fn project_local_normals(
92        &self,
93        pos12: &Pose,
94        normal1: &mut Vector,
95        normal2: &mut Vector,
96    ) -> bool;
97}
98
99// We generally use Option<&dyn NormalConstraints> instead of the naked
100// trait-object in our codebase so providing this impl is convenient.
101impl NormalConstraintsPair
102    for (
103        Option<&dyn NormalConstraints>,
104        Option<&dyn NormalConstraints>,
105    )
106{
107    fn project_local_normals(
108        &self,
109        pos12: &Pose,
110        normal1: &mut Vector,
111        normal2: &mut Vector,
112    ) -> bool {
113        if let Some(proj) = self.0 {
114            if !proj.project_local_normal1(pos12, normal1, normal2) {
115                return false;
116            }
117        }
118
119        if let Some(proj) = self.1 {
120            proj.project_local_normal2(pos12, normal1, normal2)
121        } else {
122            true
123        }
124    }
125}