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

add IgnoredCollisions component #364

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
23 changes: 23 additions & 0 deletions src/collision/broad_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,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 @@ -199,6 +200,7 @@ fn collect_collision_pairs(
intervals,
&mut broad_collision_pairs.0,
&mut aabb_intersection_query,
ignored_collisions,
);
}

Expand All @@ -209,6 +211,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 @@ -220,9 +223,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 @@ -263,7 +263,7 @@ impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
/// ```
///
/// Colliders can be further configured using various components like [`Friction`], [`Restitution`],
/// [`Sensor`], [`CollisionLayers`], [`CollisionMargin`], and [`ColliderDensity`].
/// [`Sensor`], [`IgnoredCollisions`], [`CollisionLayers`], [`CollisionMargin`], and [`ColliderDensity`].
///
/// If you need to specify the shape of the collider statically, use [`ColliderConstructor`] and build your collider
/// with the [`Collider::try_from_constructor`] method.
Expand Down Expand Up @@ -337,6 +337,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::dynamics::rigid_body::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 @@ -20,7 +20,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 @@ -684,3 +684,60 @@ pub struct AngularDamping(pub Scalar);
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[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 avian2d::prelude::*;
/// # #[cfg(feature = "3d")]
/// use avian3d::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))
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
//! - [Creation](Collider#creation)
//! - [Density](ColliderDensity)
//! - [Friction] and [restitution](Restitution) (bounciness)
//! - [Ignoring collisions](IgnoredCollisions)
//! - [Collision layers](CollisionLayers)
//! - [Sensors](Sensor)
#![cfg_attr(
Expand Down
Loading