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}