parry3d/query/contact_manifolds/normals_constraint.rs
1use crate::math::{Isometry, Real, 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<Real>) -> 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<Real>) -> Option<Vector<Real>> {
29 self.project_local_normal_mut(&mut normal).then_some(normal)
30 }
31
32 // NOTE: despite this not taking an UnitVector, the normal is
33 // assumed to be unit-sized.
34 /// Applies normal correction to the unit vectors `normal1` and `normal2` based on the
35 /// assumption that `normal1` is in the same coordinates space as `Self`.
36 ///
37 /// The `normal2` will be modified to be equal to `-normal1` expressed in the local coordinate
38 /// space of the second shape.
39 ///
40 /// If this method returns `false` then the contacts associated to that normal should be
41 /// considered invalid and be ignored by the collision-detection pipeline.
42 fn project_local_normal1(
43 &self,
44 pos12: &Isometry<Real>,
45 normal1: &mut Vector<Real>,
46 normal2: &mut Vector<Real>,
47 ) -> bool {
48 if !self.project_local_normal_mut(normal1) {
49 return false;
50 }
51
52 *normal2 = pos12.inverse_transform_vector(&-*normal1);
53
54 true
55 }
56
57 /// Applies normal correction to the unit vectors `normal1` and `normal2` based on the
58 /// assumption that `normal2` is in the same coordinates space as `Self`.
59 ///
60 /// The `normal1` will be modified to be equal to `-normal2` expressed in the local coordinate
61 /// space of the first shape.
62 ///
63 /// If this method returns `false` then the contacts associated to that normal should be
64 /// considered invalid and be ignored by the collision-detection pipeline.
65 fn project_local_normal2(
66 &self,
67 pos12: &Isometry<Real>,
68 normal1: &mut Vector<Real>,
69 normal2: &mut Vector<Real>,
70 ) -> bool {
71 if !self.project_local_normal_mut(normal2) {
72 return false;
73 }
74
75 *normal1 = pos12 * (-*normal2);
76
77 true
78 }
79}
80
81impl NormalConstraints for () {
82 fn project_local_normal_mut(&self, _: &mut Vector<Real>) -> bool {
83 true
84 }
85}
86
87/// A pair of [`NormalConstraints`].
88pub trait NormalConstraintsPair {
89 /// Applies the normal constraints to `normal1` and `normal2`.
90 ///
91 /// This trait is mostly used internally to combine two [`NormalConstraints`] conveniently.
92 fn project_local_normals(
93 &self,
94 pos12: &Isometry<Real>,
95 normal1: &mut Vector<Real>,
96 normal2: &mut Vector<Real>,
97 ) -> bool;
98}
99
100// We generally use Option<&dyn NormalConstraints> instead of the naked
101// trait-object in our codebase so providing this impl is convenient.
102impl NormalConstraintsPair
103 for (
104 Option<&dyn NormalConstraints>,
105 Option<&dyn NormalConstraints>,
106 )
107{
108 fn project_local_normals(
109 &self,
110 pos12: &Isometry<Real>,
111 normal1: &mut Vector<Real>,
112 normal2: &mut Vector<Real>,
113 ) -> bool {
114 if let Some(proj) = self.0 {
115 if !proj.project_local_normal1(pos12, normal1, normal2) {
116 return false;
117 }
118 }
119
120 if let Some(proj) = self.1 {
121 proj.project_local_normal2(pos12, normal1, normal2)
122 } else {
123 true
124 }
125 }
126}