Skip to content

Commit

Permalink
add IgnoredCollisions component
Browse files Browse the repository at this point in the history
  • Loading branch information
exoexo-dev committed Aug 28, 2024
1 parent f1a98d9 commit 3fd6b17
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 2 deletions.
23 changes: 23 additions & 0 deletions src/collision/broad_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ fn collect_collision_pairs(
intervals: ResMut<AabbIntervals>,
mut broad_collision_pairs: ResMut<BroadCollisionPairs>,
mut aabb_intersection_query: Query<&mut AabbIntersections>,
ignored_collisions: Query<&IgnoredCollisions>,
) {
for mut intersections in &mut aabb_intersection_query {
intersections.clear();
Expand All @@ -191,6 +192,7 @@ fn collect_collision_pairs(
intervals,
&mut broad_collision_pairs.0,
&mut aabb_intersection_query,
ignored_collisions,
);
}

Expand All @@ -201,6 +203,7 @@ fn sweep_and_prune(
mut intervals: ResMut<AabbIntervals>,
broad_collision_pairs: &mut Vec<(Entity, Entity)>,
aabb_intersection_query: &mut Query<&mut AabbIntersections>,
ignored_collisions: Query<&IgnoredCollisions>,
) {
// Sort bodies along the x-axis using insertion sort, a sorting algorithm great for sorting nearly sorted lists.
insertion_sort(&mut intervals.0, |a, b| a.2.min.x > b.2.min.x);
Expand All @@ -212,9 +215,29 @@ fn sweep_and_prune(
for (i, (ent1, parent1, aabb1, layers1, store_intersections1, inactive1)) in
intervals.0.iter().enumerate()
{
let ent1_ignored_collisions = ignored_collisions.get(*ent1).ok();
for (ent2, parent2, aabb2, layers2, store_intersections2, inactive2) in
intervals.0.iter().skip(i + 1)
{
// Check ignored collisions of `ent1`
if ent1_ignored_collisions
.as_ref()
.map(|i| i.contains(ent2))
.unwrap_or_default()
{
continue;
}

// Check ignored collisions of `ent2`
let ent2_ignored_collisions = ignored_collisions.get(*ent2).ok();
if ent2_ignored_collisions
.as_ref()
.map(|i| i.contains(ent1))
.unwrap_or_default()
{
continue;
}

// x doesn't intersect; check this first so we can discard as soon as possible
if aabb2.min.x > aabb1.max.x {
break;
Expand Down
3 changes: 2 additions & 1 deletion src/collision/collider/parry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
/// ```
///
/// Colliders can be further configured using various components like [`Friction`], [`Restitution`],
/// [`Sensor`], [`CollisionLayers`], and [`CollisionMargin`].
/// [`Sensor`], [`IgnoredCollisions`], [`CollisionLayers`], and [`CollisionMargin`].
///
/// In addition, Avian automatically adds some other components for colliders, like the following:
///
Expand Down Expand Up @@ -344,6 +344,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
/// - [Rigid bodies](RigidBody)
/// - [Density](ColliderDensity)
/// - [Friction] and [restitution](Restitution) (bounciness)
/// - [Ignoring collisions](IgnoredCollisions)
/// - [Collision layers](CollisionLayers)
/// - [Sensors](Sensor)
/// - [Collision margins for adding extra thickness to colliders](CollisionMargin)
Expand Down
2 changes: 2 additions & 0 deletions src/collision/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ impl Not for LayerMask {
///
/// [bitmasks]: https://en.wikipedia.org/wiki/Mask_(computing)
///
/// See also [`IgnoredCollisions`](crate::components::IgnoredCollisions).
///
/// ## Creation
///
/// Collision layers store memberships and filters using [`LayerMask`]s. A [`LayerMask`] can be created using
Expand Down
6 changes: 6 additions & 0 deletions src/collision/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ use indexmap::IndexMap;
/// The collisions can be accessed at any time, but modifications to contacts should be performed
/// in the [`PostProcessCollisions`] schedule. Otherwise, the physics solver will use the old contact data.
///
/// ### Ignoring collisions
///
/// You can attach an [`IgnoredCollisions`] component to an entity with a
/// [`Collider`] to completely avoid collision detection between the entity and
/// the entities contained within the [`IgnoredCollisions`] component.
///
/// ### Filtering and removing collisions
///
/// The following methods can be used for filtering or removing existing collisions:
Expand Down
59 changes: 58 additions & 1 deletion src/dynamics/rigid_body/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(crate) use forces::FloatZero;
pub(crate) use forces::Torque;

use crate::prelude::*;
use bevy::prelude::*;
use bevy::{prelude::*, utils::HashSet};
use derive_more::From;

/// A non-deformable body used for the simulation of most physics objects.
Expand Down Expand Up @@ -826,6 +826,63 @@ pub struct AngularDamping(pub Scalar);
#[reflect(Debug, Component, Default, PartialEq)]
pub struct Dominance(pub i8);

/// A component containing a set of entities for which any collisions with the
/// owning entity will be ignored.
///
/// ## Example
///
/// ```
/// use bevy::prelude::*;
/// # #[cfg(feature = "2d")]
/// # use bevy_xpbd_2d::prelude::*;
/// # #[cfg(feature = "3d")]
/// use bevy_xpbd_3d::prelude::*;
///
/// fn setup(mut commands: Commands) {
/// // Spawn an entity with a collider
#[cfg_attr(
feature = "2d",
doc = " let ent1 = commands",
doc = " .spawn((RigidBody::Dynamic, Collider::circle(0.5)))",
doc = " .id();"
)]
#[cfg_attr(
feature = "3d",
doc = " let ent1 = commands",
doc = " .spawn((RigidBody::Dynamic, Collider::sphere(0.5)))",
doc = " .id();"
)]
///
/// // Spawn another entity with a collider and configure it to avoid collisions with the first entity.
#[cfg_attr(
feature = "2d",
doc = " let ent1 = commands.spawn((",
doc = " RigidBody::Dynamic,",
doc = " Collider::circle(0.5),",
doc = " IgnoredCollisions::from_iter([ent1]),",
doc = "));"
)]
#[cfg_attr(
feature = "3d",
doc = " let ent1 = commands.spawn((",
doc = " RigidBody::Dynamic,",
doc = " Collider::sphere(0.5),",
doc = " IgnoredCollisions::from_iter([ent1]),",
doc = " ));"
)]
/// }
/// ```
///
/// See also [`CollisionLayers`].
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
pub struct IgnoredCollisions(pub HashSet<Entity>);

impl FromIterator<Entity> for IgnoredCollisions {
fn from_iter<T: IntoIterator<Item = Entity>>(iter: T) -> Self {
Self(HashSet::from_iter(iter))
}
}

#[cfg(test)]
mod tests {
use crate::prelude::*;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
//! - [Creation](Collider#creation)
//! - [Density](ColliderDensity)
//! - [Friction] and [restitution](Restitution) (bounciness)
//! - [Ignoring collisions](IgnoredCollisions)
//! - [Collision layers](CollisionLayers)
//! - [Sensors](Sensor)
#![cfg_attr(
Expand Down

0 comments on commit 3fd6b17

Please sign in to comment.