aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/geometry/broad_phase_multi_sap/broad_phase.rs58
-rw-r--r--src/geometry/broad_phase_multi_sap/sap_layer.rs74
-rw-r--r--src/geometry/broad_phase_multi_sap/sap_region.rs35
-rw-r--r--src/geometry/broad_phase_multi_sap/sap_utils.rs2
4 files changed, 139 insertions, 30 deletions
diff --git a/src/geometry/broad_phase_multi_sap/broad_phase.rs b/src/geometry/broad_phase_multi_sap/broad_phase.rs
index 18d394f..c97c737 100644
--- a/src/geometry/broad_phase_multi_sap/broad_phase.rs
+++ b/src/geometry/broad_phase_multi_sap/broad_phase.rs
@@ -119,6 +119,10 @@ impl BroadPhase {
}
/// 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, colliders: &mut ColliderSet) {
// Ensure we already subscribed the collider-removed events.
if self.removed_colliders.is_none() {
@@ -141,13 +145,13 @@ impl BroadPhase {
self.removed_colliders = Some(cursor);
}
- /// Removes a proxy from this broad-phase.
+ /// 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 removing them
+ /// 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 {
@@ -220,20 +224,27 @@ impl BroadPhase {
colliders.removed_colliders.ack(&cursor);
}
+ /// 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 {
- // Remove all the region endpoints from the larger layer.
- // They will be automatically replaced by the new layer's regions.
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);
- // Add all the regions from the smaller layer to the new layer.
- // This will propagate to the bigger layers automatically.
smaller_layer.propagate_existing_regions(
new_layer,
&mut self.proxies,
@@ -242,6 +253,17 @@ impl BroadPhase {
}
}
+ /// 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() {
@@ -264,6 +286,9 @@ impl BroadPhase {
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);
@@ -283,6 +308,10 @@ impl BroadPhase {
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);
@@ -315,10 +344,12 @@ impl BroadPhase {
colliders: &mut ColliderSet,
events: &mut Vec<BroadPhasePairEvent>,
) {
+ // Phase 1: pre-delete the collisions that have been deleted.
self.handle_removed_colliders(colliders);
let mut need_region_propagation = false;
+ // Phase 2: pre-delete the collisions that have been deleted.
for body_handle in bodies
.modified_inactive_set
.iter()
@@ -353,23 +384,23 @@ impl BroadPhase {
}
}
- // Bottom-up pass to propagate regions from smaller layers to larger layers.
+ // Phase 3: bottom-up pass to propagate new regions from smaller layers to larger layers.
if need_region_propagation {
self.propagate_created_regions();
}
- // Top-down pass to propagate proxies from larger layers to smaller layers.
+ // Phase 4: top-down pass to propagate proxies from larger layers to smaller layers.
self.update_layers_and_find_pairs(events);
- // 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.
+ // 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);
}
- /// Propagate regions from the smaller layers up to the larger layers.
+ /// 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 all the larger layers so we can detect whaen a object
+ /// 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) {
@@ -389,8 +420,9 @@ impl BroadPhase {
&mut self.proxies,
&mut self.region_pool,
);
+ layer.created_regions.clear();
} else {
- // Always clear the set of created regions, even else if
+ // Always clear the set of created regions, even if
// there is no larger layer.
layer.created_regions.clear();
}
diff --git a/src/geometry/broad_phase_multi_sap/sap_layer.rs b/src/geometry/broad_phase_multi_sap/sap_layer.rs
index 8242cca..1ee4468 100644
--- a/src/geometry/broad_phase_multi_sap/sap_layer.rs
+++ b/src/geometry/broad_phase_multi_sap/sap_layer.rs
@@ -38,23 +38,39 @@ impl SAPLayer {
}
}
+ /// Deletes from all the regions of this layer, all the endpoints corresponding
+ /// to subregions. Clears the arrays of subregions indices from all the regions of
+ /// this layer.
pub fn unregister_all_subregions(&mut self, proxies: &mut SAPProxies) {
for region_id in self.regions.values() {
- if let Some(mut region) = proxies[*region_id].data.take_region() {
- region.delete_all_region_endpoints(proxies);
+ // Extract the region to make the borrow-checker happy.
+ let mut region = proxies[*region_id]
+ .data
+ .take_region()
+ .expect("Should be a region proxy.");
- for subregion in region.subregions.drain(..) {
- proxies[subregion]
- .data
- .as_region_mut()
- .id_in_parent_subregion = crate::INVALID_U32;
- }
+ // Delete the endpoints.
+ region.delete_all_region_endpoints(proxies);
- proxies[*region_id].data.set_region(region);
+ // Clear the subregions vec and reset the subregions parent ids.
+ for subregion in region.subregions.drain(..) {
+ proxies[subregion]
+ .data
+ .as_region_mut()
+ .id_in_parent_subregion = crate::INVALID_U32;
}
+
+ // Re set the region to make the borrow-checker happy.
+ proxies[*region_id].data.set_region(region);
}
}
+ /// Register into `larger_layer` all the region proxies of the recently-created regions
+ /// contained by `self`.
+ ///
+ /// This method must be called in a bottom-up loop, propagating new regions from the
+ /// smallest layer, up to the largest layer. That loop is done by the Phase 3 of the
+ /// BroadPhase::update.
pub fn propagate_created_regions(
&mut self,
larger_layer: &mut Self,
@@ -66,6 +82,7 @@ impl SAPLayer {
}
}
+ /// Register into `larger_layer` all the region proxies of the region contained in `self`.
pub fn propagate_existing_regions(
&mut self,
larger_layer: &mut Self,
@@ -77,7 +94,12 @@ impl SAPLayer {
}
}
- // Preupdates the proxy of a subregion.
+ /// Registers a subregion of this layer.
+ ///
+ /// The subregion proxy will be added to the region of `self` that contains
+ /// that subregion center. Because the hierarchical grid cells have aligned boundaries
+ /// at each depth, we have the guarantee that a given subregion will only be part of
+ /// one region on its parent "larger" layer.
fn register_subregion(
&mut self,
proxy_id: SAPProxyIndex,
@@ -85,7 +107,9 @@ impl SAPLayer {
pool: &mut SAPRegionPool,
) {
if let Some(proxy) = proxies.get(proxy_id) {
- if proxy.data.as_region().id_in_parent_subregion == crate::INVALID_U32 {
+ let curr_id_in_parent_subregion = proxy.data.as_region().id_in_parent_subregion;
+
+ if curr_id_in_parent_subregion == crate::INVALID_U32 {
let region_key = super::point_key(proxy.aabb.center(), self.region_width);
let region_id = self.ensure_region_exists(region_key, proxies, pool);
let region = proxies[region_id].data.as_region_mut();
@@ -95,6 +119,20 @@ impl SAPLayer {
.data
.as_region_mut()
.id_in_parent_subregion = id_in_parent_subregion as u32;
+ } else {
+ // NOTE: all the following are just assertions to make sure the
+ // region ids are correctly wired. If this piece of code causes
+ // any performance problem, it can be deleted completely without
+ // hesitation.
+ if curr_id_in_parent_subregion != crate::INVALID_U32 {
+ let region_key = super::point_key(proxy.aabb.center(), self.region_width);
+ let region_id = self.regions.get(&region_key).unwrap();
+ let region = proxies[*region_id].data.as_region_mut();
+ assert_eq!(
+ region.subregions[curr_id_in_parent_subregion as usize],
+ proxy_id
+ );
+ }
}
}
}
@@ -138,6 +176,15 @@ impl SAPLayer {
}
}
+ /// Ensures a given region exists in this layer.
+ ///
+ /// If the region with the given region key does not exist yet, it is created.
+ /// When a region is created, it creates a new proxy for that region, and its
+ /// proxy ID is added to `self.created_region` so it can be propagated during
+ /// the Phase 3 of `BroadPhase::update`.
+ ///
+ /// This returns the proxy ID of the already existing region if it existed, or
+ /// of the new region if it did not exist and has been created by this method.
pub fn ensure_region_exists(
&mut self,
region_key: Point<i32>,
@@ -145,14 +192,19 @@ impl SAPLayer {
pool: &mut SAPRegionPool,
) -> SAPProxyIndex {
match self.regions.entry(region_key) {
+ // Yay, the region already exists!
Entry::Occupied(occupied) => *occupied.get(),
+ // The region does not exist, create it.
Entry::Vacant(vacant) => {
let region_bounds = super::region_aabb(region_key, self.region_width);
let region = SAPRegion::recycle_or_new(region_bounds, pool);
+ // Create a new proxy for that region.
let region_proxy =
SAPProxy::subregion(region, region_bounds, self.layer_id, self.depth);
let region_proxy_id = proxies.insert(region_proxy);
+ // Push this region's proxy ID to the set of created regions.
self.created_regions.push(region_proxy_id as u32);
+ // Insert the new region to this layer's region hashmap.
let _ = vacant.insert(region_proxy_id);
region_proxy_id
}
diff --git a/src/geometry/broad_phase_multi_sap/sap_region.rs b/src/geometry/broad_phase_multi_sap/sap_region.rs
index 3d0834c..1c3036d 100644
--- a/src/geometry/broad_phase_multi_sap/sap_region.rs
+++ b/src/geometry/broad_phase_multi_sap/sap_region.rs
@@ -73,18 +73,43 @@ impl SAPRegion {
}
}
+ /// Deletes from the axes of this region all the endpoints that point
+ /// to a region.
pub fn delete_all_region_endpoints(&mut self, proxies: &SAPProxies) {
- for axis in &mut self.axes {
+ let mut num_deleted_subregion_endpoints = [0; DIM];
+
+ for (i, axis) in self.axes.iter_mut().enumerate() {
let existing_proxies = &mut self.existing_proxies;
axis.endpoints.retain(|e| {
+ // NOTE: we use `if let` instead of `unwrap` because no
+ // proxy will be found for the sentinels.
if let Some(proxy) = proxies.get(e.proxy()) {
- existing_proxies.set(e.proxy() as usize, false);
- !proxy.data.is_region()
- } else {
- true
+ if proxy.data.is_region() {
+ existing_proxies.set(e.proxy() as usize, false);
+ num_deleted_subregion_endpoints[i] += 1;
+ return false;
+ }
}
+
+ true
});
}
+
+ // All axes should have deleted the same number of region endpoints.
+ for k in 1..DIM {
+ assert_eq!(
+ num_deleted_subregion_endpoints[0],
+ num_deleted_subregion_endpoints[k]
+ );
+ }
+
+ // The number of deleted endpoints should be even because there
+ // are two endpoints per proxy on each axes.
+ assert_eq!(num_deleted_subregion_endpoints[0] % 2, 0);
+
+ // All the region endpoints are subproper proxies.
+ // So update the subproper proxy count accordingly.
+ self.subproper_proxy_count -= num_deleted_subregion_endpoints[0] / 2;
}
pub fn predelete_proxy(&mut self, _proxy_id: SAPProxyIndex) {
diff --git a/src/geometry/broad_phase_multi_sap/sap_utils.rs b/src/geometry/broad_phase_multi_sap/sap_utils.rs
index ac84926..43a8bb4 100644
--- a/src/geometry/broad_phase_multi_sap/sap_utils.rs
+++ b/src/geometry/broad_phase_multi_sap/sap_utils.rs
@@ -44,7 +44,7 @@ pub(crate) fn region_width(depth: i8) -> Real {
///
/// The idea here is that an AABB should be part of a layer which has
/// regions large enough so that one AABB doesn't crosses too many
-/// regions. But the regions must also not bee too large, otherwise
+/// regions. But the regions must also not be too large, otherwise
/// we are loosing the benefits of Multi-SAP.
///
/// If the code bellow, we select a layer such that each region can