Allow several rules for combining friction/restitution coefficients.
This commit is contained in:
34
src/dynamics/coefficient_combine_rule.rs
Normal file
34
src/dynamics/coefficient_combine_rule.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub use self::rigid_body::{ActivationStatus, BodyStatus, RigidBody, RigidBodyBui
|
||||
pub use self::rigid_body_set::{BodyPair, RigidBodyHandle, RigidBodySet};
|
||||
pub use cdl::mass_properties::MassProperties;
|
||||
// #[cfg(not(feature = "parallel"))]
|
||||
pub use self::coefficient_combine_rule::CoefficientCombineRule;
|
||||
pub(crate) use self::joint::JointGraphEdge;
|
||||
pub(crate) use self::rigid_body::RigidBodyChanges;
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
@@ -18,6 +19,7 @@ pub(crate) use self::solver::IslandSolver;
|
||||
#[cfg(feature = "parallel")]
|
||||
pub(crate) use self::solver::ParallelIslandSolver;
|
||||
|
||||
mod coefficient_combine_rule;
|
||||
mod integration_parameters;
|
||||
mod joint;
|
||||
mod rigid_body;
|
||||
|
||||
@@ -62,8 +62,10 @@ pub struct RigidBody {
|
||||
pub(crate) mass_properties: MassProperties,
|
||||
/// The world-space center of mass of the rigid-body.
|
||||
pub world_com: Point<Real>,
|
||||
/// The inverse mass taking into account translation locking.
|
||||
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>,
|
||||
/// The linear velocity of the rigid-body.
|
||||
pub(crate) linvel: Vector<Real>,
|
||||
|
||||
@@ -1,309 +1,44 @@
|
||||
use crate::dynamics::{MassProperties, RigidBodyHandle};
|
||||
use crate::geometry::InteractionGroups;
|
||||
use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle};
|
||||
use crate::geometry::{ColliderShape, InteractionGroups};
|
||||
use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector};
|
||||
use cdl::bounding_volume::AABB;
|
||||
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,
|
||||
};
|
||||
use cdl::shape::Shape;
|
||||
#[cfg(feature = "dim2")]
|
||||
use cdl::shape::{ConvexPolygon, RoundConvexPolygon};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
// TODO: move this to its own file.
|
||||
/// 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
|
||||
bitflags::bitflags! {
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
/// Flags affecting the behavior of the constraints solver for a given contact manifold.
|
||||
pub(crate) struct ColliderFlags: u8 {
|
||||
const SENSOR = 1 << 0;
|
||||
const FRICTION_COMBINE_RULE_01 = 1 << 1;
|
||||
const FRICTION_COMBINE_RULE_10 = 1 << 2;
|
||||
const RESTITUTION_COMBINE_RULE_01 = 1 << 3;
|
||||
const RESTITUTION_COMBINE_RULE_10 = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
impl ColliderFlags {
|
||||
pub fn is_sensor(self) -> bool {
|
||||
self.contains(ColliderFlags::SENSOR)
|
||||
}
|
||||
|
||||
/// Initialize a ball shape defined by its radius.
|
||||
pub fn ball(radius: Real) -> Self {
|
||||
ColliderShape(Arc::new(Ball::new(radius)))
|
||||
pub fn friction_combine_rule_value(self) -> u8 {
|
||||
(self.bits & 0b0000_0110) >> 1
|
||||
}
|
||||
|
||||
/// 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)))
|
||||
pub fn restitution_combine_rule_value(self) -> u8 {
|
||||
(self.bits & 0b0001_1000) >> 3
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}))
|
||||
pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
|
||||
self.bits = (self.bits & !0b0000_0110) | ((rule as u8) << 1);
|
||||
self
|
||||
}
|
||||
|
||||
/// 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 {})
|
||||
pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
|
||||
self.bits = (self.bits & !0b0001_1000) | ((rule as u8) << 3);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +50,7 @@ impl<'de> serde::Deserialize<'de> for ColliderShape {
|
||||
pub struct Collider {
|
||||
shape: ColliderShape,
|
||||
density: Real,
|
||||
is_sensor: bool,
|
||||
pub(crate) flags: ColliderFlags,
|
||||
pub(crate) parent: RigidBodyHandle,
|
||||
pub(crate) delta: Isometry<Real>,
|
||||
pub(crate) position: Isometry<Real>,
|
||||
@@ -344,7 +79,7 @@ impl Collider {
|
||||
|
||||
/// Is this collider a sensor?
|
||||
pub fn is_sensor(&self) -> bool {
|
||||
self.is_sensor
|
||||
self.flags.is_sensor()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -415,8 +150,12 @@ pub struct ColliderBuilder {
|
||||
density: Option<Real>,
|
||||
/// The friction coefficient of the collider to be built.
|
||||
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.
|
||||
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.
|
||||
pub delta: Isometry<Real>,
|
||||
/// Is this collider a sensor?
|
||||
@@ -442,6 +181,8 @@ impl ColliderBuilder {
|
||||
user_data: 0,
|
||||
collision_groups: InteractionGroups::all(),
|
||||
solver_groups: InteractionGroups::all(),
|
||||
friction_combine_rule: CoefficientCombineRule::Average,
|
||||
restitution_combine_rule: CoefficientCombineRule::Average,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,12 +386,24 @@ impl ColliderBuilder {
|
||||
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.
|
||||
pub fn restitution(mut self, restitution: Real) -> Self {
|
||||
self.restitution = restitution;
|
||||
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.
|
||||
pub fn density(mut self, density: Real) -> Self {
|
||||
self.density = Some(density);
|
||||
@@ -700,6 +453,11 @@ impl ColliderBuilder {
|
||||
/// Builds a new collider attached to the given rigid-body.
|
||||
pub fn build(&self) -> Collider {
|
||||
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 {
|
||||
shape: self.shape.clone(),
|
||||
@@ -707,7 +465,7 @@ impl ColliderBuilder {
|
||||
friction: self.friction,
|
||||
restitution: self.restitution,
|
||||
delta: self.delta,
|
||||
is_sensor: self.is_sensor,
|
||||
flags,
|
||||
parent: RigidBodyHandle::invalid(),
|
||||
position: Isometry::identity(),
|
||||
predicted_position: Isometry::identity(),
|
||||
|
||||
304
src/geometry/collider_shape.rs
Normal file
304
src/geometry/collider_shape.rs
Normal 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 {})
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
//! Structures related to geometry: colliders, shapes, etc.
|
||||
|
||||
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_shape::ColliderShape;
|
||||
pub use self::contact_pair::{ContactData, ContactManifoldData};
|
||||
pub use self::contact_pair::{ContactPair, SolverContact, SolverFlags};
|
||||
pub use self::interaction_graph::{
|
||||
ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex,
|
||||
};
|
||||
pub use self::interaction_groups::InteractionGroups;
|
||||
pub use self::narrow_phase::NarrowPhase;
|
||||
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::collider_set::RemovedCollider;
|
||||
pub use self::interaction_groups::InteractionGroups;
|
||||
pub(crate) use self::narrow_phase::ContactManifoldIndex;
|
||||
pub(crate) use cdl::partitioning::SimdQuadTree;
|
||||
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 collider;
|
||||
mod collider_set;
|
||||
mod collider_shape;
|
||||
mod contact_pair;
|
||||
mod interaction_graph;
|
||||
mod interaction_groups;
|
||||
|
||||
@@ -3,7 +3,7 @@ use rayon::prelude::*;
|
||||
|
||||
use crate::data::pubsub::Subscription;
|
||||
use crate::data::Coarena;
|
||||
use crate::dynamics::{BodyPair, RigidBodySet};
|
||||
use crate::dynamics::{BodyPair, CoefficientCombineRule, RigidBodySet};
|
||||
use crate::geometry::{
|
||||
BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactData, ContactEvent,
|
||||
ContactManifoldData, ContactPairFilter, IntersectionEvent, PairFilterContext,
|
||||
@@ -522,6 +522,19 @@ impl NarrowPhase {
|
||||
|
||||
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 {
|
||||
let world_pos1 = manifold.subshape_pos1.prepend_to(co1.position());
|
||||
manifold.data.solver_contacts.clear();
|
||||
@@ -541,8 +554,8 @@ impl NarrowPhase {
|
||||
point: world_pos1 * contact.local_p1
|
||||
+ manifold.data.normal * contact.dist / 2.0,
|
||||
dist: contact.dist,
|
||||
friction: (co1.friction + co2.friction) / 2.0,
|
||||
restitution: (co1.restitution + co2.restitution) / 2.0,
|
||||
friction,
|
||||
restitution,
|
||||
surface_velocity: Vector::zeros(),
|
||||
data: contact.data,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user