aboutsummaryrefslogtreecommitdiff
path: root/src/geometry/broad_phase_multi_sap/broad_phase.rs
diff options
context:
space:
mode:
authorSébastien Crozet <sebcrozet@dimforge.com>2024-03-23 13:26:53 +0100
committerSébastien Crozet <sebastien@crozet.re>2024-03-23 15:17:47 +0100
commitcfb2c2c93e39e1f59557c4f32fde4a68dc4cd6fc (patch)
tree3c5f8a0e28e1aed0870e4a33fbcb7814920692ab /src/geometry/broad_phase_multi_sap/broad_phase.rs
parentcd9fb8342d115bad557ca05aa16fb961fe28b1b5 (diff)
downloadrapier-cfb2c2c93e39e1f59557c4f32fde4a68dc4cd6fc.tar.gz
rapier-cfb2c2c93e39e1f59557c4f32fde4a68dc4cd6fc.tar.bz2
rapier-cfb2c2c93e39e1f59557c4f32fde4a68dc4cd6fc.zip
feat!: rename BroadPhase to BroadPhaseMultiSap
Diffstat (limited to 'src/geometry/broad_phase_multi_sap/broad_phase.rs')
-rw-r--r--src/geometry/broad_phase_multi_sap/broad_phase.rs664
1 files changed, 0 insertions, 664 deletions
diff --git a/src/geometry/broad_phase_multi_sap/broad_phase.rs b/src/geometry/broad_phase_multi_sap/broad_phase.rs
deleted file mode 100644
index 9e1bc06..0000000
--- a/src/geometry/broad_phase_multi_sap/broad_phase.rs
+++ /dev/null
@@ -1,664 +0,0 @@
-use super::{
- BroadPhasePairEvent, ColliderPair, SAPLayer, SAPProxies, SAPProxy, SAPProxyData, SAPRegionPool,
-};
-use crate::geometry::broad_phase_multi_sap::SAPProxyIndex;
-use crate::geometry::{
- ColliderBroadPhaseData, ColliderChanges, ColliderHandle, ColliderPosition, ColliderSet,
- ColliderShape,
-};
-use crate::math::Real;
-use crate::utils::IndexMut2;
-use parry::bounding_volume::BoundingVolume;
-use parry::utils::hashmap::HashMap;
-
-/// A broad-phase combining a Hierarchical Grid and Sweep-and-Prune.
-///
-/// The basic Sweep-and-Prune (SAP) algorithm has one significant flaws:
-/// the interactions between far-away objects. This means that objects
-/// that are very far away will still have some of their endpoints swapped
-/// within the SAP data-structure. This results in poor scaling because this
-/// results in lots of swapping between endpoints of Aabbs that won't ever
-/// actually interact.
-///
-/// The first optimization to address this problem is to use the Multi-SAP
-/// method. This basically combines an SAP with a grid. The grid subdivides
-/// the spaces into equally-sized subspaces (grid cells). Each subspace, which we call
-/// a "region" contains an SAP instance (i.e. there SAP axes responsible for
-/// collecting endpoints and swapping them when they move to detect interaction pairs).
-/// Each Aabb is inserted in all the regions it intersects.
-/// This prevents the far-away problem because two objects that are far away will
-/// be located on different regions. So their endpoints will never meet.
-///
-/// However, the Multi-SAP approach has one notable problem: the region size must
-/// be chosen wisely. It could be user-defined, but that's makes it more difficult
-/// to use (for the end-user). Or it can be given a fixed value. Using a fixed
-/// value may result in large objects intersecting lots of regions, resulting in
-/// poor performances and very high memory usage.
-///
-/// So a solution to that large-objects problem is the Multi-SAP approach is to
-/// replace the grid by a hierarchical grid. A hierarchical grid is composed of
-/// several layers. And each layer have different region sizes. For example all
-/// the regions on layer 0 will have the size 1x1x1. All the regions on the layer
-/// 1 will have the size 10x10x10, etc. That way, a given Aabb will be inserted
-/// on the layer that has regions big enough to avoid the large-object problem.
-/// For example a 20x20x20 object will be inserted in the layer with region
-/// of size 10x10x10, resulting in only 8 regions being intersect by the Aabb.
-/// (If it was inserted in the layer with regions of size 1x1x1, it would have intersected
-/// 8000 regions, which is a problem performancewise.)
-///
-/// We call this new method the Hierarchical-SAP.
-///
-/// Now with the Hierarchical-SAP, we can update each layer independently from one another.
-/// However, objects belonging to different layers will never be detected as intersecting that
-/// way. So we need a way to do inter-layer interference detection. There is a lot ways of doing
-/// this: performing inter-layer Multi-Box-Pruning passes is one example (but this is not what we do).
-/// In our implementation, we do the following:
-/// - The Aabb bounds of each region of the layer `n` are inserted into the corresponding larger region
-/// of the layer `n + 1`.
-/// - When an Aabb in the region of the layer `n + 1` intersects the Aabb corresponding to one of the
-/// regions at the smaller layer `n`, we add that Aabb to that smaller region.
-/// So in the end it means that a given Aabb will be inserted into all the region it intersects at
-/// the layer `n`. And it will also be inserted into all the regions it intersects at the smaller layers
-/// (the layers `< n`), but only for the regions that already exist (so we don't have to discretize
-/// our Aabb into the layers `< n`). This involves a fair amount of bookkeeping unfortunately, but
-/// this has the benefit of keep the overall complexity of the algorithm O(1) in the typical specially
-/// coherent scenario.
-///
-/// From an implementation point-of-view, our hierarchical SAP is implemented with the following structures:
-/// - There is one `SAPLayer` per layer of the hierarchical grid.
-/// - Each `SAPLayer` contains multiple `SAPRegion` (each being a region of the grid represented by that layer).
-/// - Each `SAPRegion` contains three `SAPAxis`, representing the "classical" SAP algorithm running on this region.
-/// - Each `SAPAxis` maintains a sorted list of `SAPEndpoints` representing the endpoints of the Aabbs intersecting
-/// the bounds on the `SAPRegion` containing this `SAPAxis`.
-/// - A set of `SAPProxy` are maintained separately. It contains the Aabbs of all the colliders managed by this
-/// broad-phase, as well as the Aabbs of all the regions part of this broad-phase.
-#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
-#[derive(Clone)]
-pub struct BroadPhase {
- proxies: SAPProxies,
- layers: Vec<SAPLayer>,
- smallest_layer: u8,
- largest_layer: u8,
- // NOTE: we maintain this hashmap to simplify collider removal.
- // This information is also present in the ColliderProxyId
- // component. However if that component is removed, we need
- // a way to access it to do some cleanup.
- // Note that we could just remove the ColliderProxyId component
- // altogether but that would be slow because of the need to
- // always access this hashmap. Instead, we access this hashmap
- // only when the collider has been added/removed.
- // Another alternative would be to remove ColliderProxyId and
- // just use a Coarena. But this seems like it could use too
- // much memory.
- colliders_proxy_ids: HashMap<ColliderHandle, SAPProxyIndex>,
- #[cfg_attr(feature = "serde-serialize", serde(skip))]
- region_pool: SAPRegionPool, // To avoid repeated allocations.
- // We could think serializing this workspace is useless.
- // It turns out is is important to serialize at least its capacity
- // and restore this capacity when deserializing the hashmap.
- // This is because the order of future elements inserted into the
- // hashmap depends on its capacity (because the internal bucket indices
- // depend on this capacity). So not restoring this capacity may alter
- // the order at which future elements are reported. This will in turn
- // alter the order at which the pairs are registered in the narrow-phase,
- // thus altering the order of the contact manifold. In the end, this
- // alters the order of the resolution of contacts, resulting in
- // diverging simulation after restoration of a snapshot.
- #[cfg_attr(
- feature = "serde-serialize",
- serde(
- serialize_with = "parry::utils::hashmap::serialize_hashmap_capacity",
- deserialize_with = "parry::utils::hashmap::deserialize_hashmap_capacity"
- )
- )]
- reporting: HashMap<(u32, u32), bool>, // Workspace
-}
-
-impl Default for BroadPhase {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl BroadPhase {
- /// Create a new empty broad-phase.
- pub fn new() -> Self {
- BroadPhase {
- proxies: SAPProxies::new(),
- layers: Vec::new(),
- smallest_layer: 0,
- largest_layer: 0,
- region_pool: Vec::new(),
- reporting: HashMap::default(),
- colliders_proxy_ids: HashMap::default(),
- }
- }
-
- /// Maintain the broad-phase internal state by taking collider removal into account.
- ///
- /// For each colliders marked as removed, we make their containing layer mark
- /// its proxy as pre-deleted. The actual proxy removal will happen at the end
- /// of the `BroadPhase::update`.
- fn handle_removed_colliders(&mut self, removed_colliders: &[ColliderHandle]) {
- // For each removed collider, remove the corresponding proxy.
- for removed in removed_colliders {
- if let Some(proxy_id) = self.colliders_proxy_ids.get(removed).copied() {
- self.predelete_proxy(proxy_id);
- }
- }
- }
-
- /// Pre-deletes a proxy from this broad-phase.
- ///
- /// The removal of a proxy is a semi-lazy process. It will mark
- /// the proxy as predeleted, and will set its Aabb as +infinity.
- /// After this method has been called with all the proxies to
- /// remove, the `complete_removal` method MUST be called to
- /// complete the removal of these proxies, by actually removing them
- /// from all the relevant layers/regions/axes.
- fn predelete_proxy(&mut self, proxy_index: SAPProxyIndex) {
- if proxy_index == crate::INVALID_U32 {
- // This collider has not been added to the broad-phase yet.
- return;
- }
-
- let proxy = &mut self.proxies[proxy_index];
- let layer = &mut self.layers[proxy.layer_id as usize];
- // Let the layer know that the proxy is being deleted.
- layer.predelete_proxy(&mut self.proxies, proxy_index);
- }
-
- /// Completes the removal of the deleted proxies.
- ///
- /// If `self.predelete_proxy` was called, then this `complete_removals`
- /// method must be called to complete the removals.
- ///
- /// This method will actually remove from the proxy list all the proxies
- /// marked as deletable by `self.predelete_proxy`, making their proxy
- /// handles re-usable by new proxies.
- fn complete_removals(
- &mut self,
- colliders: &mut ColliderSet,
- removed_colliders: &[ColliderHandle],
- ) {
- // If there is no layer, there is nothing to remove.
- if self.layers.is_empty() {
- return;
- }
-
- // This is a bottom-up pass:
- // - Complete the removal on the layer `n`. This may cause so regions to be deleted.
- // - Continue with the layer `n + 1`. This will delete from `n + 1` all the proxies
- // of the regions originating from `n`.
- // This bottom-up approach will propagate region removal from the smallest layer up
- // to the largest layer.
- let mut curr_layer_id = self.smallest_layer;
-
- loop {
- let curr_layer = &mut self.layers[curr_layer_id as usize];
-
- if let Some(larger_layer_id) = curr_layer.larger_layer {
- let (curr_layer, larger_layer) = self
- .layers
- .index_mut2(curr_layer_id as usize, larger_layer_id as usize);
- curr_layer.complete_removals(
- Some(larger_layer),
- &mut self.proxies,
- &mut self.region_pool,
- );
-
- // NOTE: we don't care about reporting pairs.
- self.reporting.clear();
- curr_layer_id = larger_layer_id;
- } else {
- curr_layer.complete_removals(None, &mut self.proxies, &mut self.region_pool);
-
- // NOTE: we don't care about reporting pairs.
- self.reporting.clear();
- break;
- }
- }
-
- /*
- * Actually remove the colliders proxies.
- */
- for removed in removed_colliders {
- if let Some(proxy_id) = self.colliders_proxy_ids.remove(removed) {
- if proxy_id != crate::INVALID_U32 {
- self.proxies.remove(proxy_id);
- }
- }
-
- if let Some(co) = colliders.get_mut_internal(*removed) {
- // Reset the proxy index.
- co.bf_data.proxy_index = crate::INVALID_U32;
- }
- }
- }
-
- /// Finalize the insertion of the layer identified by `layer_id`.
- ///
- /// This will:
- /// - Remove all the subregion proxies from the larger layer.
- /// - Pre-insert all the smaller layer's region proxies into this layer.
- fn finalize_layer_insertion(&mut self, layer_id: u8) {
- // Remove all the region endpoints from the larger layer.
- // They will be automatically replaced by the new layer's regions.
- if let Some(larger_layer) = self.layers[layer_id as usize].larger_layer {
- self.layers[larger_layer as usize].unregister_all_subregions(&mut self.proxies);
- }
-
- // Add all the regions from the smaller layer to the new layer.
- // This will result in new regions to be created in the new layer.
- // These new regions will automatically propagate to the larger layers in
- // the Phase 3 of `Self::update`.
- if let Some(smaller_layer) = self.layers[layer_id as usize].smaller_layer {
- let (smaller_layer, new_layer) = self
- .layers
- .index_mut2(smaller_layer as usize, layer_id as usize);
-
- smaller_layer.propagate_existing_regions(
- new_layer,
- &mut self.proxies,
- &mut self.region_pool,
- );
- }
- }
-
- /// Ensures that a given layer exists.
- ///
- /// If the layer does not exist then:
- /// 1. It is created and added to `self.layers`.
- /// 2. The smaller/larger layer indices are updated to order them
- /// properly depending on their depth.
- /// 3. All the subregion proxies from the larger layer are deleted:
- /// they will be replaced by this new layer's regions later in
- /// the `update` function.
- /// 4. All the regions from the smaller layer are added to that new
- /// layer.
- fn ensure_layer_exists(&mut self, new_depth: i8) -> u8 {
- // Special case: we don't have any layers yet.
- if self.layers.is_empty() {
- let layer_id = self.layers.len() as u8; // TODO: check overflow.
- self.layers
- .push(SAPLayer::new(new_depth, layer_id, None, None));
- return 0;
- }
-
- // Find the first layer with a depth larger or equal to new_depth.
- let mut larger_layer_id = Some(self.smallest_layer);
-
- while let Some(curr_layer_id) = larger_layer_id {
- if self.layers[curr_layer_id as usize].depth >= new_depth {
- break;
- }
-
- larger_layer_id = self.layers[curr_layer_id as usize].larger_layer;
- }
-
- match larger_layer_id {
- None => {
- // The layer we are currently creating is the new largest layer. So
- // we need to update `self.largest_layer` accordingly then call
- // `self.finalize_layer_insertion.
- assert_ne!(self.layers.len() as u8, u8::MAX, "Not yet implemented.");
- let new_layer_id = self.layers.len() as u8;
- self.layers[self.largest_layer as usize].larger_layer = Some(new_layer_id);
- self.layers.push(SAPLayer::new(
- new_depth,
- new_layer_id,
- Some(self.largest_layer),
- None,
- ));
- self.largest_layer = new_layer_id;
- self.finalize_layer_insertion(new_layer_id);
- new_layer_id
- }
- Some(larger_layer_id) => {
- if self.layers[larger_layer_id as usize].depth == new_depth {
- // Found it! The layer already exists.
- larger_layer_id
- } else {
- // The layer does not exist yet. Create it.
- // And we found another layer that is larger than this one.
- // So we need to adjust the smaller/larger layer indices too
- // keep the list sorted, and then call `self.finalize_layer_insertion`
- // to deal with region propagation.
- let new_layer_id = self.layers.len() as u8;
- let smaller_layer_id = self.layers[larger_layer_id as usize].smaller_layer;
- self.layers[larger_layer_id as usize].smaller_layer = Some(new_layer_id);
-
- if let Some(smaller_layer_id) = smaller_layer_id {
- self.layers[smaller_layer_id as usize].larger_layer = Some(new_layer_id);
- } else {
- self.smallest_layer = new_layer_id;
- }
-
- self.layers.push(SAPLayer::new(
- new_depth,
- new_layer_id,
- smaller_layer_id,
- Some(larger_layer_id),
- ));
- self.finalize_layer_insertion(new_layer_id);
-
- new_layer_id
- }
- }
- }
- }
-
- fn handle_modified_collider(
- &mut self,
- prediction_distance: Real,
- handle: ColliderHandle,
- proxy_index: &mut u32,
- collider: (&ColliderPosition, &ColliderShape, &ColliderChanges),
- ) -> bool {
- let (co_pos, co_shape, co_changes) = collider;
-
- let mut aabb = co_shape
- .compute_aabb(co_pos)
- .loosened(prediction_distance / 2.0);
-
- if aabb.mins.coords.iter().any(|e| !e.is_finite())
- || aabb.maxs.coords.iter().any(|e| !e.is_finite())
- {
- // Reject Aabbs with non-finite values.
- return false;
- }
-
- aabb.mins = super::clamp_point(aabb.mins);
- aabb.maxs = super::clamp_point(aabb.maxs);
-
- let prev_aabb;
-
- let layer_id = if let Some(proxy) = self.proxies.get_mut(*proxy_index) {
- let mut layer_id = proxy.layer_id;
- prev_aabb = proxy.aabb;
- proxy.aabb = aabb;
-
- if co_changes.contains(ColliderChanges::SHAPE) {
- // If the shape was changed, then we need to see if this proxy should be
- // migrated to a larger layer. Indeed, if the shape was replaced by
- // a much larger shape, we need to promote the proxy to a bigger layer
- // to avoid the O(n²) discretization problem.
- let new_layer_depth = super::layer_containing_aabb(&aabb);
- if new_layer_depth > proxy.layer_depth {
- self.layers[proxy.layer_id as usize]
- .proper_proxy_moved_to_bigger_layer(&mut self.proxies, *proxy_index);
-
- // We need to promote the proxy to the bigger layer.
- layer_id = self.ensure_layer_exists(new_layer_depth);
- self.proxies[*proxy_index].layer_id = layer_id;
- self.proxies[*proxy_index].layer_depth = new_layer_depth;
- }
- }
-
- layer_id
- } else {
- let layer_depth = super::layer_containing_aabb(&aabb);
- let layer_id = self.ensure_layer_exists(layer_depth);
-
- // Create the proxy.
- let proxy = SAPProxy::collider(handle, aabb, layer_id, layer_depth);
- prev_aabb = aabb;
- *proxy_index = self.proxies.insert(proxy);
- layer_id
- };
-
- let layer = &mut self.layers[layer_id as usize];
-
- // Preupdate the collider in the layer.
- // We need to use both the prev Aabb and the new Aabb for this update, to
- // handle special cases where one Aabb has left a region that doesn’t contain
- // any other modified Aabbs.
- // If the combination of both previous and new aabbs isn’t more than 25% bigger
- // than the new Aabb, we just merge them to save some computation times (to avoid
- // discretizing twice the area at their intersection. If it’s bigger than 25% then
- // we discretize both aabbs individually.
- let merged_aabbs = prev_aabb.merged(&aabb);
-
- if merged_aabbs.volume() > aabb.volume() * 1.25 {
- layer.preupdate_collider(
- *proxy_index,
- &aabb,
- None,
- &mut self.proxies,
- &mut self.region_pool,
- );
-
- layer.preupdate_collider(
- *proxy_index,
- &prev_aabb,
- Some(&aabb),
- &mut self.proxies,
- &mut self.region_pool,
- );
- } else {
- layer.preupdate_collider(
- *proxy_index,
- &merged_aabbs,
- Some(&aabb),
- &mut self.proxies,
- &mut self.region_pool,
- );
- }
-
- // Returns true if propagation is needed.
- !layer.created_regions.is_empty()
- }
-
- /// Updates the broad-phase, taking into account the new collider positions.
- pub fn update(
- &mut self,
- prediction_distance: Real,
- colliders: &mut ColliderSet,
- modified_colliders: &[ColliderHandle],
- removed_colliders: &[ColliderHandle],
- events: &mut Vec<BroadPhasePairEvent>,
- ) {
- // Phase 1: pre-delete the collisions that have been deleted.
- self.handle_removed_colliders(removed_colliders);
-
- let mut need_region_propagation = false;
-
- // Phase 2: pre-delete the collisions that have been deleted.
- for handle in modified_colliders {
- // NOTE: we use `get` because the collider may no longer
- // exist if it has been removed.
- if let Some(co) = colliders.get_mut_internal(*handle) {
- if !co.is_enabled() || !co.changes.needs_broad_phase_update() {
- continue;
- }
-
- let mut new_proxy_id = co.bf_data.proxy_index;
-
- if self.handle_modified_collider(
- prediction_distance,
- *handle,
- &mut new_proxy_id,
- (&co.pos, &co.shape, &co.changes),
- ) {
- need_region_propagation = true;
- }
-
- if co.bf_data.proxy_index != new_proxy_id {
- self.colliders_proxy_ids.insert(*handle, new_proxy_id);
-
- // Make sure we have the new proxy index in case
- // the collider was added for the first time.
- co.bf_data = ColliderBroadPhaseData {
- proxy_index: new_proxy_id,
- };
- }
- }
- }
-
- // Phase 3: bottom-up pass to propagate new regions from smaller layers to larger layers.
- if need_region_propagation {
- self.propagate_created_regions();
- }
-
- // Phase 4: top-down pass to propagate proxies from larger layers to smaller layers.
- self.update_layers_and_find_pairs(events);
-
- // Phase 5: bottom-up pass to remove proxies, and propagate region removed from smaller
- // layers to possible remove regions from larger layers that would become empty that way.
- self.complete_removals(colliders, removed_colliders);
- }
-
- /// Propagate regions from the smallest layers up to the larger layers.
- ///
- /// Whenever a region is created on a layer `n`, then its Aabb must be
- /// added to its larger layer so we can detect when an object
- /// in a larger layer may start interacting with objects in a smaller
- /// layer.
- fn propagate_created_regions(&mut self) {
- let mut curr_layer = Some(self.smallest_layer);
-
- while let Some(curr_layer_id) = curr_layer {
- let layer = &mut self.layers[curr_layer_id as usize];
- let larger_layer = layer.larger_layer;
-
- if !layer.created_regions.is_empty() {
- if let Some(larger_layer) = larger_layer {
- let (layer, larger_layer) = self
- .layers
- .index_mut2(curr_layer_id as usize, larger_layer as usize);
- layer.propagate_created_regions(
- larger_layer,
- &mut self.proxies,
- &mut self.region_pool,
- );
- layer.created_regions.clear();
- } else {
- // Always clear the set of created regions, even if
- // there is no larger layer.
- layer.created_regions.clear();
- }
- }
-
- curr_layer = larger_layer;
- }
- }
-
- fn update_layers_and_find_pairs(&mut self, out_events: &mut Vec<BroadPhasePairEvent>) {
- if self.layers.is_empty() {
- return;
- }
-
- // This is a top-down operation: we start by updating the largest
- // layer, and continue down to the smallest layer.
- //
- // This must be top-down because:
- // 1. If a non-region proxy from the layer `n` interacts with one of
- // the regions from the layer `n - 1`, it must be added to that
- // smaller layer before that smaller layer is updated.
- // 2. If a region has been updated, then all its subregions at the
- // layer `n - 1` must be marked as "needs-to-be-updated" too, in
- // order to account for the fact that a big proxy moved.
- // NOTE: this 2nd point could probably be improved: instead of updating
- // all the subregions, we could perhaps just update the subregions
- // that crosses the boundary of the Aabb of the big proxies that
- // moved in they layer `n`.
- let mut layer_id = Some(self.largest_layer);
-
- while let Some(curr_layer_id) = layer_id {
- self.layers[curr_layer_id as usize]
- .update_regions(&mut self.proxies, &mut self.reporting);
-
- layer_id = self.layers[curr_layer_id as usize].smaller_layer;
-
- for ((proxy_id1, proxy_id2), colliding) in &self.reporting {
- let (proxy1, proxy2) = self
- .proxies
- .elements
- .index_mut2(*proxy_id1 as usize, *proxy_id2 as usize);
-
- match (&mut proxy1.data, &mut proxy2.data) {
- (SAPProxyData::Collider(handle1), SAPProxyData::Collider(handle2)) => {
- if *colliding {
- out_events.push(BroadPhasePairEvent::AddPair(ColliderPair::new(
- *handle1, *handle2,
- )));
- } else {
- out_events.push(BroadPhasePairEvent::DeletePair(ColliderPair::new(
- *handle1, *handle2,
- )));
- }
- }
- (SAPProxyData::Collider(_), SAPProxyData::Region(_)) => {
- if *colliding {
- // Add the collider to the subregion.
- proxy2
- .data
- .as_region_mut()
- .preupdate_proxy(*proxy_id1, false);
- }
- }
- (SAPProxyData::Region(_), SAPProxyData::Collider(_)) => {
- if *colliding {
- // Add the collider to the subregion.
- proxy1
- .data
- .as_region_mut()
- .preupdate_proxy(*proxy_id2, false);
- }
- }
- (SAPProxyData::Region(_), SAPProxyData::Region(_)) => {
- // This will only happen between two adjacent subregions because
- // they share some identical bounds. So this case does not matter.
- }
- }
- }
-
- self.reporting.clear();
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::dynamics::{
- ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBodyBuilder, RigidBodySet,
- };
- use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet};
-
- #[test]
- fn test_add_update_remove() {
- let mut broad_phase = BroadPhase::new();
- let mut bodies = RigidBodySet::new();
- let mut colliders = ColliderSet::new();
- let mut impulse_joints = ImpulseJointSet::new();
- let mut multibody_joints = MultibodyJointSet::new();
- let mut islands = IslandManager::new();
-
- let rb = RigidBodyBuilder::dynamic().build();
- let co = ColliderBuilder::ball(0.5).build();
- let hrb = bodies.insert(rb);
- let coh = colliders.insert_with_parent(co, hrb, &mut bodies);
-
- let mut events = Vec::new();
- broad_phase.update(0.0, &mut colliders, &[coh], &[], &mut events);
-
- bodies.remove(
- hrb,
- &mut islands,
- &mut colliders,
- &mut impulse_joints,
- &mut multibody_joints,
- true,
- );
- broad_phase.update(0.0, &mut colliders, &[], &[coh], &mut events);
-
- // Create another body.
- let rb = RigidBodyBuilder::dynamic().build();
- let co = ColliderBuilder::ball(0.5).build();
- let hrb = bodies.insert(rb);
- let coh = colliders.insert_with_parent(co, hrb, &mut bodies);
-
- // Make sure the proxy handles is recycled properly.
- broad_phase.update(0.0, &mut colliders, &[coh], &[], &mut events);
- }
-}