aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Hrusecky <robert.hrusecky@utexas.edu>2020-11-02 15:00:12 -0600
committerRobert Hrusecky <robert.hrusecky@utexas.edu>2020-11-02 15:00:12 -0600
commit8b81a3fd2727dcb911f19c3d7a98c4ec94c6f2fa (patch)
treeb59ce89ff5d1e0505cdf2b7b91740a48763efa76 /src
parentbcec54ef31d987cf20b493628a20777183a95f65 (diff)
parenta38fdc101dc74473c45a8b4f5d770f2bc43f30c2 (diff)
downloadrapier-8b81a3fd2727dcb911f19c3d7a98c4ec94c6f2fa.tar.gz
rapier-8b81a3fd2727dcb911f19c3d7a98c4ec94c6f2fa.tar.bz2
rapier-8b81a3fd2727dcb911f19c3d7a98c4ec94c6f2fa.zip
Merge branch 'master' into infinite_fall_memory
Diffstat (limited to 'src')
-rw-r--r--src/data/hashmap.rs137
-rw-r--r--src/data/maybe_serializable_data.rs14
-rw-r--r--src/data/mod.rs4
-rw-r--r--src/dynamics/joint/joint_set.rs32
-rw-r--r--src/geometry/broad_phase_multi_sap.rs9
-rw-r--r--src/geometry/contact.rs15
-rw-r--r--src/geometry/contact_generator/contact_dispatcher.rs32
-rw-r--r--src/geometry/contact_generator/contact_generator.rs8
-rw-r--r--src/geometry/contact_generator/contact_generator_workspace.rs98
-rw-r--r--src/geometry/contact_generator/heightfield_shape_contact_generator.rs80
-rw-r--r--src/geometry/contact_generator/mod.rs5
-rw-r--r--src/geometry/contact_generator/pfm_pfm_contact_generator.rs20
-rw-r--r--src/geometry/contact_generator/serializable_workspace_tag.rs9
-rw-r--r--src/geometry/contact_generator/trimesh_shape_contact_generator.rs25
-rw-r--r--src/geometry/narrow_phase.rs6
-rw-r--r--src/geometry/polyhedron_feature3d.rs12
-rw-r--r--src/utils.rs128
17 files changed, 430 insertions, 204 deletions
diff --git a/src/data/hashmap.rs b/src/data/hashmap.rs
new file mode 100644
index 0000000..d2ea980
--- /dev/null
+++ b/src/data/hashmap.rs
@@ -0,0 +1,137 @@
+//! A hash-map that behaves deterministically when the
+//! `enhanced-determinism` feature is enabled.
+
+#[cfg(all(feature = "enhanced-determinism", feature = "serde-serialize"))]
+use indexmap::IndexMap as StdHashMap;
+#[cfg(all(not(feature = "enhanced-determinism"), feature = "serde-serialize"))]
+use std::collections::HashMap as StdHashMap;
+
+/// Serializes only the capacity of a hash-map instead of its actual content.
+#[cfg(feature = "serde-serialize")]
+pub fn serialize_hashmap_capacity<S: serde::Serializer, K, V, H: std::hash::BuildHasher>(
+ map: &StdHashMap<K, V, H>,
+ s: S,
+) -> Result<S::Ok, S::Error> {
+ s.serialize_u64(map.capacity() as u64)
+}
+
+/// Creates a new hash-map with its capacity deserialized from `d`.
+#[cfg(feature = "serde-serialize")]
+pub fn deserialize_hashmap_capacity<
+ 'de,
+ D: serde::Deserializer<'de>,
+ K,
+ V,
+ H: std::hash::BuildHasher + Default,
+>(
+ d: D,
+) -> Result<StdHashMap<K, V, H>, D::Error> {
+ struct CapacityVisitor;
+ impl<'de> serde::de::Visitor<'de> for CapacityVisitor {
+ type Value = u64;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(formatter, "an integer between 0 and 2^64")
+ }
+
+ fn visit_u64<E: serde::de::Error>(self, val: u64) -> Result<Self::Value, E> {
+ Ok(val)
+ }
+ }
+
+ let capacity = d.deserialize_u64(CapacityVisitor)? as usize;
+ Ok(StdHashMap::with_capacity_and_hasher(
+ capacity,
+ Default::default(),
+ ))
+}
+
+/*
+ * FxHasher taken from rustc_hash, except that it does not depend on the pointer size.
+ */
+#[cfg(feature = "enhanced-determinism")]
+pub type FxHashMap32<K, V> = indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<FxHasher32>>;
+#[cfg(feature = "enhanced-determinism")]
+pub use {self::FxHashMap32 as HashMap, indexmap::map::Entry};
+#[cfg(not(feature = "enhanced-determinism"))]
+pub use {rustc_hash::FxHashMap as HashMap, std::collections::hash_map::Entry};
+
+const K: u32 = 0x9e3779b9;
+
+// Same as FxHasher, but with the guarantee that the internal hash is
+// an u32 instead of something that depends on the platform.
+pub struct FxHasher32 {
+ hash: u32,
+}
+
+impl Default for FxHasher32 {
+ #[inline]
+ fn default() -> FxHasher32 {
+ FxHasher32 { hash: 0 }
+ }
+}
+
+impl FxHasher32 {
+ #[inline]
+ fn add_to_hash(&mut self, i: u32) {
+ use std::ops::BitXor;
+ self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K);
+ }
+}
+
+impl std::hash::Hasher for FxHasher32 {
+ #[inline]
+ fn write(&mut self, mut bytes: &[u8]) {
+ use std::convert::TryInto;
+ let read_u32 = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap());
+ let mut hash = FxHasher32 { hash: self.hash };
+ assert!(std::mem::size_of::<u32>() <= 8);
+ while bytes.len() >= std::mem::size_of::<u32>() {
+ hash.add_to_hash(read_u32(bytes) as u32);
+ bytes = &bytes[std::mem::size_of::<u32>()..];
+ }
+ if (std::mem::size_of::<u32>() > 4) && (bytes.len() >= 4) {
+ hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as u32);
+ bytes = &bytes[4..];
+ }
+ if (std::mem::size_of::<u32>() > 2) && bytes.len() >= 2 {
+ hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as u32);
+ bytes = &bytes[2..];
+ }
+ if (std::mem::size_of::<u32>() > 1) && bytes.len() >= 1 {
+ hash.add_to_hash(bytes[0] as u32);
+ }
+ self.hash = hash.hash;
+ }
+
+ #[inline]
+ fn write_u8(&mut self, i: u8) {
+ self.add_to_hash(i as u32);
+ }
+
+ #[inline]
+ fn write_u16(&mut self, i: u16) {
+ self.add_to_hash(i as u32);
+ }
+
+ #[inline]
+ fn write_u32(&mut self, i: u32) {
+ self.add_to_hash(i as u32);
+ }
+
+ #[inline]
+ fn write_u64(&mut self, i: u64) {
+ self.add_to_hash(i as u32);
+ self.add_to_hash((i >> 32) as u32);
+ }
+
+ #[inline]
+ fn write_usize(&mut self, i: usize) {
+ self.add_to_hash(i as u32);
+ }
+
+ #[inline]
+ fn finish(&self) -> u64 {
+ self.hash as u64
+ }
+}
diff --git a/src/data/maybe_serializable_data.rs b/src/data/maybe_serializable_data.rs
new file mode 100644
index 0000000..db38963
--- /dev/null
+++ b/src/data/maybe_serializable_data.rs
@@ -0,0 +1,14 @@
+use downcast_rs::{impl_downcast, DowncastSync};
+#[cfg(feature = "serde-serialize")]
+use erased_serde::Serialize;
+
+/// Piece of data that may be serializable.
+pub trait MaybeSerializableData: DowncastSync {
+ /// Convert this shape as a serializable entity.
+ #[cfg(feature = "serde-serialize")]
+ fn as_serialize(&self) -> Option<(u32, &dyn Serialize)> {
+ None
+ }
+}
+
+impl_downcast!(sync MaybeSerializableData);
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 5d3efa6..0e0459e 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -1,5 +1,9 @@
//! Data structures modified with guaranteed deterministic behavior after deserialization.
+pub use self::maybe_serializable_data::MaybeSerializableData;
+
pub mod arena;
pub(crate) mod graph;
+pub(crate) mod hashmap;
+mod maybe_serializable_data;
pub mod pubsub;
diff --git a/src/dynamics/joint/joint_set.rs b/src/dynamics/joint/joint_set.rs
index 2b1895f..5144d97 100644
--- a/src/dynamics/joint/joint_set.rs
+++ b/src/dynamics/joint/joint_set.rs
@@ -181,6 +181,38 @@ impl JointSet {
}
}
+ /// Removes a joint from this set.
+ ///
+ /// If `wake_up` is set to `true`, then the bodies attached to this joint will be
+ /// automatically woken up.
+ pub fn remove(
+ &mut self,
+ handle: JointHandle,
+ bodies: &mut RigidBodySet,
+ wake_up: bool,
+ ) -> Option<Joint> {
+ let id = self.joint_ids.remove(handle)?;
+ let endpoints = self.joint_graph.graph.edge_endpoints(id)?;
+
+ if wake_up {
+ // Wake-up the bodies attached to this joint.
+ if let Some(rb_handle) = self.joint_graph.graph.node_weight(endpoints.0) {
+ bodies.wake_up(*rb_handle, true);
+ }
+ if let Some(rb_handle) = self.joint_graph.graph.node_weight(endpoints.1) {
+ bodies.wake_up(*rb_handle, true);
+ }
+ }
+
+ let removed_joint = self.joint_graph.graph.remove_edge(id);
+
+ if let Some(edge) = self.joint_graph.graph.edge_weight(id) {
+ self.joint_ids[edge.handle] = id;
+ }
+
+ removed_joint
+ }
+
pub(crate) fn remove_rigid_body(
&mut self,
deleted_id: RigidBodyGraphIndex,
diff --git a/src/geometry/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap.rs
index d8a46ee..d76f026 100644
--- a/src/geometry/broad_phase_multi_sap.rs
+++ b/src/geometry/broad_phase_multi_sap.rs
@@ -1,13 +1,10 @@
+use crate::data::hashmap::HashMap;
use crate::data::pubsub::Subscription;
use crate::dynamics::RigidBodySet;
use crate::geometry::{ColliderHandle, ColliderSet, RemovedCollider};
use crate::math::{Point, Vector, DIM};
-#[cfg(feature = "enhanced-determinism")]
-use crate::utils::FxHashMap32 as HashMap;
use bit_vec::BitVec;
use ncollide::bounding_volume::{BoundingVolume, AABB};
-#[cfg(not(feature = "enhanced-determinism"))]
-use rustc_hash::FxHashMap as HashMap;
use std::cmp::Ordering;
use std::ops::{Index, IndexMut};
@@ -444,8 +441,8 @@ pub struct BroadPhase {
#[cfg_attr(
feature = "serde-serialize",
serde(
- serialize_with = "crate::utils::serialize_hashmap_capacity",
- deserialize_with = "crate::utils::deserialize_hashmap_capacity"
+ serialize_with = "crate::data::hashmap::serialize_hashmap_capacity",
+ deserialize_with = "crate::data::hashmap::deserialize_hashmap_capacity"
)
)]
reporting: HashMap<(u32, u32), bool>, // Workspace
diff --git a/src/geometry/contact.rs b/src/geometry/contact.rs
index d8f3632..53e9064 100644
--- a/src/geometry/contact.rs
+++ b/src/geometry/contact.rs
@@ -1,8 +1,8 @@
+use crate::data::MaybeSerializableData;
use crate::dynamics::BodyPair;
-use crate::geometry::contact_generator::ContactPhase;
+use crate::geometry::contact_generator::{ContactGeneratorWorkspace, ContactPhase};
use crate::geometry::{Collider, ColliderPair, ColliderSet};
use crate::math::{Isometry, Point, Vector};
-use std::any::Any;
#[cfg(feature = "simd-is-enabled")]
use {
crate::math::{SimdFloat, SIMD_WIDTH},
@@ -182,15 +182,14 @@ pub struct ContactPair {
pub manifolds: Vec<ContactManifold>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(crate) generator: Option<ContactPhase>,
- #[cfg_attr(feature = "serde-serialize", serde(skip))]
- pub(crate) generator_workspace: Option<Box<dyn Any + Send + Sync>>,
+ pub(crate) generator_workspace: Option<ContactGeneratorWorkspace>,
}
impl ContactPair {
pub(crate) fn new(
pair: ColliderPair,
generator: ContactPhase,
- generator_workspace: Option<Box<dyn Any + Send + Sync>>,
+ generator_workspace: Option<ContactGeneratorWorkspace>,
) -> Self {
Self {
pair,
@@ -221,7 +220,7 @@ impl ContactPair {
&'b Collider,
&'b Collider,
&'a mut ContactManifold,
- Option<&'a mut (dyn Any + Send + Sync)>,
+ Option<&'a mut (dyn MaybeSerializableData)>,
) {
let coll1 = &colliders[self.pair.collider1];
let coll2 = &colliders[self.pair.collider2];
@@ -240,14 +239,14 @@ impl ContactPair {
coll1,
coll2,
manifold,
- self.generator_workspace.as_mut().map(|w| &mut **w),
+ self.generator_workspace.as_mut().map(|w| &mut *w.0),
)
} else {
(
coll2,
coll1,
manifold,
- self.generator_workspace.as_mut().map(|w| &mut **w),
+ self.generator_workspace.as_mut().map(|w| &mut *w.0),
)
}
}
diff --git a/src/geometry/contact_generator/contact_dispatcher.rs b/src/geometry/contact_generator/contact_dispatcher.rs
index 1872c7b..7c32714 100644
--- a/src/geometry/contact_generator/contact_dispatcher.rs
+++ b/src/geometry/contact_generator/contact_dispatcher.rs
@@ -1,11 +1,11 @@
#[cfg(feature = "dim3")]
use crate::geometry::contact_generator::PfmPfmContactManifoldGeneratorWorkspace;
use crate::geometry::contact_generator::{
- ContactGenerator, ContactPhase, HeightFieldShapeContactGeneratorWorkspace,
- PrimitiveContactGenerator, TrimeshShapeContactGeneratorWorkspace,
+ ContactGenerator, ContactGeneratorWorkspace, ContactPhase,
+ HeightFieldShapeContactGeneratorWorkspace, PrimitiveContactGenerator,
+ TrimeshShapeContactGeneratorWorkspace,
};
use crate::geometry::ShapeType;
-use std::any::Any;
/// Trait implemented by structures responsible for selecting a collision-detection algorithm
/// for a given pair of shapes.
@@ -15,16 +15,13 @@ pub trait ContactDispatcher {
&self,
shape1: ShapeType,
shape2: ShapeType,
- ) -> (
- PrimitiveContactGenerator,
- Option<Box<dyn Any + Send + Sync>>,
- );
+ ) -> (PrimitiveContactGenerator, Option<ContactGeneratorWorkspace>);
/// Select the collision-detection algorithm for the given pair of non-primitive shapes.
fn dispatch(
&self,
shape1: ShapeType,
shape2: ShapeType,
- ) -> (ContactPhase, Option<Box<dyn Any + Send + Sync>>);
+ ) -> (ContactPhase, Option<ContactGeneratorWorkspace>);
}
/// The default contact dispatcher used by Rapier.
@@ -35,10 +32,7 @@ impl ContactDispatcher for DefaultContactDispatcher {
&self,
shape1: ShapeType,
shape2: ShapeType,
- ) -> (
- PrimitiveContactGenerator,
- Option<Box<dyn Any + Send + Sync>>,
- ) {
+ ) -> (PrimitiveContactGenerator, Option<ContactGeneratorWorkspace>) {
match (shape1, shape2) {
(ShapeType::Ball, ShapeType::Ball) => (
PrimitiveContactGenerator {
@@ -106,7 +100,9 @@ impl ContactDispatcher for DefaultContactDispatcher {
generate_contacts: super::generate_contacts_pfm_pfm,
..PrimitiveContactGenerator::default()
},
- Some(Box::new(PfmPfmContactManifoldGeneratorWorkspace::default())),
+ Some(ContactGeneratorWorkspace::from(
+ PfmPfmContactManifoldGeneratorWorkspace::default(),
+ )),
),
_ => (PrimitiveContactGenerator::default(), None),
}
@@ -116,21 +112,25 @@ impl ContactDispatcher for DefaultContactDispatcher {
&self,
shape1: ShapeType,
shape2: ShapeType,
- ) -> (ContactPhase, Option<Box<dyn Any + Send + Sync>>) {
+ ) -> (ContactPhase, Option<ContactGeneratorWorkspace>) {
match (shape1, shape2) {
(ShapeType::Trimesh, _) | (_, ShapeType::Trimesh) => (
ContactPhase::NearPhase(ContactGenerator {
generate_contacts: super::generate_contacts_trimesh_shape,
..ContactGenerator::default()
}),
- Some(Box::new(TrimeshShapeContactGeneratorWorkspace::new())),
+ Some(ContactGeneratorWorkspace::from(
+ TrimeshShapeContactGeneratorWorkspace::new(),
+ )),
),
(ShapeType::HeightField, _) | (_, ShapeType::HeightField) => (
ContactPhase::NearPhase(ContactGenerator {
generate_contacts: super::generate_contacts_heightfield_shape,
..ContactGenerator::default()
}),
- Some(Box::new(HeightFieldShapeContactGeneratorWorkspace::new())),
+ Some(ContactGeneratorWorkspace::from(
+ HeightFieldShapeContactGeneratorWorkspace::new(),
+ )),
),
_ => {
let (gen, workspace) = self.dispatch_primitives(shape1, shape2);
diff --git a/src/geometry/contact_generator/contact_generator.rs b/src/geometry/contact_generator/contact_generator.rs
index 728794d..b1b1be6 100644
--- a/src/geometry/contact_generator/contact_generator.rs
+++ b/src/geometry/contact_generator/contact_generator.rs
@@ -1,3 +1,4 @@
+use crate::data::MaybeSerializableData;
use crate::geometry::{
Collider, ColliderSet, ContactDispatcher, ContactEvent, ContactManifold, ContactPair, Shape,
SolverFlags,
@@ -6,7 +7,6 @@ use crate::math::Isometry;
#[cfg(feature = "simd-is-enabled")]
use crate::math::{SimdFloat, SIMD_WIDTH};
use crate::pipeline::EventHandler;
-use std::any::Any;
#[derive(Copy, Clone)]
pub enum ContactPhase {
@@ -84,7 +84,7 @@ impl ContactPhase {
let mut manifold_arr: ArrayVec<[&mut ContactManifold; SIMD_WIDTH]> =
ArrayVec::new();
let mut workspace_arr: ArrayVec<
- [Option<&mut (dyn Any + Send + Sync)>; SIMD_WIDTH],
+ [Option<&mut (dyn MaybeSerializableData)>; SIMD_WIDTH],
> = ArrayVec::new();
for (pair, solver_flags) in
@@ -148,7 +148,7 @@ pub struct PrimitiveContactGenerationContext<'a> {
pub position1: &'a Isometry<f32>,
pub position2: &'a Isometry<f32>,
pub manifold: &'a mut ContactManifold,
- pub workspace: Option<&'a mut (dyn Any + Send + Sync)>,
+ pub workspace: Option<&'a mut (dyn MaybeSerializableData)>,
}
#[cfg(feature = "simd-is-enabled")]
@@ -161,7 +161,7 @@ pub struct PrimitiveContactGenerationContextSimd<'a, 'b> {
pub positions1: &'a Isometry<SimdFloat>,
pub positions2: &'a Isometry<SimdFloat>,
pub manifolds: &'a mut [&'b mut ContactManifold],
- pub workspaces: &'a mut [Option<&'b mut (dyn Any + Send + Sync)>],
+ pub workspaces: &'a mut [Option<&'b mut (dyn MaybeSerializableData)>],
}
#[derive(Copy, Clone)]
diff --git a/src/geometry/contact_generator/contact_generator_workspace.rs b/src/geometry/contact_generator/contact_generator_workspace.rs
new file mode 100644
index 0000000..fb6d7b4
--- /dev/null
+++ b/src/geometry/contact_generator/contact_generator_workspace.rs
@@ -0,0 +1,98 @@
+use crate::data::MaybeSerializableData;
+#[cfg(feature = "dim3")]
+use crate::geometry::contact_generator::PfmPfmContactManifoldGeneratorWorkspace;
+use crate::geometry::contact_generator::{
+ HeightFieldShapeContactGeneratorWorkspace, TrimeshShapeContactGeneratorWorkspace,
+ WorkspaceSerializationTag,
+};
+
+// Note we have this newtype because it simplifies the serialization/deserialization code.
+pub struct ContactGeneratorWorkspace(pub Box<dyn MaybeSerializableData>);
+
+impl<T: MaybeSerializableData> From<T> for ContactGeneratorWorkspace {
+ fn from(data: T) -> Self {
+ Self(Box::new(data) as Box<dyn MaybeSerializableData>)
+ }
+}
+
+#[cfg(feature = "serde-serialize")]
+impl serde::Serialize for ContactGeneratorWorkspace {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ use crate::serde::ser::SerializeStruct;
+
+ if let Some((tag, ser)) = self.0.as_serialize() {
+ let mut state = serializer.serialize_struct("ContactGeneratorWorkspace", 2)?;
+ state.serialize_field("tag", &tag)?;
+ state.serialize_field("inner", ser)?;
+ state.end()
+ } else {
+ Err(serde::ser::Error::custom(
+ "Found a non-serializable contact generator workspace.",
+ ))
+ }
+ }
+}
+
+#[cfg(feature = "serde-serialize")]
+impl<'de> serde::Deserialize<'de> for ContactGeneratorWorkspace {
+ 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 = ContactGeneratorWorkspace;
+ 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: u32 = seq
+ .next_element()?
+ .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
+
+ fn deser<'de, A, S: MaybeSerializableData + serde::Deserialize<'de>>(
+ seq: &mut A,
+ ) -> Result<Box<dyn MaybeSerializableData>, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let workspace: S = seq.next_element()?.ok_or_else(|| {
+ serde::de::Error::custom("Failed to deserialize builtin workspace.")
+ })?;
+ Ok(Box::new(workspace) as Box<dyn MaybeSerializableData>)
+ }
+
+ let workspace = match WorkspaceSerializationTag::from_u32(tag) {
+ Some(WorkspaceSerializationTag::HeightfieldShapeContactGeneratorWorkspace) => {
+ deser::<A, HeightFieldShapeContactGeneratorWorkspace>(&mut seq)?
+ }
+ Some(WorkspaceSerializationTag::TrimeshShapeContactGeneratorWorkspace) => {
+ deser::<A, TrimeshShapeContactGeneratorWorkspace>(&mut seq)?
+ }
+ #[cfg(feature = "dim3")]
+ Some(WorkspaceSerializationTag::PfmPfmContactGeneratorWorkspace) => {
+ deser::<A, PfmPfmContactManifoldGeneratorWorkspace>(&mut seq)?
+ }
+ None => {
+ return Err(serde::de::Error::custom(
+ "found invalid contact generator workspace type to deserialize",
+ ))
+ }
+ };
+
+ Ok(ContactGeneratorWorkspace(workspace))
+ }
+ }
+
+ deserializer.deserialize_struct("ContactGeneratorWorkspace", &["tag", "inner"], Visitor {})
+ }
+}
diff --git a/src/geometry/contact_generator/heightfield_shape_contact_generator.rs b/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
index f291fa0..62c6365 100644
--- a/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
+++ b/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
@@ -1,23 +1,29 @@
+use crate::data::hashmap::{Entry, HashMap};
+use crate::data::MaybeSerializableData;
use crate::geometry::contact_generator::{
- ContactGenerationContext, PrimitiveContactGenerationContext, PrimitiveContactGenerator,
+ ContactGenerationContext, ContactGeneratorWorkspace, PrimitiveContactGenerationContext,
+ PrimitiveContactGenerator,
};
#[cfg(feature = "dim2")]
use crate::geometry::Capsule;
-use crate::geometry::{Collider, ContactManifold, HeightField, Shape, ShapeType};
+use crate::geometry::{Collider, ContactManifold, HeightField, Shape};
use crate::ncollide::bounding_volume::BoundingVolume;
-use std::any::Any;
-use std::collections::hash_map::Entry;
-use std::collections::HashMap;
+#[cfg(feature = "serde-serialize")]
+use erased_serde::Serialize;
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
struct SubDetector {
- generator: PrimitiveContactGenerator,
+ #[cfg_attr(feature = "serde-serialize", serde(skip))]
+ generator: Option<PrimitiveContactGenerator>,
manifold_id: usize,
timestamp: bool,
- workspace: Option<Box<(dyn Any + Send + Sync)>>,
+ workspace: Option<ContactGeneratorWorkspace>,
}
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct HeightFieldShapeContactGeneratorWorkspace {
timestamp: bool,
+ #[cfg_attr(feature = "serde-serialize", serde(skip))]
old_manifolds: Vec<ContactManifold>,
sub_detectors: HashMap<usize, SubDetector>,
}
@@ -55,36 +61,9 @@ fn do_generate_contacts(
.generator_workspace
.as_mut()
.expect("The HeightFieldShapeContactGeneratorWorkspace is missing.")
+ .0
.downcast_mut()
.expect("Invalid workspace type, expected a HeightFieldShapeContactGeneratorWorkspace.");
- let shape_type2 = collider2.shape().shape_type();
-
- /*
- * Detect if the detector context has been reset.
- */
- if !ctxt.pair.manifolds.is_empty() && workspace.sub_detectors.is_empty() {
- // Rebuild the subdetector hashmap.
- for (manifold_id, manifold) in ctxt.pair.manifolds.iter().enumerate() {
- let subshape_id = if manifold.pair.collider1 == ctxt.pair.pair.collider1 {
- manifold.subshape_index_pair.0
- } else {
- manifold.subshape_index_pair.1
- };
- let (generator, workspace2) = ctxt
- .dispatcher
- .dispatch_primitives(ShapeType::Capsule, shape_type2);
-
- let sub_detector = SubDetector {
- generator,
- manifold_id,
- timestamp: workspace.timestamp,
- workspace: workspace2,
- };
-
- workspace.sub_detectors.insert(subshape_id, sub_detector);
- }
- }
-
let new_timestamp = !workspace.timestamp;
workspace.timestamp = new_timestamp;
@@ -127,7 +106,7 @@ fn do_generate_contacts(
let (generator, workspace2) =
dispatcher.dispatch_primitives(sub_shape1.shape_type(), shape_type2);
let sub_detector = SubDetector {
- generator,
+ generator: Some(generator),
manifold_id: manifolds.len(),
timestamp: new_timestamp,
workspace: workspace2,
@@ -146,6 +125,19 @@ fn do_generate_contacts(
}
};
+ if sub_detector.generator.is_none() {
+ // We probably lost the generator after deserialization.
+ // So we need to dispatch again.
+ let (generator, workspace2) =
+ dispatcher.dispatch_primitives(sub_shape1.shape_type(), shape_type2);
+ sub_detector.generator = Some(generator);
+
+ // Don't overwrite the workspace if we already deserialized one.
+ if sub_detector.workspace.is_none() {
+ sub_detector.workspace = workspace2;
+ }
+ }
+
let manifold = &mut manifolds[sub_detector.manifold_id];
let mut ctxt2 = if coll_pair.collider1 != manifold.pair.collider1 {
@@ -158,7 +150,7 @@ fn do_generate_contacts(
position1: collider2.position(),
position2: position1,
manifold,
- workspace: sub_detector.workspace.as_deref_mut(),
+ workspace: sub_detector.workspace.as_mut().map(|w| &mut *w.0),
}
} else {
PrimitiveContactGenerationContext {
@@ -170,14 +162,24 @@ fn do_generate_contacts(
position1,
position2: collider2.position(),
manifold,
- workspace: sub_detector.workspace.as_deref_mut(),
+ workspace: sub_detector.workspace.as_mut().map(|w| &mut *w.0),
}
};
- (sub_detector.generator.generate_contacts)(&mut ctxt2)
+ (sub_detector.generator.unwrap().generate_contacts)(&mut ctxt2)
});
workspace
.sub_detectors
.retain(|_, detector| detector.timestamp == new_timestamp)
}
+
+impl MaybeSerializableData for HeightFieldShapeContactGeneratorWorkspace {
+ #[cfg(feature = "serde-serialize")]
+ fn as_serialize(&self) -> Option<(u32, &dyn Serialize)> {
+ Some((
+ super::WorkspaceSerializationTag::HeightfieldShapeContactGeneratorWorkspace as u32,
+ self,
+ ))
+ }
+}
diff --git a/src/geometry/contact_generator/mod.rs b/src/geometry/contact_generator/mod.rs
index 0549420..9b14711 100644
--- a/src/geometry/contact_generator/mod.rs
+++ b/src/geometry/contact_generator/mod.rs
@@ -23,6 +23,7 @@ pub use self::pfm_pfm_contact_generator::{
generate_contacts_pfm_pfm, PfmPfmContactManifoldGeneratorWorkspace,
};
// pub use self::polygon_polygon_contact_generator::generate_contacts_polygon_polygon;
+pub use self::contact_generator_workspace::ContactGeneratorWorkspace;
pub use self::trimesh_shape_contact_generator::{
generate_contacts_trimesh_shape, TrimeshShapeContactGeneratorWorkspace,
};
@@ -31,12 +32,15 @@ pub(crate) use self::polygon_polygon_contact_generator::clip_segments;
#[cfg(feature = "dim2")]
pub(crate) use self::polygon_polygon_contact_generator::clip_segments_with_normal;
+pub(self) use self::serializable_workspace_tag::WorkspaceSerializationTag;
+
mod ball_ball_contact_generator;
mod ball_convex_contact_generator;
mod ball_polygon_contact_generator;
mod capsule_capsule_contact_generator;
mod contact_dispatcher;
mod contact_generator;
+mod contact_generator_workspace;
mod cuboid_capsule_contact_generator;
mod cuboid_cuboid_contact_generator;
mod cuboid_polygon_contact_generator;
@@ -45,6 +49,7 @@ mod heightfield_shape_contact_generator;
#[cfg(feature = "dim3")]
mod pfm_pfm_contact_generator;
mod polygon_polygon_contact_generator;
+mod serializable_workspace_tag;
mod trimesh_shape_contact_generator;
use crate::geometry::{Contact, ContactManifold};
diff --git a/src/geometry/contact_generator/pfm_pfm_contact_generator.rs b/src/geometry/contact_generator/pfm_pfm_contact_generator.rs
index 1dcae33..027c398 100644
--- a/src/geometry/contact_generator/pfm_pfm_contact_generator.rs
+++ b/src/geometry/contact_generator/pfm_pfm_contact_generator.rs
@@ -1,14 +1,24 @@
+use crate::data::MaybeSerializableData;
use crate::geometry::contact_generator::PrimitiveContactGenerationContext;
use crate::geometry::{KinematicsCategory, PolygonalFeatureMap, PolyhedronFace};
use crate::math::{Isometry, Vector};
+#[cfg(feature = "serde-serialize")]
+use erased_serde::Serialize;
use na::Unit;
use ncollide::query;
use ncollide::query::algorithms::{gjk::GJKResult, VoronoiSimplex};
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct PfmPfmContactManifoldGeneratorWorkspace {
+ #[cfg_attr(
+ feature = "serde-serialize",
+ serde(skip, default = "VoronoiSimplex::new")
+ )]
simplex: VoronoiSimplex<f32>,
last_gjk_dir: Option<Unit<Vector<f32>>>,
+ #[cfg_attr(feature = "serde-serialize", serde(skip))]
feature1: PolyhedronFace,
+ #[cfg_attr(feature = "serde-serialize", serde(skip))]
feature2: PolyhedronFace,
}
@@ -117,3 +127,13 @@ fn do_generate_contacts(
// Transfer impulses.
super::match_contacts(&mut ctxt.manifold, &old_manifold_points, false);
}
+
+impl MaybeSerializableData for PfmPfmContactManifoldGeneratorWorkspace {
+ #[cfg(feature = "serde-serialize")]
+ fn as_serialize(&self) -> Option<(u32, &dyn Serialize)> {
+ Some((
+ super::WorkspaceSerializationTag::PfmPfmContactGeneratorWorkspace as u32,
+ self,
+ ))
+ }
+}
diff --git a/src/geometry/contact_generator/serializable_workspace_tag.rs b/src/geometry/contact_generator/serializable_workspace_tag.rs
new file mode 100644
index 0000000..2488c1e
--- /dev/null
+++ b/src/geometry/contact_generator/serializable_workspace_tag.rs
@@ -0,0 +1,9 @@
+use num_derive::FromPrimitive;
+
+#[derive(Copy, Clone, Debug, FromPrimitive)]
+pub(super) enum WorkspaceSerializationTag {
+ TrimeshShapeContactGeneratorWorkspace = 0,
+ #[cfg(feature = "dim3")]
+ PfmPfmContactGeneratorWorkspace,
+ HeightfieldShapeContactGeneratorWorkspace,
+}
diff --git a/src/geometry/contact_generator/trimesh_shape_c