use downcast_rs::Downcast;
use na::{self, RealField};
use std::ops::Deref;
use std::sync::Arc;
use ncollide::query::TrackedContact;
use ncollide::shape::Shape;
use crate::material::MaterialsCoefficientsTable;
use crate::math::{Isometry, Vector};
#[derive(Copy, Clone)]
pub struct MaterialContext<'a, N: RealField + Copy> {
pub shape: &'a dyn Shape<N>,
pub position: &'a Isometry<N>,
pub contact: &'a TrackedContact<N>,
pub is_first: bool,
}
impl<'a, N: RealField + Copy> MaterialContext<'a, N> {
pub(crate) fn new(
shape: &'a dyn Shape<N>,
position: &'a Isometry<N>,
contact: &'a TrackedContact<N>,
is_first: bool,
) -> Self {
MaterialContext {
shape,
position,
contact,
is_first,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum MaterialCombineMode {
Average,
Min,
Multiply,
Max,
Lookup, }
impl MaterialCombineMode {
#[inline]
pub fn combine<N: RealField + Copy>(a: (N, Self), b: (N, Self)) -> (N, MaterialCombineMode) {
match (a.1, b.1) {
(MaterialCombineMode::Max, _) | (_, MaterialCombineMode::Max) => {
(a.0.max(b.0), MaterialCombineMode::Max)
}
(MaterialCombineMode::Multiply, _) | (_, MaterialCombineMode::Multiply) => {
(a.0 * b.0, MaterialCombineMode::Multiply)
}
(MaterialCombineMode::Min, _) | (_, MaterialCombineMode::Min) => {
(a.0.min(b.0), MaterialCombineMode::Min)
}
_ => ((a.0 + b.0) * na::convert(0.5), MaterialCombineMode::Average),
}
}
}
pub struct LocalMaterialProperties<N: RealField + Copy> {
pub id: Option<MaterialId>,
pub friction: (N, MaterialCombineMode),
pub restitution: (N, MaterialCombineMode),
pub surface_velocity: Vector<N>,
}
pub trait MaterialClone<N: RealField + Copy> {
fn clone_box(&self) -> Box<dyn Material<N>> {
unimplemented!()
}
}
pub type MaterialId = u32;
impl<N: RealField + Copy, T: 'static + Material<N> + Clone> MaterialClone<N> for T {
fn clone_box(&self) -> Box<dyn Material<N>> {
Box::new(self.clone())
}
}
pub trait Material<N: RealField + Copy>: Downcast + Send + Sync + MaterialClone<N> {
fn local_properties(&self, context: MaterialContext<N>) -> LocalMaterialProperties<N>;
}
impl_downcast!(Material<N> where N: RealField + Copy);
impl<N: RealField + Copy> Clone for Box<dyn Material<N>> {
fn clone(&self) -> Box<dyn Material<N>> {
self.clone_box()
}
}
impl<N: RealField + Copy> dyn Material<N> {
pub fn combine<M1, M2>(
table: &MaterialsCoefficientsTable<N>,
material1: &M1,
context1: MaterialContext<N>,
material2: &M2,
context2: MaterialContext<N>,
) -> LocalMaterialProperties<N>
where
M1: ?Sized + Material<N>,
M2: ?Sized + Material<N>,
{
let props1 = material1.local_properties(context1);
let props2 = material2.local_properties(context2);
let restitution;
let friction;
match (props1.id, props2.id) {
(Some(id1), Some(id2)) => {
restitution = table
.restitution_coefficient(id1, id2)
.map(|coeff| (coeff, MaterialCombineMode::Lookup))
.unwrap_or_else(|| {
MaterialCombineMode::combine(props1.restitution, props2.restitution)
});
friction = table
.friction_coefficient(id1, id2)
.map(|coeff| (coeff, MaterialCombineMode::Lookup))
.unwrap_or_else(|| {
MaterialCombineMode::combine(props1.friction, props2.friction)
});
}
_ => {
restitution = MaterialCombineMode::combine(props1.restitution, props2.restitution);
friction = MaterialCombineMode::combine(props1.friction, props2.friction);
}
}
LocalMaterialProperties {
id: None,
friction,
restitution,
surface_velocity: props1.surface_velocity - props2.surface_velocity,
}
}
}
#[derive(Clone)]
pub struct MaterialHandle<N: RealField + Copy>(Arc<Box<dyn Material<N>>>);
impl<N: RealField + Copy> MaterialHandle<N> {
#[inline]
pub fn new<S: Material<N> + Clone>(material: S) -> MaterialHandle<N> {
MaterialHandle(Arc::new(Box::new(material)))
}
pub(crate) fn make_mut(&mut self) -> &mut dyn Material<N> {
&mut **Arc::make_mut(&mut self.0)
}
}
impl<N: RealField + Copy> AsRef<dyn Material<N>> for MaterialHandle<N> {
#[inline]
fn as_ref(&self) -> &dyn Material<N> {
&*self.deref()
}
}
impl<N: RealField + Copy> Deref for MaterialHandle<N> {
type Target = dyn Material<N>;
#[inline]
fn deref(&self) -> &dyn Material<N> {
&**self.0.deref()
}
}