diff options
| author | Crozet Sébastien <developer@crozet.re> | 2021-03-29 14:54:54 +0200 |
|---|---|---|
| committer | Crozet Sébastien <developer@crozet.re> | 2021-03-29 14:54:54 +0200 |
| commit | 8173e7ada2e3f5c99de53b532adc085a26c1cefd (patch) | |
| tree | fbee80982c2245c3e97036b683b00678e6d14a33 /src/geometry/narrow_phase.rs | |
| parent | dec3e4197f3f8b47baedb28ddec976a846e7d099 (diff) | |
| download | rapier-8173e7ada2e3f5c99de53b532adc085a26c1cefd.tar.gz rapier-8173e7ada2e3f5c99de53b532adc085a26c1cefd.tar.bz2 rapier-8173e7ada2e3f5c99de53b532adc085a26c1cefd.zip | |
Allow collider modification after its insersion to the ColliderSet.
Diffstat (limited to 'src/geometry/narrow_phase.rs')
| -rw-r--r-- | src/geometry/narrow_phase.rs | 391 |
1 files changed, 258 insertions, 133 deletions
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index 7de0b26..de199ec 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -4,9 +4,10 @@ use rayon::prelude::*; use crate::data::pubsub::Subscription; use crate::data::Coarena; use crate::dynamics::{BodyPair, CoefficientCombineRule, RigidBodySet}; +use crate::geometry::collider::ColliderChanges; use crate::geometry::{ - BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ColliderSet, ContactData, - ContactEvent, ContactManifold, ContactManifoldData, ContactPair, InteractionGraph, + BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ColliderPair, ColliderSet, + ContactData, ContactEvent, ContactManifold, ContactManifoldData, ContactPair, InteractionGraph, IntersectionEvent, RemovedCollider, SolverContact, SolverFlags, }; use crate::math::{Real, Vector}; @@ -34,6 +35,13 @@ impl ColliderGraphIndices { } } +#[derive(Copy, Clone, PartialEq, Eq)] +enum PairRemovalMode { + FromContactGraph, + FromIntersectionGraph, + Auto, +} + /// The narrow-phase responsible for computing precise contact information between colliders. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] @@ -164,7 +172,12 @@ impl NarrowPhase { // } /// Maintain the narrow-phase internal state by taking collider removal into account. - pub fn maintain(&mut self, colliders: &mut ColliderSet, bodies: &mut RigidBodySet) { + pub fn handle_user_changes( + &mut self, + colliders: &mut ColliderSet, + bodies: &mut RigidBodySet, + events: &dyn EventHandler, + ) { // Ensure we already subscribed. if self.removed_colliders.is_none() { self.removed_colliders = Some(colliders.removed_colliders.subscribe()); @@ -207,6 +220,8 @@ impl NarrowPhase { colliders.removed_colliders.ack(&cursor); self.removed_colliders = Some(cursor); + + self.handle_modified_colliders(colliders, bodies, events); } pub(crate) fn remove_collider( @@ -248,6 +263,212 @@ impl NarrowPhase { } } + pub(crate) fn handle_modified_colliders( + &mut self, + colliders: &mut ColliderSet, + bodies: &mut RigidBodySet, + events: &dyn EventHandler, + ) { + let mut pairs_to_remove = vec![]; + + colliders.foreach_modified_colliders(|handle, collider| { + if collider.changes.needs_narrow_phase_update() { + // No flag relevant to the narrow-phase is enabled for this collider. + return; + } + + if let Some(gid) = self.graph_indices.get(handle.0) { + // For each modified colliders, we need to wake-up the bodies it is in contact with + // so that the narrow-phase properly takes into account the change in, e.g., + // collision groups. Waking up the modified collider's parent isn't enough because + // it could be a static or kinematic body which don't propagate the wake-up state. + bodies.wake_up(collider.parent, true); + + for inter in self + .contact_graph + .interactions_with(gid.contact_graph_index) + { + let other_handle = if handle == inter.0 { inter.1 } else { inter.0 }; + if let Some(other_collider) = colliders.get(other_handle) { + bodies.wake_up(other_collider.parent, true); + } + } + + // For each collider which had their sensor status modified, we need + // to transfer their contact/intersection graph edges to the intersection/contact graph. + // To achieve this we will remove the relevant contact/intersection pairs form the + // contact/intersection graphs, and then add them into the other graph. + if collider.changes.contains(ColliderChanges::SENSOR) { + if collider.is_sensor() { + // Find the contact pairs for this collider and + // push them to `pairs_to_remove`. + for inter in self + .contact_graph + .interactions_with(gid.contact_graph_index) + { + pairs_to_remove.push(( + ColliderPair::new(inter.0, inter.1), + PairRemovalMode::FromContactGraph, + )); + } + } else { + // Find the contact pairs for this collider and + // push them to `pairs_to_remove` if both involved + // colliders are not sensors. + for inter in self + .intersection_graph + .interactions_with(gid.intersection_graph_index) + .filter(|(h1, h2, _)| { + !colliders[*h1].is_sensor() && !colliders[*h2].is_sensor() + }) + { + pairs_to_remove.push(( + ColliderPair::new(inter.0, inter.1), + PairRemovalMode::FromIntersectionGraph, + )); + } + } + } + } + }); + + // Remove the pair from the relevant graph. + for pair in &pairs_to_remove { + self.remove_pair(colliders, bodies, &pair.0, events, pair.1); + } + + // Add the paid removed pair to the relevant graph. + for pair in pairs_to_remove { + self.add_pair(colliders, &pair.0); + } + } + + fn remove_pair( + &mut self, + colliders: &mut ColliderSet, + bodies: &mut RigidBodySet, + pair: &ColliderPair, + events: &dyn EventHandler, + mode: PairRemovalMode, + ) { + if let (Some(co1), Some(co2)) = + (colliders.get(pair.collider1), colliders.get(pair.collider2)) + { + // TODO: could we just unwrap here? + // Don't we have the guarantee that we will get a `AddPair` before a `DeletePair`? + if let (Some(gid1), Some(gid2)) = ( + self.graph_indices.get(pair.collider1.0), + self.graph_indices.get(pair.collider2.0), + ) { + if mode == PairRemovalMode::FromIntersectionGraph + || (mode == PairRemovalMode::Auto && (co1.is_sensor() || co2.is_sensor())) + { + let was_intersecting = self + .intersection_graph + .remove_edge(gid1.intersection_graph_index, gid2.intersection_graph_index); + + // Emit an intersection lost event if we had an intersection before removing the edge. + if Some(true) == was_intersecting { + let prox_event = + IntersectionEvent::new(pair.collider1, pair.collider2, false); + events.handle_intersection_event(prox_event) + } + } else { + let contact_pair = self + .contact_graph + .remove_edge(gid1.contact_graph_index, gid2.contact_graph_index); + + // Emit a contact stopped event if we had a contact before removing the edge. + // Also wake up the dynamic bodies that were in contact. + if let Some(ctct) = contact_pair { + if ctct.has_any_active_contact { + bodies.wake_up(co1.parent, true); + bodies.wake_up(co2.parent, true); + + events.handle_contact_event(ContactEvent::Stopped( + pair.collider1, + pair.collider2, + )) + } + } + } + } + } + } + + fn add_pair(&mut self, colliders: &mut ColliderSet, pair: &ColliderPair) { + if let (Some(co1), Some(co2)) = + (colliders.get(pair.collider1), colliders.get(pair.collider2)) + { + if co1.parent == co2.parent { + // Same parents. Ignore collisions. + return; + } + + let (gid1, gid2) = self.graph_indices.ensure_pair_exists( + pair.collider1.0, + pair.collider2.0, + ColliderGraphIndices::invalid(), + ); + + if co1.is_sensor() || co2.is_sensor() { + // NOTE: the collider won't have a graph index as long + // as it does not interact with anything. + if !InteractionGraph::<(), ()>::is_graph_index_valid(gid1.intersection_graph_index) + { + gid1.intersection_graph_index = + self.intersection_graph.graph.add_node(pair.collider1); + } + + if !InteractionGraph::<(), ()>::is_graph_index_valid(gid2.intersection_graph_index) + { + gid2.intersection_graph_index = + self.intersection_graph.graph.add_node(pair.collider2); + } + + if self + .intersection_graph + .graph + .find_edge(gid1.intersection_graph_index, gid2.intersection_graph_index) + .is_none() + { + let _ = self.intersection_graph.add_edge( + gid1.intersection_graph_index, + gid2.intersection_graph_index, + false, + ); + } + } else { + // NOTE: same code as above, but for the contact graph. + // TODO: refactor both pieces of code somehow? + + // NOTE: the collider won't have a graph index as long + // as it does not interact with anything. + if !InteractionGraph::<(), ()>::is_graph_index_valid(gid1.contact_graph_index) { + gid1.contact_graph_index = self.contact_graph.graph.add_node(pair.collider1); + } + + if !InteractionGraph::<(), ()>::is_graph_index_valid(gid2.contact_graph_index) { + gid2.contact_graph_index = self.contact_graph.graph.add_node(pair.collider2); + } + + if self + .contact_graph + .graph + .find_edge(gid1.contact_graph_index, gid2.contact_graph_index) + .is_none() + { + let interaction = ContactPair::new(*pair); + let _ = self.contact_graph.add_edge( + gid1.contact_graph_index, + gid2.contact_graph_index, + interaction, + ); + } + } + } + } + pub(crate) fn register_pairs( &mut self, colliders: &mut ColliderSet, @@ -258,135 +479,10 @@ impl NarrowPhase { for event in broad_phase_events { match event { BroadPhasePairEvent::AddPair(pair) => { - if let (Some(co1), Some(co2)) = - (colliders.get(pair.collider1), colliders.get(pair.collider2)) - { - if co1.parent == co2.parent { - // Same parents. Ignore collisions. - continue; - } - - let (gid1, gid2) = self.graph_indices.ensure_pair_exists( - pair.collider1.0, - pair.collider2.0, - ColliderGraphIndices::invalid(), - ); - - if co1.is_sensor() || co2.is_sensor() { - // NOTE: the collider won't have a graph index as long - // as it does not interact with anything. - if !InteractionGraph::<(), ()>::is_graph_index_valid( - gid1.intersection_graph_index, - ) { - gid1.intersection_graph_index = - self.intersection_graph.graph.add_node(pair.collider1); - } - - if !InteractionGraph::<(), ()>::is_graph_index_valid( - gid2.intersection_graph_index, - ) { - gid2.intersection_graph_index = - self.intersection_graph.graph.add_node(pair.collider2); - } - - if self - .intersection_graph - .graph - .find_edge( - gid1.intersection_graph_index, - gid2.intersection_graph_index, - ) - .is_none() - { - let _ = self.intersection_graph.add_edge( - gid1.intersection_graph_index, - gid2.intersection_graph_index, - false, - ); - } - } else { - // NOTE: same code as above, but for the contact graph. - // TODO: refactor both pieces of code somehow? - - // NOTE: the collider won't have a graph index as long - // as it does not interact with anything. - if !InteractionGraph::<(), ()>::is_graph_index_valid( - gid1.contact_graph_index, - ) { - gid1.contact_graph_index = - self.contact_graph.graph.add_node(pair.collider1); - } - - if !InteractionGraph::<(), ()>::is_graph_index_valid( - gid2.contact_graph_index, - ) { - gid2.contact_graph_index = - self.contact_graph.graph.add_node(pair.collider2); - } - - if self - .contact_graph - .graph - .find_edge(gid1.contact_graph_index, gid2.contact_graph_index) - .is_none() - { - let interaction = ContactPair::new(*pair); - let _ = self.contact_graph.add_edge( - gid1.contact_graph_index, - gid2.contact_graph_index, - interaction, - ); - } - } - } + self.add_pair(colliders, pair); } BroadPhasePairEvent::DeletePair(pair) => { - if let (Some(co1), Some(co2)) = - (colliders.get(pair.collider1), colliders.get(pair.collider2)) - { - // TODO: could we just unwrap here? - // Don't we have the guarantee that we will get a `AddPair` before a `DeletePair`? - if let (Some(gid1), Some(gid2)) = ( - self.graph_indices.get(pair.collider1.0), - self.graph_indices.get(pair.collider2.0), - ) { - if co1.is_sensor() || co2.is_sensor() { - let was_intersecting = self.intersection_graph.remove_edge( - gid1.intersection_graph_index, - gid2.intersection_graph_index, - ); - - // Emit an intersection lost event if we had an intersection before removing the edge. - if Some(true) == was_intersecting { - let prox_event = IntersectionEvent::new( - pair.collider1, - pair.collider2, - false, - ); - events.handle_intersection_event(prox_event) - } - } else { - let contact_pair = self.contact_graph.remove_edge( - gid1.contact_graph_index, - gid2.contact_graph_index, - ); - - // Emit a contact stopped event if we had a contact before removing the edge. - // Also wake up the dynamic bodies that were in contact. - if let Some(ctct) = contact_pair { - if ctct.has_any_active_contact { - bodies.wake_up(co1.parent, true); - bodies.wake_up(co2.parent, true); - - events.handle_contact_event(ContactEvent::Stopped( - pair.collider1, - pair.collider2, - )) - } - } - } - } - } + self.remove_pair(colliders, bodies, pair, events, PairRemovalMode::Auto); } } } @@ -399,17 +495,28 @@ impl NarrowPhase { hooks: &dyn PhysicsHooks, events: &dyn EventHandler, ) { + if !colliders.contains_any_modified_collider() { + return; + } + let nodes = &self.intersection_graph.graph.nodes; let query_dispatcher = &*self.query_dispatcher; let active_hooks = hooks.active_hooks(); + // TODO: don't iterate on all the edges. par_iter_mut!(&mut self.intersection_graph.graph.edges).for_each(|edge| { let handle1 = nodes[edge.source().index()].weight; let handle2 = nodes[edge.target().index()].weight; let co1 = &colliders[handle1]; let co2 = &colliders[handle2]; - // FIXME: avoid lookup into bodies. + if !co1.changes.needs_narrow_phase_update() && !co2.changes.needs_narrow_phase_update() + { + // No update needed for these colliders. + return; + } + + // TODO: avoid lookup into bodies. let rb1 = &bodies[co1.parent]; let rb2 = &bodies[co2.parent]; @@ -475,15 +582,26 @@ impl NarrowPhase { hooks: &dyn PhysicsHooks, events: &dyn EventHandler, ) { + if !colliders.contains_any_modified_collider() { + return; + } + let query_dispatcher = &*self.query_dispatcher; let active_hooks = hooks.active_hooks(); + // TODO: don't iterate on all the edges. 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. + if !co1.changes.needs_narrow_phase_update() && !co2.changes.needs_narrow_phase_update() + { + // No update needed for these colliders. + return; + } + + // TODO: avoid lookup into bodies. let rb1 = &bodies[co1.parent]; let rb2 = &bodies[co2.parent]; @@ -533,6 +651,13 @@ impl NarrowPhase { solver_flags.remove(SolverFlags::COMPUTE_IMPULSES); } + if co1.changes.contains(ColliderChanges::SHAPE) + || co2.changes.contains(ColliderChanges::SHAPE) + { + // The shape changed so the workspace is no longer valid. + pair.workspace = None; + } + let pos12 = co1.position().inv_mul(co2.position()); let _ = query_dispatcher.contact_manifolds( &pos12, @@ -657,7 +782,7 @@ impl NarrowPhase { out_island.clear(); } - // FIXME: don't iterate through all the interactions. + // TODO: 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.data.body_pair.body1]; |
