avian3d/collision/broad_phase/
mod.rs

1//! Finds pairs of entities with overlapping [`ColliderAabb`] and creates contacts
2//! for the [narrow phase].
3//!
4//! [narrow phase]: crate::collision::narrow_phase
5//!
6//! # Overview
7//!
8//! To speed up collision detection, the broad phase quickly identifies pairs of entities
9//! whose [`ColliderAabb`]s overlap. These contacts are then passed to the [narrow phase]
10//! for more detailed collision checks.
11//!
12//! In Avian, the broad phase is implemented with two plugins:
13//!
14//! - [`BroadPhaseCorePlugin`]: Sets up resources, system sets, and diagnostics required for broad phase collision detection.
15//! - [`BvhBroadPhasePlugin`]: Implements a broad phase using a [Bounding Volume Hierarchy (BVH)][BVH] to efficiently find overlapping AABBs.
16//!
17//! The former is required for all broad phase implementations, while the latter is an optional plugin
18//! that can be replaced with another broad phase strategy if desired. See the following section for details.
19//!
20//! [BVH]: https://en.wikipedia.org/wiki/Bounding_volume_hierarchy
21//!
22//! # Custom Broad Phase Implementations
23//!
24//! By default, Avian uses the [`BvhBroadPhasePlugin`] for broad phase collision detection.
25//! However, it is possible to replace it with a custom broad phase strategy, such as
26//! sweep and prune (SAP) or some kind of spatial grid.
27//!
28//! For simplicity's sake, we will demonstrate how to create a simple brute-force O(n^2)
29//! broad phase plugin that checks all pairs of colliders for AABB overlaps.
30//!
31//! In short, all we need to do is add a system to [`BroadPhaseSystems::CollectCollisions`]
32//! that finds overlapping AABBs and creates contacts for them in the [`ContactGraph`] resource.
33//! However, we are responsible for handling any pair filtering that we might need. This includes:
34//!
35//! - [`CollisionLayers`]
36//! - [`CollisionHooks`]
37//! - [`JointCollisionDisabled`]
38//! - Skip collisions with parent rigid body
39//! - Skip non-dynamic vs non-dynamic pairs
40//!
41//! and so on. We will only implement a subset of these for demonstration purposes,
42//! but you can take a look at the source code of the [`BvhBroadPhasePlugin`] for a complete reference.
43//!
44//! First, we define our brute-force broad phase plugin:
45//!
46//! ```
47#![cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
48#![cfg_attr(not(feature = "2d"), doc = "use avian3d::prelude::*;")]
49//! use bevy::prelude::*;
50//!
51//! pub struct BruteForceBroadPhasePlugin;
52//!
53//! impl Plugin for BruteForceBroadPhasePlugin {
54//!     fn build(&self, app: &mut App) {
55//!         app.add_systems(
56//!             PhysicsSchedule,
57//!             collect_collision_pairs.in_set(BroadPhaseSystems::CollectCollisions),
58//!         );
59//!     }
60//! }
61//!
62//! # fn collect_collision_pairs() {}
63//! ```
64//!
65//! In `collect_collision_pairs`, we query all combinations of colliders,
66//! check for AABB overlaps, and create contacts for overlapping colliders:
67//!
68//! ```
69#![cfg_attr(
70    feature = "2d",
71    doc = "# use avian2d::{dynamics::solver::joint_graph::JointGraph, prelude::*};"
72)]
73#![cfg_attr(
74    not(feature = "2d"),
75    doc = "# use avian3d::{dynamics::solver::joint_graph::JointGraph, prelude::*};"
76)]
77//! # use bevy::prelude::*;
78//! #
79//! fn collect_collision_pairs(
80//!     colliders: Query<(Entity, &ColliderAabb, &CollisionLayers, &ColliderOf)>,
81//!     bodies: Query<&RigidBody>,
82//!     mut contact_graph: ResMut<ContactGraph>,
83//!     joint_graph: Res<JointGraph>,
84//! ) {
85//!     // Loop through all entity combinations and create contact pairs for overlapping AABBs.
86//!     for [
87//!         (collider1, aabb1, layers1, collider_of1),
88//!         (collider2, aabb2, layers2, collider_of2),
89//!     ] in colliders.iter_combinations()
90//!     {
91//!         // Get the rigid bodies of the colliders.
92//!         let Ok(rb1) = bodies.get(collider_of1.body) else {
93//!             continue;
94//!         };
95//!         let Ok(rb2) = bodies.get(collider_of2.body) else {
96//!             continue;
97//!         };
98//!
99//!         // Skip pairs where both bodies are non-dynamic.
100//!         if !rb1.is_dynamic() && !rb2.is_dynamic() {
101//!             continue;
102//!         }
103//!
104//!         // Check if the AABBs intersect.
105//!         if !aabb1.intersects(aabb2) {
106//!             continue;
107//!         }
108//!
109//!         // Check collision layers.
110//!         if !layers1.interacts_with(*layers2) {
111//!             continue;
112//!         }
113//!
114//!         // Check if a joint disables contacts between the two bodies.
115//!         if joint_graph
116//!             .joints_between(collider_of1.body, collider_of2.body)
117//!             .any(|edge| edge.collision_disabled)
118//!         {
119//!             continue;
120//!         }
121//!
122//!         // Create a contact in the contact graph.
123//!         let mut contact_edge = ContactEdge::new(collider1, collider2);
124//!         contact_edge.body1 = Some(collider_of1.body);
125//!         contact_edge.body2 = Some(collider_of2.body);
126//!         contact_graph.add_edge(contact_edge);
127//!     }
128//! }
129//! ```
130//!
131//! Now, we can simply replace the [`BvhBroadPhasePlugin`] with our custom
132//! `BruteForceBroadPhasePlugin` when building the app:
133//!
134//! ```
135#![cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
136#![cfg_attr(not(feature = "2d"), doc = "# use avian3d::prelude::*;")]
137//! # use bevy::prelude::*;
138//! #
139//! # fn main() {
140//! #     let mut app = App::new();
141//! #
142//! app.add_plugins(
143//!     PhysicsPlugins::default()
144//!         .build()
145//!         .disable::<BvhBroadPhasePlugin>()
146//!         .add(BruteForceBroadPhasePlugin)
147//! );
148//! # }
149//! #
150//! # struct BruteForceBroadPhasePlugin;
151//! # impl Plugin for BruteForceBroadPhasePlugin {
152//! #     fn build(&self, app: &mut App) {}
153//! # }
154//! ```
155
156mod bvh_broad_phase;
157pub use bvh_broad_phase::BvhBroadPhasePlugin;
158
159use crate::{
160    collision::CollisionDiagnostics, dynamics::solver::joint_graph::JointGraph, prelude::*,
161};
162use bevy::prelude::*;
163
164/// The core [broad phase](crate::collision::broad_phase) plugin that sets up the
165/// resources, system sets, and diagnostics required for broad phase collision detection.
166///
167/// This does *not* implement any specific broad phase algorithm by itself,
168/// but provides the foundation for other broad phase plugins to build upon.
169/// By default, the [`BvhBroadPhasePlugin`] is used, but it can be replaced
170/// with a custom strategy if desired.
171///
172/// See the [module-level documentation](crate::collision::broad_phase) for more information
173/// and an example of creating a custom broad phase plugin.
174pub struct BroadPhaseCorePlugin;
175
176impl Plugin for BroadPhaseCorePlugin {
177    fn build(&self, app: &mut App) {
178        app.init_resource::<ContactGraph>()
179            .init_resource::<JointGraph>();
180
181        app.configure_sets(
182            PhysicsSchedule,
183            (
184                BroadPhaseSystems::First,
185                BroadPhaseSystems::CollectCollisions,
186                BroadPhaseSystems::Last,
187            )
188                .chain()
189                .in_set(PhysicsStepSystems::BroadPhase),
190        );
191    }
192
193    fn finish(&self, app: &mut App) {
194        // Register timer diagnostics for collision detection.
195        app.register_physics_diagnostics::<CollisionDiagnostics>();
196    }
197}
198
199/// System sets for systems running in [`PhysicsStepSystems::BroadPhase`].
200#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
201pub enum BroadPhaseSystems {
202    /// Runs at the start of the broad phase.
203    First,
204    /// Finds pairs of entities with overlapping [`ColliderAabb`]s
205    /// and creates contact pairs for them in [`Collisions`].
206    CollectCollisions,
207    /// Runs at the end of the broad phase.
208    Last,
209}