Allow several rules for combining friction/restitution coefficients.

This commit is contained in:
Crozet Sébastien
2021-01-21 16:03:27 +01:00
parent 8f330b2a00
commit 98d3980db7
7 changed files with 414 additions and 299 deletions

View File

@@ -0,0 +1,34 @@
use crate::math::Real;
/// Rules used to combine two coefficients.
///
/// This is used to determine the effective restitution and
/// friction coefficients for a contact between two colliders.
/// Each collider has its combination rule of type
/// `CoefficientCombineRule`. And the rule
/// actually used is given by `max(first_combine_rule as usize, second_combine_rule as usize)`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum CoefficientCombineRule {
/// The two coefficients are averaged.
Average = 0,
/// The smallest coefficient is chosen.
Min,
/// The two coefficients are multiplied.
Multiply,
/// The greatest coefficient is chosen.
Max,
}
impl CoefficientCombineRule {
pub(crate) fn combine(coeff1: Real, coeff2: Real, rule_value1: u8, rule_value2: u8) -> Real {
let effective_rule = rule_value1.max(rule_value2);
match effective_rule {
0 => (coeff1 + coeff1) / 2.0,
1 => coeff1.min(coeff2),
2 => coeff1 * coeff2,
_ => coeff1.max(coeff2),
}
}
}

View File

@@ -11,6 +11,7 @@ pub use self::rigid_body::{ActivationStatus, BodyStatus, RigidBody, RigidBodyBui
pub use self::rigid_body_set::{BodyPair, RigidBodyHandle, RigidBodySet}; pub use self::rigid_body_set::{BodyPair, RigidBodyHandle, RigidBodySet};
pub use cdl::mass_properties::MassProperties; pub use cdl::mass_properties::MassProperties;
// #[cfg(not(feature = "parallel"))] // #[cfg(not(feature = "parallel"))]
pub use self::coefficient_combine_rule::CoefficientCombineRule;
pub(crate) use self::joint::JointGraphEdge; pub(crate) use self::joint::JointGraphEdge;
pub(crate) use self::rigid_body::RigidBodyChanges; pub(crate) use self::rigid_body::RigidBodyChanges;
#[cfg(not(feature = "parallel"))] #[cfg(not(feature = "parallel"))]
@@ -18,6 +19,7 @@ pub(crate) use self::solver::IslandSolver;
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
pub(crate) use self::solver::ParallelIslandSolver; pub(crate) use self::solver::ParallelIslandSolver;
mod coefficient_combine_rule;
mod integration_parameters; mod integration_parameters;
mod joint; mod joint;
mod rigid_body; mod rigid_body;

View File

@@ -62,8 +62,10 @@ pub struct RigidBody {
pub(crate) mass_properties: MassProperties, pub(crate) mass_properties: MassProperties,
/// The world-space center of mass of the rigid-body. /// The world-space center of mass of the rigid-body.
pub world_com: Point<Real>, pub world_com: Point<Real>,
/// The inverse mass taking into account translation locking.
pub effective_inv_mass: Real, pub effective_inv_mass: Real,
/// The square-root of the inverse angular inertia tensor of the rigid-body. /// The square-root of the world-space inverse angular inertia tensor of the rigid-body,
/// taking into account rotation locking.
pub effective_world_inv_inertia_sqrt: AngularInertia<Real>, pub effective_world_inv_inertia_sqrt: AngularInertia<Real>,
/// The linear velocity of the rigid-body. /// The linear velocity of the rigid-body.
pub(crate) linvel: Vector<Real>, pub(crate) linvel: Vector<Real>,

View File

@@ -1,309 +1,44 @@
use crate::dynamics::{MassProperties, RigidBodyHandle}; use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle};
use crate::geometry::InteractionGroups; use crate::geometry::{ColliderShape, InteractionGroups};
use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector}; use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector};
use cdl::bounding_volume::AABB; use cdl::bounding_volume::AABB;
use cdl::shape::{ use cdl::shape::Shape;
Ball, Capsule, Compound, Cuboid, HalfSpace, HeightField, RoundCuboid, RoundShape,
RoundTriangle, Segment, Shape, ShapeType, TriMesh, Triangle,
};
#[cfg(feature = "dim3")]
use cdl::shape::{
Cone, ConvexPolyhedron, Cylinder, RoundCone, RoundConvexPolyhedron, RoundCylinder,
};
#[cfg(feature = "dim2")] #[cfg(feature = "dim2")]
use cdl::shape::{ConvexPolygon, RoundConvexPolygon}; use cdl::shape::{ConvexPolygon, RoundConvexPolygon};
use std::ops::Deref;
use std::sync::Arc;
// TODO: move this to its own file. bitflags::bitflags! {
/// The shape of a collider. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)] /// Flags affecting the behavior of the constraints solver for a given contact manifold.
pub struct ColliderShape(pub Arc<dyn Shape>); pub(crate) struct ColliderFlags: u8 {
const SENSOR = 1 << 0;
impl Deref for ColliderShape { const FRICTION_COMBINE_RULE_01 = 1 << 1;
type Target = dyn Shape; const FRICTION_COMBINE_RULE_10 = 1 << 2;
fn deref(&self) -> &dyn Shape { const RESTITUTION_COMBINE_RULE_01 = 1 << 3;
&*self.0 const RESTITUTION_COMBINE_RULE_10 = 1 << 4;
} }
} }
impl ColliderShape { impl ColliderFlags {
/// Initialize a compound shape defined by its subshapes. pub fn is_sensor(self) -> bool {
pub fn compound(shapes: Vec<(Isometry<Real>, ColliderShape)>) -> Self { self.contains(ColliderFlags::SENSOR)
let raw_shapes = shapes.into_iter().map(|s| (s.0, s.1 .0)).collect();
let compound = Compound::new(raw_shapes);
ColliderShape(Arc::new(compound))
} }
/// Initialize a ball shape defined by its radius. pub fn friction_combine_rule_value(self) -> u8 {
pub fn ball(radius: Real) -> Self { (self.bits & 0b0000_0110) >> 1
ColliderShape(Arc::new(Ball::new(radius)))
} }
/// Initialize a cylindrical shape defined by its half-height pub fn restitution_combine_rule_value(self) -> u8 {
/// (along along the y axis) and its radius. (self.bits & 0b0001_1000) >> 3
#[cfg(feature = "dim3")]
pub fn cylinder(half_height: Real, radius: Real) -> Self {
ColliderShape(Arc::new(Cylinder::new(half_height, radius)))
} }
/// Initialize a rounded cylindrical shape defined by its half-height pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
/// (along along the y axis), its radius, and its roundedness (the self.bits = (self.bits & !0b0000_0110) | ((rule as u8) << 1);
/// radius of the sphere used for dilating the cylinder). self
#[cfg(feature = "dim3")]
pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cylinder::new(half_height, radius),
border_radius,
}))
} }
/// Initialize a rounded cone shape defined by its half-height pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
/// (along along the y axis), its radius, and its roundedness (the self.bits = (self.bits & !0b0001_1000) | ((rule as u8) << 3);
/// radius of the sphere used for dilating the cylinder). self
#[cfg(feature = "dim3")]
pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cone::new(half_height, radius),
border_radius,
}))
}
/// Initialize a cone shape defined by its half-height
/// (along along the y axis) and its basis radius.
#[cfg(feature = "dim3")]
pub fn cone(half_height: Real, radius: Real) -> Self {
ColliderShape(Arc::new(Cone::new(half_height, radius)))
}
/// Initialize a cuboid shape defined by its half-extents.
#[cfg(feature = "dim2")]
pub fn cuboid(hx: Real, hy: Real) -> Self {
ColliderShape(Arc::new(Cuboid::new(Vector::new(hx, hy))))
}
/// Initialize a round cuboid shape defined by its half-extents and border radius.
#[cfg(feature = "dim2")]
pub fn round_cuboid(hx: Real, hy: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cuboid::new(Vector::new(hx, hy)),
border_radius,
}))
}
/// Initialize a cuboid shape defined by its half-extents.
#[cfg(feature = "dim3")]
pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
ColliderShape(Arc::new(Cuboid::new(Vector::new(hx, hy, hz))))
}
/// Initialize a round cuboid shape defined by its half-extents and border radius.
#[cfg(feature = "dim3")]
pub fn round_cuboid(hx: Real, hy: Real, hz: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cuboid::new(Vector::new(hx, hy, hz)),
border_radius,
}))
}
/// Initialize a capsule shape from its endpoints and radius.
pub fn capsule(a: Point<Real>, b: Point<Real>, radius: Real) -> Self {
ColliderShape(Arc::new(Capsule::new(a, b, radius)))
}
/// Initialize a segment shape from its endpoints.
pub fn segment(a: Point<Real>, b: Point<Real>) -> Self {
ColliderShape(Arc::new(Segment::new(a, b)))
}
/// Initializes a triangle shape.
pub fn triangle(a: Point<Real>, b: Point<Real>, c: Point<Real>) -> Self {
ColliderShape(Arc::new(Triangle::new(a, b, c)))
}
/// Initializes a triangle mesh shape defined by its vertex and index buffers.
pub fn trimesh(vertices: Vec<Point<Real>>, indices: Vec<[u32; 3]>) -> Self {
ColliderShape(Arc::new(TriMesh::new(vertices, indices)))
}
pub fn convex_hull(points: &[Point<Real>]) -> Option<Self> {
#[cfg(feature = "dim2")]
return ConvexPolygon::from_convex_hull(points).map(|ch| ColliderShape(Arc::new(ch)));
#[cfg(feature = "dim3")]
return ConvexPolyhedron::from_convex_hull(points).map(|ch| ColliderShape(Arc::new(ch)));
}
#[cfg(feature = "dim2")]
pub fn convex_polyline(points: Vec<Point<Real>>) -> Option<Self> {
ConvexPolygon::from_convex_polyline(points).map(|ch| ColliderShape(Arc::new(ch)))
}
#[cfg(feature = "dim3")]
pub fn convex_mesh(points: Vec<Point<Real>>, indices: &[[u32; 3]]) -> Option<Self> {
ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| ColliderShape(Arc::new(ch)))
}
pub fn round_convex_hull(points: &[Point<Real>], border_radius: Real) -> Option<Self> {
#[cfg(feature = "dim2")]
return ConvexPolygon::from_convex_hull(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
});
#[cfg(feature = "dim3")]
return ConvexPolyhedron::from_convex_hull(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
});
}
#[cfg(feature = "dim2")]
pub fn round_convex_polyline(points: Vec<Point<Real>>, border_radius: Real) -> Option<Self> {
ConvexPolygon::from_convex_polyline(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
})
}
#[cfg(feature = "dim3")]
pub fn round_convex_mesh(
points: Vec<Point<Real>>,
indices: &[[u32; 3]],
border_radius: Real,
) -> Option<Self> {
ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
})
}
/// Initializes an heightfield shape defined by its set of height and a scale
/// factor along each coordinate axis.
#[cfg(feature = "dim2")]
pub fn heightfield(heights: na::DVector<Real>, scale: Vector<Real>) -> Self {
ColliderShape(Arc::new(HeightField::new(heights, scale)))
}
/// Initializes an heightfield shape on the x-z plane defined by its set of height and a scale
/// factor along each coordinate axis.
#[cfg(feature = "dim3")]
pub fn heightfield(heights: na::DMatrix<Real>, scale: Vector<Real>) -> Self {
ColliderShape(Arc::new(HeightField::new(heights, scale)))
}
}
#[cfg(feature = "serde-serialize")]
impl serde::Serialize for ColliderShape {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use crate::serde::ser::SerializeStruct;
if let Some(ser) = self.0.as_serialize() {
let typ = self.0.shape_type();
let mut state = serializer.serialize_struct("ColliderShape", 2)?;
state.serialize_field("tag", &(typ as i32))?;
state.serialize_field("inner", ser)?;
state.end()
} else {
Err(serde::ser::Error::custom(
"Found a non-serializable custom shape.",
))
}
}
}
#[cfg(feature = "serde-serialize")]
impl<'de> serde::Deserialize<'de> for ColliderShape {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor {};
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = ColliderShape;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "one shape type tag and the inner shape data")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
use num::cast::FromPrimitive;
let tag: i32 = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
fn deser<'de, A, S: Shape + serde::Deserialize<'de>>(
seq: &mut A,
) -> Result<Arc<dyn Shape>, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let shape: S = seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom("Failed to deserialize builtin shape.")
})?;
Ok(Arc::new(shape) as Arc<dyn Shape>)
}
let shape = match ShapeType::from_i32(tag) {
Some(ShapeType::Ball) => deser::<A, Ball>(&mut seq)?,
Some(ShapeType::Cuboid) => deser::<A, Cuboid>(&mut seq)?,
Some(ShapeType::Capsule) => deser::<A, Capsule>(&mut seq)?,
Some(ShapeType::Triangle) => deser::<A, Triangle>(&mut seq)?,
Some(ShapeType::Segment) => deser::<A, Segment>(&mut seq)?,
Some(ShapeType::TriMesh) => deser::<A, TriMesh>(&mut seq)?,
Some(ShapeType::HeightField) => deser::<A, HeightField>(&mut seq)?,
Some(ShapeType::HalfSpace) => deser::<A, HalfSpace>(&mut seq)?,
Some(ShapeType::RoundCuboid) => deser::<A, RoundCuboid>(&mut seq)?,
Some(ShapeType::RoundTriangle) => deser::<A, RoundTriangle>(&mut seq)?,
#[cfg(feature = "dim2")]
Some(ShapeType::ConvexPolygon) => deser::<A, ConvexPolygon>(&mut seq)?,
#[cfg(feature = "dim2")]
Some(ShapeType::RoundConvexPolygon) => {
deser::<A, RoundConvexPolygon>(&mut seq)?
}
#[cfg(feature = "dim3")]
Some(ShapeType::Cylinder) => deser::<A, Cylinder>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::ConvexPolyhedron) => deser::<A, ConvexPolyhedron>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::Cone) => deser::<A, Cone>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundCylinder) => deser::<A, RoundCylinder>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundCone) => deser::<A, RoundCone>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundConvexPolyhedron) => {
deser::<A, RoundConvexPolyhedron>(&mut seq)?
}
Some(ShapeType::Compound) => {
return Err(serde::de::Error::custom(
"found invalid shape type to deserialize",
))
}
None => {
return Err(serde::de::Error::custom(
"found invalid shape type to deserialize",
))
}
};
Ok(ColliderShape(shape))
}
}
deserializer.deserialize_struct("ColliderShape", &["tag", "inner"], Visitor {})
} }
} }
@@ -315,7 +50,7 @@ impl<'de> serde::Deserialize<'de> for ColliderShape {
pub struct Collider { pub struct Collider {
shape: ColliderShape, shape: ColliderShape,
density: Real, density: Real,
is_sensor: bool, pub(crate) flags: ColliderFlags,
pub(crate) parent: RigidBodyHandle, pub(crate) parent: RigidBodyHandle,
pub(crate) delta: Isometry<Real>, pub(crate) delta: Isometry<Real>,
pub(crate) position: Isometry<Real>, pub(crate) position: Isometry<Real>,
@@ -344,7 +79,7 @@ impl Collider {
/// Is this collider a sensor? /// Is this collider a sensor?
pub fn is_sensor(&self) -> bool { pub fn is_sensor(&self) -> bool {
self.is_sensor self.flags.is_sensor()
} }
#[doc(hidden)] #[doc(hidden)]
@@ -415,8 +150,12 @@ pub struct ColliderBuilder {
density: Option<Real>, density: Option<Real>,
/// The friction coefficient of the collider to be built. /// The friction coefficient of the collider to be built.
pub friction: Real, pub friction: Real,
/// The rule used to combine two friction coefficients.
pub friction_combine_rule: CoefficientCombineRule,
/// The restitution coefficient of the collider to be built. /// The restitution coefficient of the collider to be built.
pub restitution: Real, pub restitution: Real,
/// The rule used to combine two restitution coefficients.
pub restitution_combine_rule: CoefficientCombineRule,
/// The position of this collider relative to the local frame of the rigid-body it is attached to. /// The position of this collider relative to the local frame of the rigid-body it is attached to.
pub delta: Isometry<Real>, pub delta: Isometry<Real>,
/// Is this collider a sensor? /// Is this collider a sensor?
@@ -442,6 +181,8 @@ impl ColliderBuilder {
user_data: 0, user_data: 0,
collision_groups: InteractionGroups::all(), collision_groups: InteractionGroups::all(),
solver_groups: InteractionGroups::all(), solver_groups: InteractionGroups::all(),
friction_combine_rule: CoefficientCombineRule::Average,
restitution_combine_rule: CoefficientCombineRule::Average,
} }
} }
@@ -645,12 +386,24 @@ impl ColliderBuilder {
self self
} }
/// Sets the rule to be used to combine two friction coefficients in a contact.
pub fn friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
self.friction_combine_rule = rule;
self
}
/// Sets the restitution coefficient of the collider this builder will build. /// Sets the restitution coefficient of the collider this builder will build.
pub fn restitution(mut self, restitution: Real) -> Self { pub fn restitution(mut self, restitution: Real) -> Self {
self.restitution = restitution; self.restitution = restitution;
self self
} }
/// Sets the rule to be used to combine two restitution coefficients in a contact.
pub fn restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
self.restitution_combine_rule = rule;
self
}
/// Sets the density of the collider this builder will build. /// Sets the density of the collider this builder will build.
pub fn density(mut self, density: Real) -> Self { pub fn density(mut self, density: Real) -> Self {
self.density = Some(density); self.density = Some(density);
@@ -700,6 +453,11 @@ impl ColliderBuilder {
/// Builds a new collider attached to the given rigid-body. /// Builds a new collider attached to the given rigid-body.
pub fn build(&self) -> Collider { pub fn build(&self) -> Collider {
let density = self.get_density(); let density = self.get_density();
let mut flags = ColliderFlags::empty();
flags.set(ColliderFlags::SENSOR, self.is_sensor);
flags = flags
.with_friction_combine_rule(self.friction_combine_rule)
.with_restitution_combine_rule(self.restitution_combine_rule);
Collider { Collider {
shape: self.shape.clone(), shape: self.shape.clone(),
@@ -707,7 +465,7 @@ impl ColliderBuilder {
friction: self.friction, friction: self.friction,
restitution: self.restitution, restitution: self.restitution,
delta: self.delta, delta: self.delta,
is_sensor: self.is_sensor, flags,
parent: RigidBodyHandle::invalid(), parent: RigidBodyHandle::invalid(),
position: Isometry::identity(), position: Isometry::identity(),
predicted_position: Isometry::identity(), predicted_position: Isometry::identity(),

View File

@@ -0,0 +1,304 @@
use crate::math::{Isometry, Point, Real, Vector};
use cdl::shape::{
Ball, Capsule, Compound, Cuboid, HalfSpace, HeightField, RoundCuboid, RoundShape,
RoundTriangle, Segment, Shape, ShapeType, TriMesh, Triangle,
};
#[cfg(feature = "dim3")]
use cdl::shape::{
Cone, ConvexPolyhedron, Cylinder, RoundCone, RoundConvexPolyhedron, RoundCylinder,
};
#[cfg(feature = "dim2")]
use cdl::shape::{ConvexPolygon, RoundConvexPolygon};
use std::ops::Deref;
use std::sync::Arc;
/// The shape of a collider.
#[derive(Clone)]
pub struct ColliderShape(pub Arc<dyn Shape>);
impl Deref for ColliderShape {
type Target = dyn Shape;
fn deref(&self) -> &dyn Shape {
&*self.0
}
}
impl ColliderShape {
/// Initialize a compound shape defined by its subshapes.
pub fn compound(shapes: Vec<(Isometry<Real>, ColliderShape)>) -> Self {
let raw_shapes = shapes.into_iter().map(|s| (s.0, s.1 .0)).collect();
let compound = Compound::new(raw_shapes);
ColliderShape(Arc::new(compound))
}
/// Initialize a ball shape defined by its radius.
pub fn ball(radius: Real) -> Self {
ColliderShape(Arc::new(Ball::new(radius)))
}
/// Initialize a cylindrical shape defined by its half-height
/// (along along the y axis) and its radius.
#[cfg(feature = "dim3")]
pub fn cylinder(half_height: Real, radius: Real) -> Self {
ColliderShape(Arc::new(Cylinder::new(half_height, radius)))
}
/// Initialize a rounded cylindrical shape defined by its half-height
/// (along along the y axis), its radius, and its roundedness (the
/// radius of the sphere used for dilating the cylinder).
#[cfg(feature = "dim3")]
pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cylinder::new(half_height, radius),
border_radius,
}))
}
/// Initialize a rounded cone shape defined by its half-height
/// (along along the y axis), its radius, and its roundedness (the
/// radius of the sphere used for dilating the cylinder).
#[cfg(feature = "dim3")]
pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cone::new(half_height, radius),
border_radius,
}))
}
/// Initialize a cone shape defined by its half-height
/// (along along the y axis) and its basis radius.
#[cfg(feature = "dim3")]
pub fn cone(half_height: Real, radius: Real) -> Self {
ColliderShape(Arc::new(Cone::new(half_height, radius)))
}
/// Initialize a cuboid shape defined by its half-extents.
#[cfg(feature = "dim2")]
pub fn cuboid(hx: Real, hy: Real) -> Self {
ColliderShape(Arc::new(Cuboid::new(Vector::new(hx, hy))))
}
/// Initialize a round cuboid shape defined by its half-extents and border radius.
#[cfg(feature = "dim2")]
pub fn round_cuboid(hx: Real, hy: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cuboid::new(Vector::new(hx, hy)),
border_radius,
}))
}
/// Initialize a cuboid shape defined by its half-extents.
#[cfg(feature = "dim3")]
pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
ColliderShape(Arc::new(Cuboid::new(Vector::new(hx, hy, hz))))
}
/// Initialize a round cuboid shape defined by its half-extents and border radius.
#[cfg(feature = "dim3")]
pub fn round_cuboid(hx: Real, hy: Real, hz: Real, border_radius: Real) -> Self {
ColliderShape(Arc::new(RoundShape {
base_shape: Cuboid::new(Vector::new(hx, hy, hz)),
border_radius,
}))
}
/// Initialize a capsule shape from its endpoints and radius.
pub fn capsule(a: Point<Real>, b: Point<Real>, radius: Real) -> Self {
ColliderShape(Arc::new(Capsule::new(a, b, radius)))
}
/// Initialize a segment shape from its endpoints.
pub fn segment(a: Point<Real>, b: Point<Real>) -> Self {
ColliderShape(Arc::new(Segment::new(a, b)))
}
/// Initializes a triangle shape.
pub fn triangle(a: Point<Real>, b: Point<Real>, c: Point<Real>) -> Self {
ColliderShape(Arc::new(Triangle::new(a, b, c)))
}
/// Initializes a triangle mesh shape defined by its vertex and index buffers.
pub fn trimesh(vertices: Vec<Point<Real>>, indices: Vec<[u32; 3]>) -> Self {
ColliderShape(Arc::new(TriMesh::new(vertices, indices)))
}
pub fn convex_hull(points: &[Point<Real>]) -> Option<Self> {
#[cfg(feature = "dim2")]
return ConvexPolygon::from_convex_hull(points).map(|ch| ColliderShape(Arc::new(ch)));
#[cfg(feature = "dim3")]
return ConvexPolyhedron::from_convex_hull(points).map(|ch| ColliderShape(Arc::new(ch)));
}
#[cfg(feature = "dim2")]
pub fn convex_polyline(points: Vec<Point<Real>>) -> Option<Self> {
ConvexPolygon::from_convex_polyline(points).map(|ch| ColliderShape(Arc::new(ch)))
}
#[cfg(feature = "dim3")]
pub fn convex_mesh(points: Vec<Point<Real>>, indices: &[[u32; 3]]) -> Option<Self> {
ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| ColliderShape(Arc::new(ch)))
}
pub fn round_convex_hull(points: &[Point<Real>], border_radius: Real) -> Option<Self> {
#[cfg(feature = "dim2")]
return ConvexPolygon::from_convex_hull(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
});
#[cfg(feature = "dim3")]
return ConvexPolyhedron::from_convex_hull(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
});
}
#[cfg(feature = "dim2")]
pub fn round_convex_polyline(points: Vec<Point<Real>>, border_radius: Real) -> Option<Self> {
ConvexPolygon::from_convex_polyline(points).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
})
}
#[cfg(feature = "dim3")]
pub fn round_convex_mesh(
points: Vec<Point<Real>>,
indices: &[[u32; 3]],
border_radius: Real,
) -> Option<Self> {
ConvexPolyhedron::from_convex_mesh(points, indices).map(|ch| {
ColliderShape(Arc::new(RoundShape {
base_shape: ch,
border_radius,
}))
})
}
/// Initializes an heightfield shape defined by its set of height and a scale
/// factor along each coordinate axis.
#[cfg(feature = "dim2")]
pub fn heightfield(heights: na::DVector<Real>, scale: Vector<Real>) -> Self {
ColliderShape(Arc::new(HeightField::new(heights, scale)))
}
/// Initializes an heightfield shape on the x-z plane defined by its set of height and a scale
/// factor along each coordinate axis.
#[cfg(feature = "dim3")]
pub fn heightfield(heights: na::DMatrix<Real>, scale: Vector<Real>) -> Self {
ColliderShape(Arc::new(HeightField::new(heights, scale)))
}
}
#[cfg(feature = "serde-serialize")]
impl serde::Serialize for ColliderShape {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use crate::serde::ser::SerializeStruct;
if let Some(ser) = self.0.as_serialize() {
let typ = self.0.shape_type();
let mut state = serializer.serialize_struct("ColliderShape", 2)?;
state.serialize_field("tag", &(typ as i32))?;
state.serialize_field("inner", ser)?;
state.end()
} else {
Err(serde::ser::Error::custom(
"Found a non-serializable custom shape.",
))
}
}
}
#[cfg(feature = "serde-serialize")]
impl<'de> serde::Deserialize<'de> for ColliderShape {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor {};
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = ColliderShape;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "one shape type tag and the inner shape data")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
use num::cast::FromPrimitive;
let tag: i32 = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
fn deser<'de, A, S: Shape + serde::Deserialize<'de>>(
seq: &mut A,
) -> Result<Arc<dyn Shape>, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let shape: S = seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom("Failed to deserialize builtin shape.")
})?;
Ok(Arc::new(shape) as Arc<dyn Shape>)
}
let shape = match ShapeType::from_i32(tag) {
Some(ShapeType::Ball) => deser::<A, Ball>(&mut seq)?,
Some(ShapeType::Cuboid) => deser::<A, Cuboid>(&mut seq)?,
Some(ShapeType::Capsule) => deser::<A, Capsule>(&mut seq)?,
Some(ShapeType::Triangle) => deser::<A, Triangle>(&mut seq)?,
Some(ShapeType::Segment) => deser::<A, Segment>(&mut seq)?,
Some(ShapeType::TriMesh) => deser::<A, TriMesh>(&mut seq)?,
Some(ShapeType::HeightField) => deser::<A, HeightField>(&mut seq)?,
Some(ShapeType::HalfSpace) => deser::<A, HalfSpace>(&mut seq)?,
Some(ShapeType::RoundCuboid) => deser::<A, RoundCuboid>(&mut seq)?,
Some(ShapeType::RoundTriangle) => deser::<A, RoundTriangle>(&mut seq)?,
#[cfg(feature = "dim2")]
Some(ShapeType::ConvexPolygon) => deser::<A, ConvexPolygon>(&mut seq)?,
#[cfg(feature = "dim2")]
Some(ShapeType::RoundConvexPolygon) => {
deser::<A, RoundConvexPolygon>(&mut seq)?
}
#[cfg(feature = "dim3")]
Some(ShapeType::Cylinder) => deser::<A, Cylinder>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::ConvexPolyhedron) => deser::<A, ConvexPolyhedron>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::Cone) => deser::<A, Cone>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundCylinder) => deser::<A, RoundCylinder>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundCone) => deser::<A, RoundCone>(&mut seq)?,
#[cfg(feature = "dim3")]
Some(ShapeType::RoundConvexPolyhedron) => {
deser::<A, RoundConvexPolyhedron>(&mut seq)?
}
Some(ShapeType::Compound) => {
return Err(serde::de::Error::custom(
"found invalid shape type to deserialize",
))
}
None => {
return Err(serde::de::Error::custom(
"found invalid shape type to deserialize",
))
}
};
Ok(ColliderShape(shape))
}
}
deserializer.deserialize_struct("ColliderShape", &["tag", "inner"], Visitor {})
}
}

View File

@@ -1,13 +1,15 @@
//! Structures related to geometry: colliders, shapes, etc. //! Structures related to geometry: colliders, shapes, etc.
pub use self::broad_phase_multi_sap::BroadPhase; pub use self::broad_phase_multi_sap::BroadPhase;
pub use self::collider::{Collider, ColliderBuilder, ColliderShape}; pub use self::collider::{Collider, ColliderBuilder};
pub use self::collider_set::{ColliderHandle, ColliderSet}; pub use self::collider_set::{ColliderHandle, ColliderSet};
pub use self::collider_shape::ColliderShape;
pub use self::contact_pair::{ContactData, ContactManifoldData}; pub use self::contact_pair::{ContactData, ContactManifoldData};
pub use self::contact_pair::{ContactPair, SolverContact, SolverFlags}; pub use self::contact_pair::{ContactPair, SolverContact, SolverFlags};
pub use self::interaction_graph::{ pub use self::interaction_graph::{
ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex, ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex,
}; };
pub use self::interaction_groups::InteractionGroups;
pub use self::narrow_phase::NarrowPhase; pub use self::narrow_phase::NarrowPhase;
pub use self::pair_filter::{ContactPairFilter, PairFilterContext, ProximityPairFilter}; pub use self::pair_filter::{ContactPairFilter, PairFilterContext, ProximityPairFilter};
@@ -81,7 +83,6 @@ impl IntersectionEvent {
pub(crate) use self::broad_phase_multi_sap::{BroadPhasePairEvent, ColliderPair}; pub(crate) use self::broad_phase_multi_sap::{BroadPhasePairEvent, ColliderPair};
pub(crate) use self::collider_set::RemovedCollider; pub(crate) use self::collider_set::RemovedCollider;
pub use self::interaction_groups::InteractionGroups;
pub(crate) use self::narrow_phase::ContactManifoldIndex; pub(crate) use self::narrow_phase::ContactManifoldIndex;
pub(crate) use cdl::partitioning::SimdQuadTree; pub(crate) use cdl::partitioning::SimdQuadTree;
pub use cdl::shape::*; pub use cdl::shape::*;
@@ -98,6 +99,7 @@ pub(crate) fn default_query_dispatcher() -> std::sync::Arc<dyn cdl::query::Query
mod broad_phase_multi_sap; mod broad_phase_multi_sap;
mod collider; mod collider;
mod collider_set; mod collider_set;
mod collider_shape;
mod contact_pair; mod contact_pair;
mod interaction_graph; mod interaction_graph;
mod interaction_groups; mod interaction_groups;

View File

@@ -3,7 +3,7 @@ use rayon::prelude::*;
use crate::data::pubsub::Subscription; use crate::data::pubsub::Subscription;
use crate::data::Coarena; use crate::data::Coarena;
use crate::dynamics::{BodyPair, RigidBodySet}; use crate::dynamics::{BodyPair, CoefficientCombineRule, RigidBodySet};
use crate::geometry::{ use crate::geometry::{
BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactData, ContactEvent, BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactData, ContactEvent,
ContactManifoldData, ContactPairFilter, IntersectionEvent, PairFilterContext, ContactManifoldData, ContactPairFilter, IntersectionEvent, PairFilterContext,
@@ -522,6 +522,19 @@ impl NarrowPhase {
let mut has_any_active_contact = false; let mut has_any_active_contact = false;
let friction = CoefficientCombineRule::combine(
co1.friction,
co2.friction,
co1.flags.friction_combine_rule_value(),
co2.flags.friction_combine_rule_value(),
);
let restitution = CoefficientCombineRule::combine(
co1.restitution,
co2.restitution,
co1.flags.restitution_combine_rule_value(),
co2.flags.restitution_combine_rule_value(),
);
for manifold in &mut pair.manifolds { for manifold in &mut pair.manifolds {
let world_pos1 = manifold.subshape_pos1.prepend_to(co1.position()); let world_pos1 = manifold.subshape_pos1.prepend_to(co1.position());
manifold.data.solver_contacts.clear(); manifold.data.solver_contacts.clear();
@@ -541,8 +554,8 @@ impl NarrowPhase {
point: world_pos1 * contact.local_p1 point: world_pos1 * contact.local_p1
+ manifold.data.normal * contact.dist / 2.0, + manifold.data.normal * contact.dist / 2.0,
dist: contact.dist, dist: contact.dist,
friction: (co1.friction + co2.friction) / 2.0, friction,
restitution: (co1.restitution + co2.restitution) / 2.0, restitution,
surface_velocity: Vector::zeros(), surface_velocity: Vector::zeros(),
data: contact.data, data: contact.data,
}; };