Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for custom broad-phases #606

Merged
merged 4 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
based only on its current velocity.
- Add `Collider::copy_from` to copy most collider attributes to an existing collider.
- Add `RigidBody::copy_from` to copy most rigid-body attributes to an existing rigid-body.
- Add the `BroadPhase` trait and expect an implementor of this trait as input to `PhysicsPipeline::step`.

### Modified

- Renamed `BroadPhase` to `BroadPhaseMultiSap`. The `BroadPhase` is no a trait that can be
implemented for providing a custom broad-phase to rapier. Equivalently, the `DefaultBroadPhase` type
alias can be used in place of `BroadPhaseMultiSap`.

## v0.18.0 (24 Jan. 2024)

Expand Down
2 changes: 1 addition & 1 deletion examples3d-f64/debug_serialized3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rapier_testbed3d::Testbed;
#[derive(serde::Deserialize)]
struct State {
pub islands: IslandManager,
pub broad_phase: BroadPhase,
pub broad_phase: DefaultBroadPhase,
pub narrow_phase: NarrowPhase,
pub bodies: RigidBodySet,
pub colliders: ColliderSet,
Expand Down
2 changes: 1 addition & 1 deletion examples3d/debug_deserialize3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct PhysicsState {
pub gravity: Vector<f32>,
pub integration_parameters: IntegrationParameters,
pub islands: IslandManager,
pub broad_phase: BroadPhase,
pub broad_phase: DefaultBroadPhase,
pub narrow_phase: NarrowPhase,
pub bodies: RigidBodySet,
pub colliders: ColliderSet,
Expand Down
47 changes: 47 additions & 0 deletions src/geometry/broad_phase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::geometry::{BroadPhasePairEvent, ColliderHandle, ColliderSet};
use parry::math::Real;

/// An internal index stored in colliders by some broad-phase algorithms.
pub type BroadPhaseProxyIndex = u32;

/// Trait implemented by broad-phase algorithms supported by Rapier.
///
/// The task of a broad-phase algorithm is to detect potential collision pairs, usually based on
/// bounding volumes. The pairs must be concervative: it is OK to create a collision pair if
/// two objects don’t actually touch, but it is incorrect to remove a pair between two objects
/// that are still touching. In other words, it can have false-positive (though these induce
/// some computational overhead on the narrow-phase), but cannot have false-negative.
pub trait BroadPhase {
/// Updates the broad-phase.
///
/// The results must be output through the `events` struct. The broad-phase algorithm is only
/// required to generate new events (i.e. no need to re-send an `AddPair` event if it was already
/// sent previously and no `RemovePair` happened since then). Sending redundant events is allowed
/// but can result in a slight computational overhead.
///
/// The `colliders` set is mutable only to provide access to
/// [`collider.set_internal_broad_phase_proxy_index`]. Other properties of the collider should
/// **not** be modified during the broad-phase update.
///
/// # Parameters
/// - `prediction_distance`: colliders that are not exactly touching, but closer to this
/// distance must form a collision pair.
/// - `colliders`: the set of colliders. Change detection with `collider.needs_broad_phase_update()`
/// can be relied on at this stage.
/// - `modified_colliders`: colliders that are know to be modified since the last update.
/// - `removed_colliders`: colliders that got removed since the last update. Any associated data
/// in the broad-phase should be removed by this call to `update`.
/// - `events`: the broad-phase’s output. They indicate what collision pairs need to be created
/// and what pairs need to be removed. It is OK to create pairs for colliders that don’t
/// actually collide (though this can increase computational overhead in the narrow-phase)
/// but it is important not to indicate removal of a collision pair if the underlying colliders
/// are still touching or closer than `prediction_distance`.
fn update(
&mut self,
prediction_distance: Real,
colliders: &mut ColliderSet,
modified_colliders: &[ColliderHandle],
removed_colliders: &[ColliderHandle],
events: &mut Vec<BroadPhasePairEvent>,
);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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,
BroadPhaseProxyIndex, ColliderBroadPhaseData, ColliderChanges, ColliderHandle,
ColliderPosition, ColliderSet, ColliderShape,
};
use crate::math::Real;
use crate::prelude::BroadPhase;
use crate::utils::IndexMut2;
use parry::bounding_volume::BoundingVolume;
use parry::utils::hashmap::HashMap;
Expand Down Expand Up @@ -74,7 +74,7 @@ use parry::utils::hashmap::HashMap;
/// 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 {
pub struct BroadPhaseMultiSap {
proxies: SAPProxies,
layers: Vec<SAPLayer>,
smallest_layer: u8,
Expand All @@ -90,7 +90,7 @@ pub struct BroadPhase {
// 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>,
colliders_proxy_ids: HashMap<ColliderHandle, BroadPhaseProxyIndex>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
region_pool: SAPRegionPool, // To avoid repeated allocations.
// We could think serializing this workspace is useless.
Expand All @@ -114,16 +114,16 @@ pub struct BroadPhase {
reporting: HashMap<(u32, u32), bool>, // Workspace
}

impl Default for BroadPhase {
impl Default for BroadPhaseMultiSap {
fn default() -> Self {
Self::new()
}
}

impl BroadPhase {
impl BroadPhaseMultiSap {
/// Create a new empty broad-phase.
pub fn new() -> Self {
BroadPhase {
BroadPhaseMultiSap {
proxies: SAPProxies::new(),
layers: Vec::new(),
smallest_layer: 0,
Expand All @@ -138,7 +138,7 @@ impl BroadPhase {
///
/// 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`.
/// of the `BroadPhaseMultiSap::update`.
fn handle_removed_colliders(&mut self, removed_colliders: &[ColliderHandle]) {
// For each removed collider, remove the corresponding proxy.
for removed in removed_colliders {
Expand All @@ -156,7 +156,7 @@ impl BroadPhase {
/// 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) {
fn predelete_proxy(&mut self, proxy_index: BroadPhaseProxyIndex) {
if proxy_index == crate::INVALID_U32 {
// This collider has not been added to the broad-phase yet.
return;
Expand Down Expand Up @@ -449,65 +449,6 @@ impl BroadPhase {
!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
Expand Down Expand Up @@ -618,16 +559,77 @@ impl BroadPhase {
}
}

impl BroadPhase for BroadPhaseMultiSap {
/// Updates the broad-phase, taking into account the new collider positions.
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);
}
}

#[cfg(test)]
mod test {
use crate::dynamics::{
ImpulseJointSet, IslandManager, MultibodyJointSet, RigidBodyBuilder, RigidBodySet,
};
use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet};
use crate::geometry::{BroadPhase, BroadPhaseMultiSap, ColliderBuilder, ColliderSet};

#[test]
fn test_add_update_remove() {
let mut broad_phase = BroadPhase::new();
let mut broad_phase = BroadPhaseMultiSap::new();
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let mut impulse_joints = ImpulseJointSet::new();
Expand Down
5 changes: 2 additions & 3 deletions src/geometry/broad_phase_multi_sap/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pub use self::broad_phase::BroadPhase;
pub use self::broad_phase_multi_sap::BroadPhaseMultiSap;
pub use self::broad_phase_pair_event::{BroadPhasePairEvent, ColliderPair};
pub use self::sap_proxy::SAPProxyIndex;

use self::sap_axis::*;
use self::sap_endpoint::*;
Expand All @@ -9,7 +8,7 @@ use self::sap_proxy::*;
use self::sap_region::*;
use self::sap_utils::*;

mod broad_phase;
mod broad_phase_multi_sap;
mod broad_phase_pair_event;
mod sap_axis;
mod sap_endpoint;
Expand Down
4 changes: 2 additions & 2 deletions src/geometry/broad_phase_multi_sap/sap_axis.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{SAPEndpoint, SAPProxies, NUM_SENTINELS};
use crate::geometry::broad_phase_multi_sap::DELETED_AABB_VALUE;
use crate::geometry::SAPProxyIndex;
use crate::geometry::BroadPhaseProxyIndex;
use crate::math::Real;
use bit_vec::BitVec;
use parry::bounding_volume::BoundingVolume;
Expand Down Expand Up @@ -39,7 +39,7 @@ impl SAPAxis {
pub fn batch_insert(
&mut self,
dim: usize,
new_proxies: &[SAPProxyIndex],
new_proxies: &[BroadPhaseProxyIndex],
proxies: &SAPProxies,
reporting: Option<&mut HashMap<(u32, u32), bool>>,
) {
Expand Down
Loading
Loading