diff options
| author | Crozet Sébastien <developer@crozet.re> | 2021-01-21 16:03:27 +0100 |
|---|---|---|
| committer | Crozet Sébastien <developer@crozet.re> | 2021-01-21 16:03:27 +0100 |
| commit | 98d3980db7a9803f4ee965237599a87771a417d1 (patch) | |
| tree | b46aeef3ef6f703d0e938b5e1b778aa0f73a4680 | |
| parent | 8f330b2a00610e5b68c1acd9208120e8f750c7aa (diff) | |
| download | rapier-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.rs | 34 | ||||
| -rw-r--r-- | src/dynamics/mod.rs | 2 | ||||
| -rw-r--r-- | src/dynamics/rigid_body.rs | 4 | ||||
| -rw-r--r-- | src/geometry/collider.rs | 344 | ||||
| -rw-r--r-- | src/geometry/collider_shape.rs | 304 | ||||
| -rw-r--r-- | src/geometry/mod.rs | 6 | ||||
| -rw-r--r-- | src/geometry/narrow_phase.rs | 19 |
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; |
