diff options
| author | Sébastien Crozet <developer@crozet.re> | 2021-01-29 14:42:32 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-29 14:42:32 +0100 |
| commit | 7ca46f38cde6cf8bf8bf41ea6067ae5bc938205c (patch) | |
| tree | 3781b9d7c92a6a8111573ba4cae1c5d41435950e /src/geometry/narrow_phase.rs | |
| parent | e6fc8f67faf3e37afe38d683cbd930d457f289be (diff) | |
| parent | 825f33efaec4ce6a8903751e836a0ea9c466ff92 (diff) | |
| download | rapier-7ca46f38cde6cf8bf8bf41ea6067ae5bc938205c.tar.gz rapier-7ca46f38cde6cf8bf8bf41ea6067ae5bc938205c.tar.bz2 rapier-7ca46f38cde6cf8bf8bf41ea6067ae5bc938205c.zip | |
Merge pull request #79 from dimforge/split_geom
Move most of the geometric code to another crate.
Diffstat (limited to 'src/geometry/narrow_phase.rs')
| -rw-r--r-- | src/geometry/narrow_phase.rs | 399 |
1 files changed, 221 insertions, 178 deletions
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index a21d3c6..c462689 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -1,45 +1,34 @@ #[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::data::pubsub::Subscription; +use crate::data::Coarena; +use crate::dynamics::{BodyPair, CoefficientCombineRule, RigidBodySet}; use crate::geometry::{ - BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ContactPairFilter, - PairFilterContext, ProximityEvent, ProximityPair, ProximityPairFilter, RemovedCollider, - SolverFlags, + BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactData, ContactEvent, + ContactManifoldData, ContactPairFilter, IntersectionEvent, IntersectionPairFilter, + PairFilterContext, RemovedCollider, SolverContact, SolverFlags, }; 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::data::Coarena; -use crate::ncollide::query::Proximity; +use crate::math::{Real, Vector}; use crate::pipeline::EventHandler; +use parry::query::{DefaultQueryDispatcher, PersistentQueryDispatcher}; +use parry::utils::IsometryOpt; use std::collections::HashMap; -//use simba::simd::SimdValue; +use std::sync::Arc; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] struct ColliderGraphIndices { contact_graph_index: ColliderGraphIndex, - proximity_graph_index: ColliderGraphIndex, + intersection_graph_index: ColliderGraphIndex, } impl ColliderGraphIndices { fn invalid() -> Self { Self { - contact_graph_index: InteractionGraph::<ContactPair>::invalid_graph_index(), - proximity_graph_index: InteractionGraph::<ProximityPair>::invalid_graph_index(), + contact_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(), + intersection_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(), } } } @@ -48,14 +37,15 @@ impl ColliderGraphIndices { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] pub struct NarrowPhase { - contact_graph: InteractionGraph<ContactPair>, - proximity_graph: InteractionGraph<ProximityPair>, + #[cfg_attr( + feature = "serde-serialize", + serde(skip, default = "crate::geometry::default_persistent_query_dispatcher") + )] + query_dispatcher: Arc<dyn PersistentQueryDispatcher<ContactManifoldData, ContactData>>, + contact_graph: InteractionGraph<ColliderHandle, ContactPair>, + intersection_graph: InteractionGraph<ColliderHandle, bool>, graph_indices: Coarena<ColliderGraphIndices>, 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>, - // shape_shape_prox: Vec<usize>, // Workspace: Vec<*mut ProximityPair>, } pub(crate) type ContactManifoldIndex = usize; @@ -63,26 +53,31 @@ pub(crate) type ContactManifoldIndex = usize; impl NarrowPhase { /// Creates a new empty narrow-phase. pub fn new() -> Self { + Self::with_query_dispatcher(DefaultQueryDispatcher) + } + + /// Creates a new empty narrow-phase with a custom query dispatcher. + pub fn with_query_dispatcher<D>(d: D) -> Self + where + D: 'static + PersistentQueryDispatcher<ContactManifoldData, ContactData>, + { Self { + query_dispatcher: Arc::new(d), contact_graph: InteractionGraph::new(), - proximity_graph: InteractionGraph::new(), + intersection_graph: InteractionGraph::new(), graph_indices: Coarena::new(), removed_colliders: None, - // 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> { + pub fn contact_graph(&self) -> &InteractionGraph<ColliderHandle, 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 + /// The intersection graph containing all intersection pairs and their intersection information. + pub fn intersection_graph(&self) -> &InteractionGraph<ColliderHandle, bool> { + &self.intersection_graph } /// All the contacts involving the given collider. @@ -90,19 +85,20 @@ impl NarrowPhase { &self, collider: ColliderHandle, ) -> Option<impl Iterator<Item = (ColliderHandle, ColliderHandle, &ContactPair)>> { - let id = self.graph_indices.get(collider)?; + let id = self.graph_indices.get(collider.0)?; Some(self.contact_graph.interactions_with(id.contact_graph_index)) } - /// All the proximities involving the given collider. - pub fn proximities_with( - &self, + /// All the intersections involving the given collider. + pub fn intersections_with<'a>( + &'a self, collider: ColliderHandle, - ) -> Option<impl Iterator<Item = (ColliderHandle, ColliderHandle, &ProximityPair)>> { - let id = self.graph_indices.get(collider)?; + ) -> Option<impl Iterator<Item = (ColliderHandle, ColliderHandle, bool)> + 'a> { + let id = self.graph_indices.get(collider.0)?; Some( - self.proximity_graph - .interactions_with(id.proximity_graph_index), + self.intersection_graph + .interactions_with(id.intersection_graph_index) + .map(|e| (e.0, e.1, *e.2)), ) } @@ -116,28 +112,27 @@ impl NarrowPhase { collider1: ColliderHandle, collider2: ColliderHandle, ) -> Option<&ContactPair> { - let id1 = self.graph_indices.get(collider1)?; - let id2 = self.graph_indices.get(collider2)?; + let id1 = self.graph_indices.get(collider1.0)?; + let id2 = self.graph_indices.get(collider2.0)?; self.contact_graph .interaction_pair(id1.contact_graph_index, id2.contact_graph_index) .map(|c| c.2) } - /// The proximity pair involving two specific colliders. + /// The intersection pair involving two specific colliders. /// - /// If this returns `None`, there is no intersection between the two colliders. - /// If this returns `Some`, then there may be an intersection between the two colliders. Check the - /// value of [`ProximityPair::proximity`] method to see if there is an actual intersection. - pub fn proximity_pair( + /// If this returns `None` or `Some(false)`, then there is no intersection between the two colliders. + /// If this returns `Some(true)`, then there may be an intersection between the two colliders. + pub fn intersection_pair( &self, collider1: ColliderHandle, collider2: ColliderHandle, - ) -> Option<&ProximityPair> { - let id1 = self.graph_indices.get(collider1)?; - let id2 = self.graph_indices.get(collider2)?; - self.proximity_graph - .interaction_pair(id1.proximity_graph_index, id2.proximity_graph_index) - .map(|c| c.2) + ) -> Option<bool> { + let id1 = self.graph_indices.get(collider1.0)?; + let id2 = self.graph_indices.get(collider2.0)?; + self.intersection_graph + .interaction_pair(id1.intersection_graph_index, id2.intersection_graph_index) + .map(|c| *c.2) } /// All the contact pairs maintained by this narrow-phase. @@ -145,9 +140,13 @@ impl NarrowPhase { self.contact_graph.interactions() } - /// All the proximity pairs maintained by this narrow-phase. - pub fn proximity_pairs(&self) -> impl Iterator<Item = &ProximityPair> { - self.proximity_graph.interactions() + /// All the intersection pairs maintained by this narrow-phase. + pub fn intersection_pairs<'a>( + &'a self, + ) -> impl Iterator<Item = (ColliderHandle, ColliderHandle, bool)> + 'a { + self.intersection_graph + .interactions_with_endpoints() + .map(|e| (e.0, e.1, *e.2)) } // #[cfg(feature = "parallel")] @@ -166,7 +165,7 @@ impl NarrowPhase { // 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. + // by the contact/intersection graphs when a node is removed. let mut prox_id_remap = HashMap::new(); let mut contact_id_remap = HashMap::new(); let mut i = 0; @@ -174,18 +173,18 @@ impl NarrowPhase { while let Some(collider) = colliders.removed_colliders.read_ith(&cursor, i) { // NOTE: if the collider does not have any graph indices currently, there is nothing // to remove in the narrow-phase for this collider. - if let Some(graph_idx) = self.graph_indices.get(collider.handle) { - let proximity_graph_id = prox_id_remap + if let Some(graph_idx) = self.graph_indices.get(collider.handle.0) { + let intersection_graph_id = prox_id_remap .get(&collider.handle) .copied() - .unwrap_or(graph_idx.proximity_graph_index); + .unwrap_or(graph_idx.intersection_graph_index); let contact_graph_id = contact_id_remap .get(&collider.handle) .copied() .unwrap_or(graph_idx.contact_graph_index); self.remove_collider( - proximity_graph_id, + intersection_graph_id, contact_graph_id, colliders, bodies, @@ -203,7 +202,7 @@ impl NarrowPhase { pub(crate) fn remove_collider<'a>( &mut self, - proximity_graph_id: ColliderGraphIndex, + intersection_graph_id: ColliderGraphIndex, contact_graph_id: ColliderGraphIndex, colliders: &mut ColliderSet, bodies: &mut RigidBodySet, @@ -223,16 +222,16 @@ impl NarrowPhase { // We have to manage the fact that one other collider will // have its graph index changed because of the node's swap-remove. - if let Some(replacement) = self.proximity_graph.remove_node(proximity_graph_id) { - if let Some(replacement) = self.graph_indices.get_mut(replacement) { - replacement.proximity_graph_index = proximity_graph_id; + if let Some(replacement) = self.intersection_graph.remove_node(intersection_graph_id) { + if let Some(replacement) = self.graph_indices.get_mut(replacement.0) { + replacement.intersection_graph_index = intersection_graph_id; } else { - prox_id_remap.insert(replacement, proximity_graph_id); + prox_id_remap.insert(replacement, intersection_graph_id); } } if let Some(replacement) = self.contact_graph.remove_node(contact_graph_id) { - if let Some(replacement) = self.graph_indices.get_mut(replacement) { + if let Some(replacement) = self.graph_indices.get_mut(replacement.0) { replacement.contact_graph_index = contact_graph_id; } else { contact_id_remap.insert(replacement, contact_graph_id); @@ -259,43 +258,41 @@ impl NarrowPhase { } let (gid1, gid2) = self.graph_indices.ensure_pair_exists( - pair.collider1, - pair.collider2, + 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::<ProximityPair>::is_graph_index_valid( - gid1.proximity_graph_index, + if !InteractionGraph::<(), ()>::is_graph_index_valid( + gid1.intersection_graph_index, ) { - gid1.proximity_graph_index = - self.proximity_graph.graph.add_node(pair.collider1); + gid1.intersection_graph_index = + self.intersection_graph.graph.add_node(pair.collider1); } - if !InteractionGraph::<ProximityPair>::is_graph_index_valid( - gid2.proximity_graph_index, + if !InteractionGraph::<(), ()>::is_graph_index_valid( + gid2.intersection_graph_index, ) { - gid2.proximity_graph_index = - self.proximity_graph.graph.add_node(pair.collider2); + gid2.intersection_graph_index = + self.intersection_graph.graph.add_node(pair.collider2); } if self - .proximity_graph + .intersection_graph .graph - .find_edge(gid1.proximity_graph_index, gid2.proximity_graph_index) + .find_edge( + gid1.intersection_graph_index, + gid2.intersection_graph_index, + ) .is_none() { - let dispatcher = DefaultProximityDispatcher; - let generator = dispatcher - .dispatch(co1.shape().shape_type(), co2.shape().shape_type()); - let interaction = - ProximityPair::new(*pair, generator.0, generator.1); - let _ = self.proximity_graph.add_edge( - gid1.proximity_graph_index, - gid2.proximity_graph_index, - interaction, + let _ = self.intersection_graph.add_edge( + gid1.intersection_graph_index, + gid2.intersection_graph_index, + false, ); } } else { @@ -304,14 +301,14 @@ impl NarrowPhase { // 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( + if !InteractionGraph::<(), ()>::is_graph_index_valid( gid1.contact_graph_index, ) { gid1.contact_graph_index = self.contact_graph.graph.add_node(pair.collider1); } - if !InteractionGraph::<ContactPair>::is_graph_index_valid( + if !InteractionGraph::<(), ()>::is_graph_index_valid( gid2.contact_graph_index, ) { gid2.contact_graph_index = @@ -324,10 +321,7 @@ impl NarrowPhase { .find_edge(gid1.contact_graph_index, gid2.contact_graph_index) .is_none() { - let dispatcher = DefaultContactDispatcher; - let generator = dispatcher - .dispatch(co1.shape().shape_type(), co2.shape().shape_type()); - let interaction = ContactPair::new(*pair, generator.0, generator.1); + let interaction = ContactPair::new(*pair); let _ = self.contact_graph.add_edge( gid1.contact_graph_index, gid2.contact_graph_index, @@ -344,26 +338,23 @@ impl NarrowPhase { // 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), - self.graph_indices.get(pair.collider2), + self.graph_indices.get(pair.collider1.0), + self.graph_indices.get(pair.collider2.0), ) { if co1.is_sensor() || co2.is_sensor() { - let prox_pair = self.proximity_graph.remove_edge( - gid1.proximity_graph_index, - gid2.proximity_graph_index, + let was_intersecting = self.intersection_graph.remove_edge( + gid1.intersection_graph_index, + gid2.intersection_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) - } + // 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( @@ -374,7 +365,7 @@ impl NarrowPhase { // 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() { + if ctct.has_any_active_contact { bodies.wake_up(co1.parent, true); bodies.wake_up(co2.parent, true); @@ -392,18 +383,20 @@ impl NarrowPhase { } } - pub(crate) fn compute_proximities( + pub(crate) fn compute_intersections( &mut self, - prediction_distance: f32, bodies: &RigidBodySet, colliders: &ColliderSet, - pair_filter: Option<&dyn ProximityPairFilter>, + pair_filter: Option<&dyn IntersectionPairFilter>, 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]; + let nodes = &self.intersection_graph.graph.nodes; + let query_dispatcher = &*self.query_dispatcher; + 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. let rb1 = &bodies[co1.parent]; @@ -413,17 +406,17 @@ impl NarrowPhase { || (rb2.is_sleeping() && rb1.is_static()) || (rb1.is_sleeping() && rb2.is_sleeping()) { - // No need to update this proximity because nothing moved. + // No need to update this intersection because nothing moved. return; } if !co1.collision_groups.test(co2.collision_groups) { - // The proximity is not allowed. + // The intersection is not allowed. return; } if pair_filter.is_none() && !rb1.is_dynamic() && !rb2.is_dynamic() { - // Default filtering rule: no proximity between two non-dynamic bodies. + // Default filtering rule: no intersection between two non-dynamic bodies. return; } @@ -435,45 +428,39 @@ impl NarrowPhase { collider2: co2, }; - if !filter.filter_proximity_pair(&context) { - // No proximity allowed. + if !filter.filter_intersection_pair(&context) { + // No intersection allowed. 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().shape_type(), co2.shape().shape_type()); - pair.detector = Some(detector); - pair.detector_workspace = workspace; - } - - let context = ProximityDetectionContext { - dispatcher: &dispatcher, - prediction_distance, - colliders, - pair, - }; + let pos12 = co1.position().inv_mul(co2.position()); - context - .pair - .detector - .unwrap() - .detect_proximity(context, events); + if let Ok(intersection) = + query_dispatcher.intersection_test(&pos12, co1.shape(), co2.shape()) + { + if intersection != edge.weight { + edge.weight = intersection; + events.handle_intersection_event(IntersectionEvent::new( + handle1, + handle2, + intersection, + )); + } + } }); } pub(crate) fn compute_contacts( &mut self, - prediction_distance: f32, + prediction_distance: Real, bodies: &RigidBodySet, colliders: &ColliderSet, pair_filter: Option<&dyn ContactPairFilter>, events: &dyn EventHandler, ) { + let query_dispatcher = &*self.query_dispatcher; + par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| { let pair = &mut edge.weight; let co1 = &colliders[pair.pair.collider1]; @@ -523,33 +510,88 @@ impl NarrowPhase { solver_flags.remove(SolverFlags::COMPUTE_IMPULSES); } - 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().shape_type(), co2.shape().shape_type()); - pair.generator = Some(generator); - - // Keep the workspace if one already exists. - if pair.generator_workspace.is_none() { - pair.generator_workspace = workspace; + let pos12 = co1.position().inv_mul(co2.position()); + let _ = query_dispatcher.contact_manifolds( + &pos12, + co1.shape(), + co2.shape(), + prediction_distance, + &mut pair.manifolds, + &mut pair.workspace, + ); + + let mut has_any_active_contact = false; + + let friction = CoefficientCombineRule::combine( + co1.friction, + co2.friction, + co1.flags.friction_combine_rule_value(), + co2.flags.friction_combine_rule_value(), + ); + let restitution = CoefficientCombineRule::combine( + co1.restitution, + co2.restitution, + co1.flags.restitution_combine_rule_value(), + co2.flags.restitution_combine_rule_value(), + ); + + for manifold in &mut pair.manifolds { + let world_pos1 = manifold.subshape_pos1.prepend_to(co1.position()); + manifold.data.solver_contacts.clear(); + manifold.data.body_pair = BodyPair::new(co1.parent(), co2.parent()); + manifold.data.solver_flags = solver_flags; + manifold.data.normal = world_pos1 * manifold.local_n1; + + // Sort contacts to keep only these with distances bellow + // the prediction, and generate solver contacts. + let mut first_inactive_index = manifold.points.len(); + + while manifold.data.num_active_contacts() != first_inactive_index { + let contact = &manifold.points[manifold.data.num_active_contacts()]; + if contact.dist < prediction_distance { + // Generate the solver contact. + let solver_contact = SolverContact { + point: world_pos1 * contact.local_p1 + + manifold.data.normal * contact.dist / 2.0, + dist: contact.dist, + friction, + restitution, + surface_velocity: Vector::zeros(), + data: contact.data, + }; + + // TODO: apply the user-defined contact modification/removal, if needed. + + manifold.data.solver_contacts.push(solver_contact); + has_any_active_contact = true; + continue; + } + + // If we reach this code, then the contact must be ignored by the constraints solver. + // Swap with the last contact. + manifold.points.swap( + manifold.data.num_active_contacts(), + first_inactive_index - 1, + ); + first_inactive_index -= 1; } } - let context = ContactGenerationContext { - dispatcher: &dispatcher, - prediction_distance, - colliders, - pair, - solver_flags, - }; + if has_any_active_contact != pair.has_any_active_contact { + if has_any_active_contact { + events.handle_contact_event(ContactEvent::Started( + pair.pair.collider1, + pair.pair.collider2, + )); + } else { + events.handle_contact_event(ContactEvent::Stopped( + pair.pair.collider1, + pair.pair.collider2, + )); + } - context - .pair - .generator - .unwrap() - .generate_contacts(context, events); + pair.has_any_active_contact = has_any_active_contact; + } }); } @@ -568,12 +610,13 @@ impl NarrowPhase { // 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]; + let rb1 = &bodies[manifold.data.body_pair.body1]; + let rb2 = &bodies[manifold.data.body_pair.body2]; if manifold + .data .solver_flags .contains(SolverFlags::COMPUTE_IMPULSES) - && manifold.num_active_contacts() != 0 + && manifold.data.num_active_contacts() != 0 && (rb1.is_dynamic() || rb2.is_dynamic()) && (!rb1.is_dynamic() || !rb1.is_sleeping()) && (!rb2.is_dynamic() || !rb2.is_sleeping()) |
