aboutsummaryrefslogtreecommitdiff
path: root/src/geometry/narrow_phase.rs
diff options
context:
space:
mode:
authorSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
committerSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
commit754a48b7ff6d8c58b1ee08651e60112900b60455 (patch)
tree7d777a6c003f1f5d8f8d24f533f35a95a88957fe /src/geometry/narrow_phase.rs
downloadrapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.gz
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.bz2
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.zip
First public release of Rapier.v0.1.0
Diffstat (limited to 'src/geometry/narrow_phase.rs')
-rw-r--r--src/geometry/narrow_phase.rs483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
new file mode 100644
index 0000000..3eb2c30
--- /dev/null
+++ b/src/geometry/narrow_phase.rs
@@ -0,0 +1,483 @@
+#[cfg(feature = "parallel")]
+use rayon::prelude::*;
+
+use crate::dynamics::RigidBodySet;
+use crate::geometry::contact_generator::{
+ ContactDispatcher, ContactGenerationContext, DefaultContactDispatcher,
+};
+use crate::geometry::proximity_detector::{
+ DefaultProximityDispatcher, ProximityDetectionContext, ProximityDispatcher,
+};
+//#[cfg(feature = "simd-is-enabled")]
+//use crate::geometry::{
+// contact_generator::ContactGenerationContextSimd,
+// proximity_detector::ProximityDetectionContextSimd, WBall,
+//};
+use crate::geometry::{
+ BroadPhasePairEvent, ColliderHandle, ContactEvent, ProximityEvent, ProximityPair,
+};
+use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph};
+//#[cfg(feature = "simd-is-enabled")]
+//use crate::math::{SimdFloat, SIMD_WIDTH};
+use crate::ncollide::query::Proximity;
+use crate::pipeline::EventHandler;
+//use simba::simd::SimdValue;
+
+/// The narrow-phase responsible for computing precise contact information between colliders.
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+pub struct NarrowPhase {
+ contact_graph: InteractionGraph<ContactPair>,
+ proximity_graph: InteractionGraph<ProximityPair>,
+ // 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>,
+ // shape_shape_prox: Vec<usize>, // Workspace: Vec<*mut ProximityPair>,
+}
+
+pub(crate) type ContactManifoldIndex = usize;
+
+impl NarrowPhase {
+ /// Creates a new empty narrow-phase.
+ pub fn new() -> Self {
+ Self {
+ contact_graph: InteractionGraph::new(),
+ proximity_graph: InteractionGraph::new(),
+ // ball_ball: Vec::new(),
+ // shape_shape: Vec::new(),
+ // ball_ball_prox: Vec::new(),
+ // shape_shape_prox: Vec::new(),
+ }
+ }
+
+ /// The contact graph containing all contact pairs and their contact information.
+ pub fn contact_graph(&self) -> &InteractionGraph<ContactPair> {
+ &self.contact_graph
+ }
+
+ /// The proximity graph containing all proximity pairs and their proximity information.
+ pub fn proximity_graph(&self) -> &InteractionGraph<ProximityPair> {
+ &self.proximity_graph
+ }
+
+ // #[cfg(feature = "parallel")]
+ // pub fn contact_pairs(&self) -> &[ContactPair] {
+ // &self.contact_graph.interactions
+ // }
+
+ // pub fn contact_pairs_mut(&mut self) -> &mut [ContactPair] {
+ // &mut self.contact_graph.interactions
+ // }
+
+ // #[cfg(feature = "parallel")]
+ // pub(crate) fn contact_pairs_vec_mut(&mut self) -> &mut Vec<ContactPair> {
+ // &mut self.contact_graph.interactions
+ // }
+
+ pub(crate) fn remove_colliders(
+ &mut self,
+ handles: &[ColliderHandle],
+ colliders: &mut ColliderSet,
+ bodies: &mut RigidBodySet,
+ ) {
+ 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(parent) = colliders.get(a).map(|c| c.parent) {
+ bodies.wake_up(parent)
+ }
+
+ if let Some(parent) = colliders.get(b).map(|c| c.parent) {
+ bodies.wake_up(parent)
+ }
+ }
+
+ // We have to manage the fact that one other collider will
+ // hive its graph index changed because of the node's swap-remove.
+ if let Some(replacement) = self
+ .proximity_graph
+ .remove_node(proximity_graph_id)
+ .and_then(|h| colliders.get_mut(h))
+ {
+ replacement.proximity_graph_index = proximity_graph_id;
+ }
+
+ if let Some(replacement) = self
+ .contact_graph
+ .remove_node(contact_graph_id)
+ .and_then(|h| colliders.get_mut(h))
+ {
+ replacement.contact_graph_index = contact_graph_id;
+ }
+ }
+ }
+ }
+
+ pub(crate) fn register_pairs(
+ &mut self,
+ colliders: &mut ColliderSet,
+ broad_phase_events: &[BroadPhasePairEvent],
+ events: &dyn EventHandler,
+ ) {
+ for event in broad_phase_events {
+ match event {
+ BroadPhasePairEvent::AddPair(pair) => {
+ // println!("Adding pair: {:?}", *pair);
+ if let (Some(co1), Some(co2)) =
+ colliders.get2_mut_internal(pair.collider1, pair.collider2)
+ {
+ if co1.is_sensor() || co2.is_sensor() {
+ let gid1 = co1.proximity_graph_index;
+ let gid2 = co2.proximity_graph_index;
+
+ // NOTE: the collider won't have a graph index as long
+ // as it does not interact with anything.
+ if !InteractionGraph::<ProximityPair>::is_graph_index_valid(gid1) {
+ co1.proximity_graph_index =
+ self.proximity_graph.graph.add_node(pair.collider1);
+ }
+
+ if !InteractionGraph::<ProximityPair>::is_graph_index_valid(gid2) {
+ co2.proximity_graph_index =
+ self.proximity_graph.graph.add_node(pair.collider2);
+ }
+
+ if self.proximity_graph.graph.find_edge(gid1, gid2).is_none() {
+ let dispatcher = DefaultProximityDispatcher;
+ let generator = dispatcher.dispatch(co1.shape(), co2.shape());
+ let interaction =
+ ProximityPair::new(*pair, generator.0, generator.1);
+ let _ = self.proximity_graph.add_edge(
+ co1.proximity_graph_index,
+ co2.proximity_graph_index,
+ interaction,
+ );
+ }
+ } else {
+ // NOTE: same code as above, but for the contact graph.
+ // TODO: refactor both pieces of code somehow?
+ let gid1 = co1.contact_graph_index;
+ let gid2 = co2.contact_graph_index;
+
+ // NOTE: the collider won't have a graph index as long
+ // as it does not interact with anything.
+ if !InteractionGraph::<ContactPair>::is_graph_index_valid(gid1) {
+ co1.contact_graph_index =
+ self.contact_graph.graph.add_node(pair.collider1);
+ }
+
+ if !InteractionGraph::<ContactPair>::is_graph_index_valid(gid2) {
+ co2.contact_graph_index =
+ self.contact_graph.graph.add_node(pair.collider2);
+ }
+
+ if self.contact_graph.graph.find_edge(gid1, gid2).is_none() {
+ let dispatcher = DefaultContactDispatcher;
+ let generator = dispatcher.dispatch(co1.shape(), co2.shape());
+ let interaction = ContactPair::new(*pair, generator.0, generator.1);
+ let _ = self.contact_graph.add_edge(
+ co1.contact_graph_index,
+ co2.contact_graph_index,
+ interaction,
+ );
+ }
+ }
+ }
+ }
+ BroadPhasePairEvent::DeletePair(pair) => {
+ if let (Some(co1), Some(co2)) =
+ colliders.get2_mut_internal(pair.collider1, pair.collider2)
+ {
+ if co1.is_sensor() || co2.is_sensor() {
+ let prox_pair = self
+ .proximity_graph
+ .remove_edge(co1.proximity_graph_index, co2.proximity_graph_index);
+
+ // Emit a proximity lost event if we had a proximity before removing the edge.
+ if let Some(prox) = prox_pair {
+ if prox.proximity != Proximity::Disjoint {
+ let prox_event = ProximityEvent::new(
+ pair.collider1,
+ pair.collider2,
+ prox.proximity,
+ Proximity::Disjoint,
+ );
+ events.handle_proximity_event(prox_event)
+ }
+ }
+ } else {
+ let contact_pair = self
+ .contact_graph
+ .remove_edge(co1.contact_graph_index, co2.contact_graph_index);
+
+ // Emit a contact stopped event if we had a proximity before removing the edge.
+ if let Some(ctct) = contact_pair {
+ if ctct.has_any_active_contact() {
+ events.handle_contact_event(ContactEvent::Stopped(
+ pair.collider1,
+ pair.collider2,
+ ))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ pub(crate) fn compute_proximities(
+ &mut self,
+ prediction_distance: f32,
+ bodies: &RigidBodySet,
+ colliders: &ColliderSet,
+ events: &dyn EventHandler,
+ ) {
+ par_iter_mut!(&mut self.proximity_graph.graph.edges).for_each(|edge| {
+ let pair = &mut edge.weight;
+ let co1 = &colliders[pair.pair.collider1];
+ let co2 = &colliders[pair.pair.collider2];
+
+ // FIXME: avoid lookup into bodies.
+ let rb1 = &bodies[co1.parent];
+ let rb2 = &bodies[co2.parent];
+
+ if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
+ {
+ // No need to update this contact because nothing moved.
+ return;
+ }
+
+ let dispatcher = DefaultProximityDispatcher;
+ if pair.detector.is_none() {
+ // We need a redispatch for this detector.
+ // This can happen, e.g., after restoring a snapshot of the narrow-phase.
+ let (detector, workspace) = dispatcher.dispatch(co1.shape(), co2.shape());
+ pair.detector = Some(detector);
+ pair.detector_workspace = workspace;
+ }
+
+ let context = ProximityDetectionContext {
+ dispatcher: &dispatcher,
+ prediction_distance,
+ colliders,
+ pair,
+ };
+
+ context
+ .pair
+ .detector
+ .unwrap()
+ .detect_proximity(context, events);
+ });
+
+ /*
+ // First, group pairs.
+ // NOTE: the transmutes here are OK because the Vec are all cleared
+ // before we leave this method.
+ // We do this in order to avoid reallocating those vecs each time
+ // we compute the contacts. Unsafe is necessary because we can't just
+ // store a Vec<&mut ProximityPair> into the NarrowPhase struct without
+ // polluting the World with lifetimes.
+ let ball_ball_prox: &mut Vec<&mut ProximityPair> =
+ unsafe { std::mem::transmute(&mut self.ball_ball_prox) };
+ let shape_shape_prox: &mut Vec<&mut ProximityPair> =
+ unsafe { std::mem::transmute(&mut self.shape_shape_prox) };
+
+ let bodies = &bodies.bodies;
+
+ // FIXME: don't iterate through all the interactions.
+ for pair in &mut self.proximity_graph.interactions {
+ let co1 = &colliders[pair.pair.collider1];
+ let co2 = &colliders[pair.pair.collider2];
+
+ // FIXME: avoid lookup into bodies.
+ let rb1 = &bodies[co1.parent];
+ let rb2 = &bodies[co2.parent];
+
+ if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
+ {
+ // No need to update this proximity because nothing moved.
+ continue;
+ }
+
+ match (co1.shape(), co2.shape()) {
+ (Shape::Ball(_), Shape::Ball(_)) => ball_ball_prox.push(pair),
+ _ => shape_shape_prox.push(pair),
+ }
+ }
+
+ par_chunks_mut!(ball_ball_prox, SIMD_WIDTH).for_each(|pairs| {
+ let context = ProximityDetectionContextSimd {
+ dispatcher: &DefaultProximityDispatcher,
+ prediction_distance,
+ colliders,
+ pairs,
+ };
+ context.pairs[0]
+ .detector
+ .detect_proximity_simd(context, events);
+ });
+
+ par_iter_mut!(shape_shape_prox).for_each(|pair| {
+ let context = ProximityDetectionContext {
+ dispatcher: &DefaultProximityDispatcher,
+ prediction_distance,
+ colliders,
+ pair,
+ };
+
+ context.pair.detector.detect_proximity(context, events);
+ });
+
+ ball_ball_prox.clear();
+ shape_shape_prox.clear();
+ */
+ }
+
+ pub(crate) fn compute_contacts(
+ &mut self,
+ prediction_distance: f32,
+ bodies: &RigidBodySet,
+ colliders: &ColliderSet,
+ events: &dyn EventHandler,
+ ) {
+ par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| {
+ let pair = &mut edge.weight;
+ let co1 = &colliders[pair.pair.collider1];
+ let co2 = &colliders[pair.pair.collider2];
+
+ // FIXME: avoid lookup into bodies.
+ let rb1 = &bodies[co1.parent];
+ let rb2 = &bodies[co2.parent];
+
+ if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
+ {
+ // No need to update this contact because nothing moved.
+ return;
+ }
+
+ let dispatcher = DefaultContactDispatcher;
+ if pair.generator.is_none() {
+ // We need a redispatch for this generator.
+ // This can happen, e.g., after restoring a snapshot of the narrow-phase.
+ let (generator, workspace) = dispatcher.dispatch(co1.shape(), co2.shape());
+ pair.generator = Some(generator);
+ pair.generator_workspace = workspace;
+ }
+
+ let context = ContactGenerationContext {
+ dispatcher: &dispatcher,
+ prediction_distance,
+ colliders,
+ pair,
+ };
+
+ context
+ .pair
+ .generator
+ .unwrap()
+ .generate_contacts(context, events);
+ });
+
+ /*
+ // First, group pairs.
+ // NOTE: the transmutes here are OK because the Vec are all cleared
+ // before we leave this method.
+ // We do this in order to avoid reallocating those vecs each time
+ // we compute the contacts. Unsafe is necessary because we can't just
+ // store a Vec<&mut ContactPair> into the NarrowPhase struct without
+ // polluting the World with lifetimes.
+ let ball_ball: &mut Vec<&mut ContactPair> =
+ unsafe { std::mem::transmute(&mut self.ball_ball) };
+ let shape_shape: &mut Vec<&mut ContactPair> =
+ unsafe { std::mem::transmute(&mut self.shape_shape) };
+
+ let bodies = &bodies.bodies;
+
+ // FIXME: don't iterate through all the interactions.
+ for pair in &mut self.contact_graph.interactions {
+ let co1 = &colliders[pair.pair.collider1];
+ let co2 = &colliders[pair.pair.collider2];
+
+ // FIXME: avoid lookup into bodies.
+ let rb1 = &bodies[co1.parent];
+ let rb2 = &bodies[co2.parent];
+
+ if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
+ {
+ // No need to update this contact because nothing moved.
+ continue;
+ }
+
+ match (co1.shape(), co2.shape()) {
+ (Shape::Ball(_), Shape::Ball(_)) => ball_ball.push(pair),
+ _ => shape_shape.push(pair),
+ }
+ }
+
+ par_chunks_mut!(ball_ball, SIMD_WIDTH).for_each(|pairs| {
+ let context = ContactGenerationContextSimd {
+ dispatcher: &DefaultContactDispatcher,
+ prediction_distance,
+ colliders,
+ pairs,
+ };
+ context.pairs[0]
+ .generator
+ .generate_contacts_simd(context, events);
+ });
+
+ par_iter_mut!(shape_shape).for_each(|pair| {
+ let context = ContactGenerationContext {
+ dispatcher: &DefaultContactDispatcher,
+ prediction_distance,
+ colliders,
+ pair,
+ };
+
+ context.pair.generator.generate_contacts(context, events);
+ });
+
+ ball_ball.clear();
+ shape_shape.clear();
+ */
+ }
+
+ /// Retrieve all the interactions with at least one contact point, happening between two active bodies.
+ // NOTE: this is very similar to the code from JointSet::select_active_interactions.
+ pub(crate) fn sort_and_select_active_contacts<'a>(
+ &'a mut self,
+ bodies: &RigidBodySet,
+ out_manifolds: &mut Vec<&'a mut ContactManifold>,
+ out: &mut Vec<Vec<ContactManifoldIndex>>,
+ ) {
+ for out_island in &mut out[..bodies.num_islands()] {
+ out_island.clear();
+ }
+
+ // FIXME: don't iterate through all the interactions.
+ for inter in self.contact_graph.graph.edges.iter_mut() {
+ for manifold in &mut inter.weight.manifolds {
+ let rb1 = &bodies[manifold.body_pair.body1];
+ let rb2 = &bodies[manifold.body_pair.body2];
+ if manifold.num_active_contacts() != 0
+ && (!rb1.is_dynamic() || !rb1.is_sleeping())
+ && (!rb2.is_dynamic() || !rb2.is_sleeping())
+ {
+ let island_index = if !rb1.is_dynamic() {
+ rb2.active_island_id
+ } else {
+ rb1.active_island_id
+ };
+
+ out[island_index].push(out_manifolds.len());
+ out_manifolds.push(manifold);
+ }
+ }
+ }
+ }
+}