aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCrozet Sébastien <developer@crozet.re>2021-01-21 16:03:27 +0100
committerCrozet Sébastien <developer@crozet.re>2021-01-21 16:03:27 +0100
commit98d3980db7a9803f4ee965237599a87771a417d1 (patch)
treeb46aeef3ef6f703d0e938b5e1b778aa0f73a4680
parent8f330b2a00610e5b68c1acd9208120e8f750c7aa (diff)
downloadrapier-98d3980db7a9803f4ee965237599a87771a417d1.tar.gz
rapier-98d3980db7a9803f4ee965237599a87771a417d1.tar.bz2
rapier-98d3980db7a9803f4ee965237599a87771a417d1.zip
Allow several rules for combining friction/restitution coefficients.
-rw-r--r--src/dynamics/coefficient_combine_rule.rs34
-rw-r--r--src/dynamics/mod.rs2
-rw-r--r--src/dynamics/rigid_body.rs4
-rw-r--r--src/geometry/collider.rs344
-rw-r--r--src/geometry/collider_shape.rs304
-rw-r--r--src/geometry/mod.rs6
-rw-r--r--src/geometry/narrow_phase.rs19
7 files changed, 414 insertions, 299 deletions
diff --git a/src/dynamics/coefficient_combine_rule.rs b/src/dynamics/coefficient_combine_rule.rs
new file mode 100644
index 0000000..5e8b4a0
--- /dev/null
+++ b/src/dynamics/coefficient_combine_rule.rs
@@ -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),
+ }
+ }
+}
diff --git a/src/dynamics/mod.rs b/src/dynamics/mod.rs
index d7ce0c1..0bc0b05 100644
--- a/src/dynamics/mod.rs
+++ b/src/dynamics/mod.rs
@@ -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;
diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs
index 9279e4f..85fdec9 100644
--- a/src/dynamics/rigid_body.rs
+++ b/src/dynamics/rigid_body.rs
@@ -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>,
diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs
index a4de108..c5f53f7 100644
--- a/src/geometry/collider.rs
+++ b/src/geometry/collider.rs
@@ -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))
- }
-
- /// 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,
- }))
- })
+impl ColliderFlags {
+ pub fn is_sensor(self) -> bool {
+ self.contains(ColliderFlags::SENSOR)
}
- /// 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)))
+ pub fn friction_combine_rule_value(self) -> u8 {
+ (self.bits & 0b0000_0110) >> 1
}
- /// 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)))
+ pub fn restitution_combine_rule_value(self) -> u8 {
+ (self.bits & 0b0001_1000) >> 3
}
-}
-#[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.",
- ))
- }
+ pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
+ self.bits = (self.bits & !0b0000_0110) | ((rule as u8) << 1);
+ self
}
-}
-#[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(),
diff --git a/src/geometry/collider_shape.rs b/src/geometry/collider_shape.rs
new file mode 100644
index 0000000..4777850
--- /dev/null
+++ b/src/geometry/collider_shape.rs
@@ -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 {})
+ }
+}
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index ac7e63b..75fbb75 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -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;
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
index 621a3b4..8a9e578 100644
--- a/src/geometry/narrow_phase.rs
+++ b/src/geometry/narrow_phase.rs
@@ -3,7 +3,7 @@ use rayon::prelude::*;
use crate::data::pubsub::Subscription;
use crate::data::Coarena;