aboutsummaryrefslogtreecommitdiff
path: root/src/geometry
diff options
context:
space:
mode:
authorRobert Hrusecky <robert.hrusecky@utexas.edu>2020-10-06 14:22:26 -0500
committerRobert Hrusecky <robert.hrusecky@utexas.edu>2020-10-06 14:22:26 -0500
commitdd8e25bc4756b8bd01d283b5d7e7c5daa9a1af3f (patch)
tree43a0e92a698d5c622edf406dfdd27037471a5e24 /src/geometry
parent0c1b210109e6d4816dc54f2a6dc93e8d6beb5089 (diff)
parent6b1cd9cd404bd1da6aec94527e58dcd483a50c67 (diff)
downloadrapier-dd8e25bc4756b8bd01d283b5d7e7c5daa9a1af3f.tar.gz
rapier-dd8e25bc4756b8bd01d283b5d7e7c5daa9a1af3f.tar.bz2
rapier-dd8e25bc4756b8bd01d283b5d7e7c5daa9a1af3f.zip
Merge branch 'master' into infinite_fall_memory
Diffstat (limited to 'src/geometry')
-rw-r--r--src/geometry/broad_phase.rs255
-rw-r--r--src/geometry/broad_phase_multi_sap.rs128
-rw-r--r--src/geometry/collider.rs52
-rw-r--r--src/geometry/collider_set.rs47
-rw-r--r--src/geometry/contact_generator/heightfield_shape_contact_generator.rs8
-rw-r--r--src/geometry/contact_generator/trimesh_shape_contact_generator.rs13
-rw-r--r--src/geometry/mod.rs15
-rw-r--r--src/geometry/narrow_phase.rs123
-rw-r--r--src/geometry/proximity_detector/trimesh_shape_proximity_detector.rs13
-rw-r--r--src/geometry/trimesh.rs109
-rw-r--r--src/geometry/waabb.rs108
-rw-r--r--src/geometry/wquadtree.rs560
12 files changed, 1026 insertions, 405 deletions
diff --git a/src/geometry/broad_phase.rs b/src/geometry/broad_phase.rs
deleted file mode 100644
index a43b7af..0000000
--- a/src/geometry/broad_phase.rs
+++ /dev/null
@@ -1,255 +0,0 @@
-use crate::geometry::ColliderHandle;
-use ncollide::bounding_volume::AABB;
-#[cfg(feature = "simd-is-enabled")]
-use {
- crate::geometry::WAABB,
- crate::math::{Point, SIMD_WIDTH},
- crate::utils::WVec,
- simba::simd::SimdBool as _,
-};
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
-pub struct ColliderPair {
- pub collider1: ColliderHandle,
- pub collider2: ColliderHandle,
-}
-
-impl ColliderPair {
- pub fn new(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
- ColliderPair {
- collider1,
- collider2,
- }
- }
-
- pub fn new_sorted(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
- if collider1.into_raw_parts().0 <= collider2.into_raw_parts().0 {
- Self::new(collider1, collider2)
- } else {
- Self::new(collider2, collider1)
- }
- }
-
- pub fn swap(self) -> Self {
- Self::new(self.collider2, self.collider1)
- }
-
- pub fn zero() -> Self {
- Self {
- collider1: ColliderHandle::from_raw_parts(0, 0),
- collider2: ColliderHandle::from_raw_parts(0, 0),
- }
- }
-}
-
-pub struct WAABBHierarchyIntersections {
- curr_level_interferences: Vec<usize>,
- next_level_interferences: Vec<usize>,
-}
-
-impl WAABBHierarchyIntersections {
- pub fn new() -> Self {
- Self {
- curr_level_interferences: Vec::new(),
- next_level_interferences: Vec::new(),
- }
- }
-
- pub fn computed_interferences(&self) -> &[usize] {
- &self.curr_level_interferences[..]
- }
-
- pub(crate) fn computed_interferences_mut(&mut self) -> &mut Vec<usize> {
- &mut self.curr_level_interferences
- }
-}
-
-#[cfg(feature = "simd-is-enabled")]
-#[derive(Clone)]
-#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
-pub struct WAABBHierarchy {
- levels: Vec<Vec<WAABB>>,
-}
-
-#[cfg(feature = "simd-is-enabled")]
-impl WAABBHierarchy {
- pub fn new(aabbs: &[AABB<f32>]) -> Self {
- let mut waabbs: Vec<_> = aabbs
- .chunks_exact(SIMD_WIDTH)
- .map(|aabbs| WAABB::from(array![|ii| aabbs[ii]; SIMD_WIDTH]))
- .collect();
-
- if aabbs.len() % SIMD_WIDTH != 0 {
- let first_i = (aabbs.len() / SIMD_WIDTH) * SIMD_WIDTH;
- let last_i = aabbs.len() - 1;
- let last_waabb =
- WAABB::from(array![|ii| aabbs[(first_i + ii).min(last_i)]; SIMD_WIDTH]);
- waabbs.push(last_waabb);
- }
-
- let mut levels = vec![waabbs];
-
- loop {
- let last_level = levels.last().unwrap();
- let mut next_level = Vec::new();
-
- for chunk in last_level.chunks_exact(SIMD_WIDTH) {
- let mins = Point::from(array![|ii| chunk[ii].mins.horizontal_inf(); SIMD_WIDTH]);
- let maxs = Point::from(array![|ii| chunk[ii].maxs.horizontal_sup(); SIMD_WIDTH]);
- next_level.push(WAABB::new(mins, maxs));
- }
-
- // Deal with the last non-exact chunk.
- if last_level.len() % SIMD_WIDTH != 0 {
- let first_id = (last_level.len() / SIMD_WIDTH) * SIMD_WIDTH;
- let last_id = last_level.len() - 1;
- let mins = array![|ii| last_level[(first_id + ii).min(last_id)]
- .mins
- .horizontal_inf(); SIMD_WIDTH];
- let maxs = array![|ii| last_level[(first_id + ii).min(last_id)]
- .maxs
- .horizontal_sup(); SIMD_WIDTH];
-
- let mins = Point::from(mins);
- let maxs = Point::from(maxs);
- next_level.push(WAABB::new(mins, maxs));
- }
-
- if next_level.len() == 1 {
- levels.push(next_level);
- break;
- }
-
- levels.push(next_level);
- }
-
- Self { levels }
- }
-
- pub fn compute_interferences_with(
- &self,
- aabb: AABB<f32>,
- workspace: &mut WAABBHierarchyIntersections,
- ) {
- let waabb1 = WAABB::splat(aabb);
- workspace.next_level_interferences.clear();
- workspace.curr_level_interferences.clear();
- workspace.curr_level_interferences.push(0);
-
- for level in self.levels.iter().rev() {
- for i in &workspace.curr_level_interferences {
- // This `if let` handle the case when `*i` is out of bounds because
- // the initial number of aabbs was not a power of SIMD_WIDTH.
- if let Some(waabb2) = level.get(*i) {
- // NOTE: using `intersect.bitmask()` and performing bit comparisons
- // is much more efficient than testing if each intersect.extract(i) is true.
- let intersect = waabb1.intersects_lanewise(waabb2);
- let bitmask = intersect.bitmask();
-
- for j in 0..SIMD_WIDTH {
- if (bitmask & (1 << j)) != 0 {
- workspace.next_level_interferences.push(i * SIMD_WIDTH + j)
- }
- }
- }
- }
-
- std::mem::swap(
- &mut workspace.curr_level_interferences,
- &mut workspace.next_level_interferences,
- );
- workspace.next_level_interferences.clear();
- }
- }
-}
-
-#[cfg(not(feature = "simd-is-enabled"))]
-#[derive(Clone)]
-#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
-pub struct WAABBHierarchy {
- levels: Vec<Vec<AABB<f32>>>,
-}
-
-#[cfg(not(feature = "simd-is-enabled"))]
-impl WAABBHierarchy {
- const GROUP_SIZE: usize = 4;
-
- pub fn new(aabbs: &[AABB<f32>]) -> Self {
- use ncollide::bounding_volume::BoundingVolume;
-
- let mut levels = vec![aabbs.to_vec()];
-
- loop {
- let last_level = levels.last().unwrap();
- let mut next_level = Vec::new();
-
- for chunk in last_level.chunks(Self::GROUP_SIZE) {
- let mut merged = chunk[0];
- for aabb in &chunk[1..] {
- merged.merge(aabb)
- }
-
- next_level.push(merged);
- }
-
- if next_level.len() == 1 {
- levels.push(next_level);
- break;
- }
-
- levels.push(next_level);
- }
-
- Self { levels }
- }
-
- pub fn compute_interferences_with(
- &self,
- aabb1: AABB<f32>,
- workspace: &mut WAABBHierarchyIntersections,
- ) {
- use ncollide::bounding_volume::BoundingVolume;
-
- workspace.next_level_interferences.clear();
- workspace.curr_level_interferences.clear();
- workspace.curr_level_interferences.push(0);
-
- for level in self.levels[1..].iter().rev() {
- for i in &workspace.curr_level_interferences {
- for j in 0..Self::GROUP_SIZE {
- if let Some(aabb2) = level.get(*i + j) {
- if aabb1.intersects(aabb2) {
- workspace
- .next_level_interferences
- .push((i + j) * Self::GROUP_SIZE)
- }
- }
- }
- }
-
- std::mem::swap(
- &mut workspace.curr_level_interferences,
- &mut workspace.next_level_interferences,
- );
- workspace.next_level_interferences.clear();
- }
-
- // Last level.
- for i in &workspace.curr_level_interferences {
- for j in 0..Self::GROUP_SIZE {
- if let Some(aabb2) = self.levels[0].get(*i + j) {
- if aabb1.intersects(aabb2) {
- workspace.next_level_interferences.push(i + j)
- }
- }
- }
- }
-
- std::mem::swap(
- &mut workspace.curr_level_interferences,
- &mut workspace.next_level_interferences,
- );
- workspace.next_level_interferences.clear();
- }
-}
diff --git a/src/geometry/broad_phase_multi_sap.rs b/src/geometry/broad_phase_multi_sap.rs
index 10c0c8b..db83fa3 100644
--- a/src/geometry/broad_phase_multi_sap.rs
+++ b/src/geometry/broad_phase_multi_sap.rs
@@ -1,5 +1,6 @@
+use crate::data::pubsub::Subscription;
use crate::dynamics::RigidBodySet;
-use crate::geometry::{ColliderHandle, ColliderPair, ColliderSet};
+use crate::geometry::{ColliderHandle, ColliderSet, RemovedCollider};
use crate::math::{Point, Vector, DIM};
#[cfg(feature = "enhanced-determinism")]
use crate::utils::FxHashMap32 as HashMap;
@@ -15,6 +16,41 @@ const NEXT_FREE_SENTINEL: u32 = u32::MAX;
const SENTINEL_VALUE: f32 = f32::MAX;
const CELL_WIDTH: f32 = 20.0;
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+pub struct ColliderPair {
+ pub collider1: ColliderHandle,
+ pub collider2: ColliderHandle,
+}
+
+impl ColliderPair {
+ pub fn new(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
+ ColliderPair {
+ collider1,
+ collider2,
+ }
+ }
+
+ pub fn new_sorted(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
+ if collider1.into_raw_parts().0 <= collider2.into_raw_parts().0 {
+ Self::new(collider1, collider2)
+ } else {
+ Self::new(collider2, collider1)
+ }
+ }
+
+ pub fn swap(self) -> Self {
+ Self::new(self.collider2, self.collider1)
+ }
+
+ pub fn zero() -> Self {
+ Self {
+ collider1: ColliderHandle::from_raw_parts(0, 0),
+ collider2: ColliderHandle::from_raw_parts(0, 0),
+ }
+ }
+}
+
pub enum BroadPhasePairEvent {
AddPair(ColliderPair),
DeletePair(ColliderPair),
@@ -392,6 +428,7 @@ impl SAPRegion {
pub struct BroadPhase {
proxies: Proxies,
regions: HashMap<Point<i32>, SAPRegion>,
+ removed_colliders: Option<Subscription<RemovedCollider>>,
deleted_any: bool,
// We could think serializing this workspace is useless.
// It turns out is is important to serialize at least its capacity
@@ -480,6 +517,7 @@ impl BroadPhase {
/// Create a new empty broad-phase.
pub fn new() -> Self {
BroadPhase {
+ removed_colliders: None,
proxies: Proxies::new(),
regions: HashMap::default(),
reporting: HashMap::default(),
@@ -487,46 +525,60 @@ impl BroadPhase {
}
}
- pub(crate) fn remove_colliders(&mut self, handles: &[ColliderHandle], colliders: &ColliderSet) {
- for collider in handles.iter().filter_map(|h| colliders.get(*h)) {
- if collider.proxy_index == crate::INVALID_USIZE {
- // This collider has not been added to the broad-phase yet.
- continue;
- }
+ /// Maintain the broad-phase internal state by taking collider removal into account.
+ pub fn maintain(&mut self, colliders: &mut ColliderSet) {
+ // Ensure we already subscribed.
+ if self.removed_colliders.is_none() {
+ self.removed_colliders = Some(colliders.removed_colliders.subscribe());
+ }
- let proxy = &mut self.proxies[collider.proxy_index];
+ let mut cursor = self.removed_colliders.take().unwrap();
+ for collider in colliders.removed_colliders.read(&cursor) {
+ self.remove_collider(collider.proxy_index);
+ }
- // Push the proxy to infinity, but not beyond the sentinels.
- proxy.aabb.mins.coords.fill(SENTINEL_VALUE / 2.0);
- proxy.aabb.maxs.coords.fill(SENTINEL_VALUE / 2.0);
- // Discretize the AABB to find the regions that need to be invalidated.
- let start = point_key(proxy.aabb.mins);
- let end = point_key(proxy.aabb.maxs);
+ colliders.removed_colliders.ack(&mut cursor);
+ self.removed_colliders = Some(cursor);
+ }
- #[cfg(feature = "dim2")]
- for i in start.x..=end.x {
- for j in start.y..=end.y {
- if let Some(region) = self.regions.get_mut(&Point::new(i, j)) {
- region.predelete_proxy(collider.proxy_index);
- self.deleted_any = true;
- }
+ fn remove_collider<'a>(&mut self, proxy_index: usize) {
+ if proxy_index == crate::INVALID_USIZE {
+ // This collider has not been added to the broad-phase yet.
+ return;
+ }
+
+ let proxy = &mut self.proxies[proxy_index];
+
+ // Push the proxy to infinity, but not beyond the sentinels.
+ proxy.aabb.mins.coords.fill(SENTINEL_VALUE / 2.0);
+ proxy.aabb.maxs.coords.fill(SENTINEL_VALUE / 2.0);
+ // Discretize the AABB to find the regions that need to be invalidated.
+ let start = point_key(proxy.aabb.mins);
+ let end = point_key(proxy.aabb.maxs);
+
+ #[cfg(feature = "dim2")]
+ for i in start.x..=end.x {
+ for j in start.y..=end.y {
+ if let Some(region) = self.regions.get_mut(&Point::new(i, j)) {
+ region.predelete_proxy(proxy_index);
+ self.deleted_any = true;
}
}
+ }
- #[cfg(feature = "dim3")]
- for i in start.x..=end.x {
- for j in start.y..=end.y {
- for k in start.z..=end.z {
- if let Some(region) = self.regions.get_mut(&Point::new(i, j, k)) {
- region.predelete_proxy(collider.proxy_index);
- self.deleted_any = true;
- }
+ #[cfg(feature = "dim3")]
+ for i in start.x..=end.x {
+ for j in start.y..=end.y {
+ for k in start.z..=end.z {
+ if let Some(region) = self.regions.get_mut(&Point::new(i, j, k)) {
+ region.predelete_proxy(proxy_index);
+ self.deleted_any = true;
}
}
}
-
- self.proxies.remove(collider.proxy_index);
}
+
+ self.proxies.remove(proxy_index);
}
pub(crate) fn update_aabbs(
@@ -664,16 +716,13 @@ impl BroadPhase {
mod test {
use crate::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet};
use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
- use crate::pipeline::PhysicsPipeline;
#[test]
fn test_add_update_remove() {
let mut broad_phase = BroadPhase::new();
- let mut narrow_phase = NarrowPhase::new();
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let mut joints = JointSet::new();
- let mut pipeline = PhysicsPipeline::new();
let rb = RigidBodyBuilder::new_dynamic().build();
let co = ColliderBuilder::ball(0.5).build();
@@ -682,15 +731,8 @@ mod test {
broad_phase.update_aabbs(0.0, &bodies, &mut colliders);
- pipeline.remove_rigid_body(
- hrb,
- &mut broad_phase,
- &mut narrow_phase,
- &mut bodies,
- &mut colliders,
- &mut joints,
- );
-
+ bodies.remove(hrb, &mut colliders, &mut joints);
+ broad_phase.maintain(&mut colliders);
broad_phase.update_aabbs(0.0, &bodies, &mut colliders);
// Create another body.
diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs
index 2d55857..7c293b6 100644
--- a/src/geometry/collider.rs
+++ b/src/geometry/collider.rs
@@ -1,11 +1,12 @@
use crate::dynamics::{MassProperties, RigidBodyHandle, RigidBodySet};
use crate::geometry::{
Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph, Polygon,
- Proximity, Triangle, Trimesh,
+ Proximity, Ray, RayIntersection, Triangle, Trimesh,
};
use crate::math::{AngVector, Isometry, Point, Rotation, Vector};
use na::Point3;
use ncollide::bounding_volume::{HasBoundingVolume, AABB};
+use ncollide::query::RayCast;
use num::Zero;
#[derive(Clone)]
@@ -97,6 +98,49 @@ impl Shape {
Shape::HeightField(heightfield) => heightfield.bounding_volume(position),
}
}
+
+ /// Computes the first intersection point between a ray in this collider.
+ ///
+ /// Some shapes are not supported yet and will always return `None`.
+ ///
+ /// # Parameters
+ /// - `position`: the position of this shape.
+ /// - `ray`: the ray to cast.
+ /// - `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
+ /// limits the length of the ray to `ray.dir.norm() * max_toi`. Use `f32::MAX` for an unbounded ray.
+ pub fn cast_ray(
+ &self,
+ position: &Isometry<f32>,
+ ray: &Ray,
+ max_toi: f32,
+ ) -> Option<RayIntersection> {
+ match self {
+ Shape::Ball(ball) => ball.toi_and_normal_with_ray(position, ray, max_toi, true),
+ Shape::Polygon(_poly) => None,
+ Shape::Capsule(caps) => {
+ let pos = position * caps.transform_wrt_y();
+ let caps = ncollide::shape::Capsule::new(caps.half_height(), caps.radius);
+ caps.toi_and_normal_with_ray(&pos, ray, max_toi, true)
+ }
+ Shape::Cuboid(cuboid) => cuboid.toi_and_normal_with_ray(position, ray, max_toi, true),
+ #[cfg(feature = "dim2")]
+ Shape::Triangle(_) | Shape::Trimesh(_) => {
+ // This is not implemented yet in 2D.
+ None
+ }
+ #[cfg(feature = "dim3")]
+ Shape::Triangle(triangle) => {
+ triangle.toi_and_normal_with_ray(position, ray, max_toi, true)
+ }
+ #[cfg(feature = "dim3")]
+ Shape::Trimesh(trimesh) => {
+ trimesh.toi_and_normal_with_ray(position, ray, max_toi, true)
+ }
+ Shape::HeightField(heightfield) => {
+ heightfield.toi_and_normal_with_ray(position, ray, max_toi, true)
+ }
+ }
+ }
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
@@ -353,6 +397,12 @@ impl ColliderBuilder {
self
}
+ /// Sets the restitution coefficient of the collider this builder will build.
+ pub fn restitution(mut self, restitution: f32) -> Self {
+ self.restitution = restitution;
+ self
+ }
+
/// Sets the density of the collider this builder will build.
pub fn density(mut self, density: f32) -> Self {
self.density = Some(density);
diff --git a/src/geometry/collider_set.rs b/src/geometry/collider_set.rs
index 22bba1b..5ac9658 100644
--- a/src/geometry/collider_set.rs
+++ b/src/geometry/collider_set.rs
@@ -1,14 +1,25 @@
use crate::data::arena::Arena;
+use crate::data::pubsub::PubSub;
use crate::dynamics::{RigidBodyHandle, RigidBodySet};
-use crate::geometry::Collider;
+use crate::geometry::{Collider, ColliderGraphIndex};
use std::ops::{Index, IndexMut};
/// The unique identifier of a collider added to a collider set.
pub type ColliderHandle = crate::data::arena::Index;
+#[derive(Copy, Clone, Debug)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+pub(crate) struct RemovedCollider {
+ pub handle: ColliderHandle,
+ pub(crate) contact_graph_index: ColliderGraphIndex,
+ pub(crate) proximity_graph_index: ColliderGraphIndex,
+ pub(crate) proxy_index: usize,
+}
+
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
/// A set of colliders that can be handled by a physics `World`.
pub struct ColliderSet {
+ pub(crate) removed_colliders: PubSub<RemovedCollider>,
pub(crate) colliders: Arena<Collider>,
}
@@ -16,6 +27,7 @@ impl ColliderSet {
/// Create a new empty set of colliders.
pub fn new() -> Self {
ColliderSet {
+ removed_colliders: PubSub::new(),
colliders: Arena::new(),
}
}
@@ -26,7 +38,7 @@ impl ColliderSet {
}
/// Iterate through all the colliders on this set.
- pub fn iter(&self) -> impl Iterator<Item = (ColliderHandle, &Collider)> {
+ pub fn iter(&self) -> impl ExactSizeIterator<Item = (ColliderHandle, &Collider)> {
self.colliders.iter()
}
@@ -60,8 +72,35 @@ impl ColliderSet {
handle
}
- pub(crate) fn remove_internal(&mut self, handle: ColliderHandle) -> Option<Collider> {
- self.colliders.remove(handle)
+ /// Remove a collider from this set and update its parent accordingly.
+ pub fn remove(
+ &mut self,
+ handle: ColliderHandle,
+ bodies: &mut RigidBodySet,
+ ) -> Option<Collider> {
+ let collider = self.colliders.remove(handle)?;
+
+ /*
+ * Delete the collider from its parent body.
+ */
+ if let Some(parent) = bodies.get_mut_internal(collider.parent) {
+ parent.remove_collider_internal(handle, &collider);
+ bodies.wake_up(collider.parent, true);
+ }
+
+ /*
+ * Publish removal.
+ */
+ let message = RemovedCollider {
+ handle,
+ contact_graph_index: collider.contact_graph_index,
+ proximity_graph_index: collider.proximity_graph_index,
+ proxy_index: collider.proxy_index,
+ };
+
+ self.removed_colliders.publish(message);
+
+ Some(collider)
}
/// Gets the collider with the given handle without a known generation.
diff --git a/src/geometry/contact_generator/heightfield_shape_contact_generator.rs b/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
index f59a94b..04afc65 100644
--- a/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
+++ b/src/geometry/contact_generator/heightfield_shape_contact_generator.rs
@@ -71,10 +71,10 @@ fn do_generate_contacts(
} else {
manifold.subshape_index_pair.1
};
- println!(
- "Restoring for {} [chosen with {:?}]",
- subshape_id, manifold.subshape_index_pair
- );
+ // println!(
+ // "Restoring for {} [chosen with {:?}]",
+ // subshape_id, manifold.subshape_index_pair
+ // );
// Use dummy shapes for the dispatch.
#[cfg(feature = "dim2")]
diff --git a/src/geometry/contact_generator/trimesh_shape_contact_generator.rs b/src/geometry/contact_generator/trimesh_shape_contact_generator.rs
index 78f26bb..52ba9b7 100644
--- a/src/geometry/contact_generator/trimesh_shape_contact_generator.rs
+++ b/src/geometry/contact_generator/trimesh_shape_contact_generator.rs
@@ -1,11 +1,11 @@
use crate::geometry::contact_generator::{
ContactGenerationContext, PrimitiveContactGenerationContext,
};
-use crate::geometry::{Collider, ContactManifold, Shape, Trimesh, WAABBHierarchyIntersections};
+use crate::geometry::{Collider, ContactManifold, Shape, Trimesh};
use crate::ncollide::bounding_volume::{BoundingVolume, AABB};
pub struct TrimeshShapeContactGeneratorWorkspace {
- interferences: WAABBHierarchyIntersections,
+ interferences: Vec<usize>,
local_aabb2: AABB<f32>,
old_interferences: Vec<usize>,
old_manifolds: Vec<ContactManifold>,
@@ -14,7 +14,7 @@ pub struct TrimeshShapeContactGeneratorWorkspace {
impl TrimeshShapeContactGeneratorWorkspace {
pub fn new() -> Self {
Self {
- interferences: WAABBHierarchyIntersections::new(),
+ interferences: Vec::new(),
local_aabb2: AABB::new_invalid(),
old_interferences: Vec::new(),
old_manifolds: Vec::new(),
@@ -74,7 +74,7 @@ fn do_generate_contacts(
let local_aabb2 = new_local_aabb2; // .loosened(ctxt.prediction_distance * 2.0); // FIXME: what would be the best value?
std::mem::swap(
&mut workspace.old_interferences,
- workspace.interferences.computed_interferences_mut(),
+ &mut workspace.interferences,
);
std::mem::swap(&mut workspace.old_manifolds, &mut ctxt.pair.manifolds);
ctxt.pair.manifolds.clear();
@@ -108,16 +108,17 @@ fn do_generate_contacts(
// workspace.old_manifolds.len()
// );
+ workspace.interferences.clear();
trimesh1
.waabbs()
- .compute_interferences_with(local_aabb2, &mut workspace.interferences);
+ .intersect_aabb(&local_aabb2, &mut workspace.interferences);
workspace.local_aabb2 = local_aabb2;
}
/*
* Dispatch to the specific solver by keeping the previous manifold if we already had one.
*/
- let new_interferences = workspace.interferences.computed_interferences();
+ let new_interferences = &workspace.interferences;
let mut old_inter_it = workspace.old_interferences.drain(..).peekable();
let mut old_manifolds_it = workspace.old_manifolds.drain(..);
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index 4f72778..562f962 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -36,11 +36,15 @@ pub type AABB = ncollide::bounding_volume::AABB<f32>;
pub type ContactEvent = ncollide::pipeline::ContactEvent<ColliderHandle>;
/// Event triggered when a sensor collider starts or stop being in proximity with another collider (sensor or not).
pub type ProximityEvent = ncollide::pipeline::ProximityEvent<ColliderHandle>;
+/// A ray that can be cast against colliders.
+pub type Ray = ncollide::query::Ray<f32>;
+/// The intersection between a ray and a collider.
+pub type RayIntersection = ncollide::query::RayIntersection<f32>;
#[cfg(feature = "simd-is-enabled")]
pub(crate) use self::ball::WBall;
-pub(crate) use self::broad_phase::{ColliderPair, WAABBHierarchy, WAABBHierarchyIntersections};
-pub(crate) use self::broad_phase_multi_sap::BroadPhasePairEvent;
+pub(crate) use self::broad_phase_multi_sap::{BroadPhasePairEvent, ColliderPair};
+pub(crate) use self::collider_set::RemovedCollider;
#[cfg(feature = "simd-is-enabled")]
pub(crate) use self::contact::WContact;
#[cfg(feature = "dim2")]
@@ -48,12 +52,11 @@ pub(crate) use self::contact_generator::{clip_segments, clip_segments_with_norma
pub(crate) use self::narrow_phase::ContactManifoldIndex;
#[cfg(feature = "dim3")]
pub(crate) use self::polyhedron_feature3d::PolyhedronFace;
-#[cfg(feature = "simd-is-enabled")]
-pub(crate) use self::waabb::WAABB;
+pub(crate) use self::waabb::{WRay, WAABB};
+pub(crate) use self::wquadtree::WQuadtree;
//pub(crate) use self::z_order::z_cmp_floats;
mod ball;
-mod broad_phase;
mod broad_phase_multi_sap;
mod capsule;
mod collider;
@@ -75,6 +78,6 @@ mod proximity_detector;
pub(crate) mod sat;
pub(crate) mod triangle;
mod trimesh;
-#[cfg(feature = "simd-is-enabled")]
mod waabb;
+mod wquadtree;
//mod z_order;
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
index 1a36511..ebe0a79 100644
--- a/src/geometry/narrow_phase.rs
+++ b/src/geometry/narrow_phase.rs
@@ -14,13 +14,16 @@ use crate::geometry::proximity_detector::{
// proximity_detector::ProximityDetectionContextSimd, WBall,
//};
use crate::geometry::{
- BroadPhasePairEvent, ColliderHandle, ContactEvent, ProximityEvent, ProximityPair,
+ BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ProximityEvent,
+ ProximityPair, RemovedCollider,
};
use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph};
//#[cfg(feature = "simd-is-enabled")]
//use crate::math::{SimdFloat, SIMD_WIDTH};
+use crate::data::pubsub::Subscription;
use crate::ncollide::query::Proximity;
use crate::pipeline::EventHandler;
+use std::collections::HashMap;
//use simba::simd::SimdValue;
/// The narrow-phase responsible for computing precise contact information between colliders.
@@ -28,6 +31,7 @@ use crate::pipeline::EventHandler;
pub struct NarrowPhase {
contact_graph: InteractionGraph<ContactPair>,
proximity_graph: InteractionGraph<ProximityPair>,
+ removed_colliders: Option<Subscription<RemovedCollider>>,
// ball_ball: Vec<usize>, // Workspace: Vec<*mut ContactPair>,
// shape_shape: Vec<usize>, // Workspace: Vec<*mut ContactPair>,
// ball_ball_prox: Vec<usize>, // Workspace: Vec<*mut ProximityPair>,
@@ -42,6 +46,7 @@ impl NarrowPhase {
Self {
contact_graph: InteractionGraph::new(),
proximity_graph: InteractionGraph::new(),
+ removed_colliders: None,
// ball_ball: Vec::new(),
// shape_shape: Vec::new(),
// ball_ball_prox: Vec::new(),
@@ -73,45 +78,84 @@ impl NarrowPhase {
// &mut self.contact_graph.interactions
// }
- pub(crate) fn remove_colliders(
+ /// Maintain the narrow-phase internal state by taking collider removal into account.
+ pub fn maintain(&mut self, colliders: &mut ColliderSet, bodies: &mut RigidBodySet) {
+ // Ensure we already subscribed.
+ if self.removed_colliders.is_none() {
+ self.removed_colliders = Some(colliders.removed_colliders.subscribe());
+ }
+
+ let mut cursor = self.removed_colliders.take().unwrap();
+
+ // TODO: avoid these hash-maps.
+ // They are necessary to handle the swap-remove done internally
+ // by the contact/proximity graphs when a node is removed.
+ let mut prox_id_remap = HashMap::new();
+ let mut contact_id_remap = HashMap::new();
+
+ for i in 0.. {
+ if let Some(collider) = colliders.removed_colliders.read_ith(&cursor, i) {
+ let proximity_graph_id = prox_id_remap
+ .get(&collider.handle)
+ .copied()
+ .unwrap_or(collider.proximity_graph_index);
+ let contact_graph_id = contact_id_remap
+ .get(&collider.handle)
+ .copied()
+ .unwrap_or(collider.contact_graph_index);
+
+ self.remove_collider(
+ proximity_graph_id,
+ contact_graph_id,
+ colliders,
+ bodies,
+ &mut prox_id_remap,
+ &mut contact_id_remap,
+ );
+ } else {
+ break;
+ }
+ }
+
+ colliders.removed_colliders.ack(&mut cursor);
+ self.removed_colliders = Some(cursor);
+ }
+
+ pub(crate) fn remove_collider<'a>(
&mut self,
- handles: &[ColliderHandle],
+ proximity_graph_id: ColliderGraphIndex,
+ contact_graph_id: ColliderGraphIndex,
colliders: &mut ColliderSet,
bodies: &mut RigidBodySet,
+ prox_id_remap: &mut HashMap<ColliderHandle, ColliderGraphIndex>,
+ contact_id_remap: &mut HashMap<ColliderHandle, ColliderGraphIndex>,
) {
- for handle in handles {
- if let Some(collider) = colliders.get(*handle) {
- let proximity_graph_id = collider.proximity_graph_index;
- let contact_graph_id = collider.contact_graph_index;
-
- // Wake up every body in contact with the deleted collider.
- for (a, b, _) in self.contact_graph.interactions_with(contact_graph_id) {
- if let Some(pa