nphysics3d/material/
material.rs

1use downcast_rs::Downcast;
2use na::{self, RealField};
3use std::ops::Deref;
4use std::sync::Arc;
5
6use ncollide::query::TrackedContact;
7use ncollide::shape::Shape;
8
9use crate::material::MaterialsCoefficientsTable;
10use crate::math::{Isometry, Vector};
11
12/// The context for determining the local material properties at a contact.
13#[derive(Copy, Clone)]
14pub struct MaterialContext<'a, N: RealField + Copy> {
15    /// The shape of the collider involved in the contact.
16    pub shape: &'a dyn Shape<N>,
17    /// The position of the collider involved in the contact.
18    pub position: &'a Isometry<N>,
19    /// The contact.
20    pub contact: &'a TrackedContact<N>,
21    /// Whether the bodies (and collider) in this structure are the first one involved in the
22    /// contact.
23    ///
24    /// This is `false` if the body involved in the contact is the second one.
25    pub is_first: bool,
26}
27
28impl<'a, N: RealField + Copy> MaterialContext<'a, N> {
29    pub(crate) fn new(
30        shape: &'a dyn Shape<N>,
31        position: &'a Isometry<N>,
32        contact: &'a TrackedContact<N>,
33        is_first: bool,
34    ) -> Self {
35        MaterialContext {
36            shape,
37            position,
38            contact,
39            is_first,
40        }
41    }
42}
43
44/// The way the friction and restitution coefficients of two materials should be combined.
45#[derive(Copy, Clone, Debug)]
46pub enum MaterialCombineMode {
47    /// Combination by averaging the coefficients from both materials.
48    Average,
49    /// Combination by taking the min of the coefficients from both materials.
50    ///
51    /// Has precedence over the `Average` combine mode.
52    Min,
53    /// Combination by multiplying the coefficients from both materials.
54    ///
55    /// Has precedence over the `Min` and `Average` combine modes.
56    Multiply,
57    /// Combination by taking the max the coefficients from both materials.
58    ///
59    /// Has precedence over all other combine mode.
60    Max,
61    /// Should not be used directly. This is set as a result of the `combine` method
62    /// if the combination was performed by a lookup on the `MaterialsCoefficientsTable`.
63    Lookup, // Same as Average if specified by the user.
64}
65
66impl MaterialCombineMode {
67    /// Combines two coefficients using their associated MaterialCombineMode.
68    ///
69    /// The combine mode with the highest precedence among the two provided determines
70    /// the actual formula used. Precedences are described on the `MaterialCombineMode` enum.
71    #[inline]
72    pub fn combine<N: RealField + Copy>(a: (N, Self), b: (N, Self)) -> (N, MaterialCombineMode) {
73        match (a.1, b.1) {
74            (MaterialCombineMode::Max, _) | (_, MaterialCombineMode::Max) => {
75                (a.0.max(b.0), MaterialCombineMode::Max)
76            }
77            (MaterialCombineMode::Multiply, _) | (_, MaterialCombineMode::Multiply) => {
78                (a.0 * b.0, MaterialCombineMode::Multiply)
79            }
80            (MaterialCombineMode::Min, _) | (_, MaterialCombineMode::Min) => {
81                (a.0.min(b.0), MaterialCombineMode::Min)
82            }
83            // Average
84            _ => ((a.0 + b.0) * na::convert(0.5), MaterialCombineMode::Average),
85        }
86    }
87}
88
89/// Computed material properties at a contact point.
90pub struct LocalMaterialProperties<N: RealField + Copy> {
91    /// The optional material identifier used for pairwise material coefficient lookup table.
92    pub id: Option<MaterialId>,
93    /// The friction coefficient and its combination mode.
94    pub friction: (N, MaterialCombineMode),
95    /// The restitution coefficient and its combination mode.
96    pub restitution: (N, MaterialCombineMode),
97    /// The surface velocity at this point.
98    pub surface_velocity: Vector<N>,
99}
100
101/// An utility trait to clone material trait-objects.
102pub trait MaterialClone<N: RealField + Copy> {
103    /// Clone a material trait-object.
104    fn clone_box(&self) -> Box<dyn Material<N>> {
105        unimplemented!()
106    }
107}
108
109/// The identifier of a material.
110pub type MaterialId = u32;
111
112impl<N: RealField + Copy, T: 'static + Material<N> + Clone> MaterialClone<N> for T {
113    fn clone_box(&self) -> Box<dyn Material<N>> {
114        Box::new(self.clone())
115    }
116}
117
118/// An abstract material.
119pub trait Material<N: RealField + Copy>: Downcast + Send + Sync + MaterialClone<N> {
120    /// Retrieve the local material properties of a collider at the given contact point.
121    fn local_properties(&self, context: MaterialContext<N>) -> LocalMaterialProperties<N>;
122}
123
124impl_downcast!(Material<N> where N: RealField + Copy);
125
126impl<N: RealField + Copy> Clone for Box<dyn Material<N>> {
127    fn clone(&self) -> Box<dyn Material<N>> {
128        self.clone_box()
129    }
130}
131
132impl<N: RealField + Copy> dyn Material<N> {
133    /// Combine two materials given their contexts and a material lookup table.
134    pub fn combine<M1, M2>(
135        table: &MaterialsCoefficientsTable<N>,
136        material1: &M1,
137        context1: MaterialContext<N>,
138        material2: &M2,
139        context2: MaterialContext<N>,
140    ) -> LocalMaterialProperties<N>
141    where
142        M1: ?Sized + Material<N>,
143        M2: ?Sized + Material<N>,
144    {
145        let props1 = material1.local_properties(context1);
146        let props2 = material2.local_properties(context2);
147        let restitution;
148        let friction;
149
150        match (props1.id, props2.id) {
151            (Some(id1), Some(id2)) => {
152                restitution = table
153                    .restitution_coefficient(id1, id2)
154                    .map(|coeff| (coeff, MaterialCombineMode::Lookup))
155                    .unwrap_or_else(|| {
156                        MaterialCombineMode::combine(props1.restitution, props2.restitution)
157                    });
158                friction = table
159                    .friction_coefficient(id1, id2)
160                    .map(|coeff| (coeff, MaterialCombineMode::Lookup))
161                    .unwrap_or_else(|| {
162                        MaterialCombineMode::combine(props1.friction, props2.friction)
163                    });
164            }
165            _ => {
166                restitution = MaterialCombineMode::combine(props1.restitution, props2.restitution);
167                friction = MaterialCombineMode::combine(props1.friction, props2.friction);
168            }
169        }
170
171        LocalMaterialProperties {
172            id: None,
173            friction,
174            restitution,
175            surface_velocity: props1.surface_velocity - props2.surface_velocity,
176        }
177    }
178}
179
180/// A shared handle to an abstract shape.
181///
182/// This can be mutated using COW.
183#[derive(Clone)]
184pub struct MaterialHandle<N: RealField + Copy>(Arc<Box<dyn Material<N>>>);
185
186impl<N: RealField + Copy> MaterialHandle<N> {
187    /// Creates a sharable shape handle from a shape.
188    #[inline]
189    pub fn new<S: Material<N> + Clone>(material: S) -> MaterialHandle<N> {
190        MaterialHandle(Arc::new(Box::new(material)))
191    }
192
193    pub(crate) fn make_mut(&mut self) -> &mut dyn Material<N> {
194        &mut **Arc::make_mut(&mut self.0)
195    }
196}
197
198impl<N: RealField + Copy> AsRef<dyn Material<N>> for MaterialHandle<N> {
199    #[inline]
200    fn as_ref(&self) -> &dyn Material<N> {
201        &*self.deref()
202    }
203}
204
205impl<N: RealField + Copy> Deref for MaterialHandle<N> {
206    type Target = dyn Material<N>;
207
208    #[inline]
209    fn deref(&self) -> &dyn Material<N> {
210        &**self.0.deref()
211    }
212}