Skip to main content

avian3d/collider_tree/
mod.rs

1//! Tree acceleration structures for spatial queries on [colliders](crate::collision::collider::Collider).
2//!
3//! To speed up [broad phase](crate::collision::broad_phase) collision detection and [spatial queries](crate::spatial_query),
4//! Avian maintains a [`ColliderTree`] structure for all colliders in the physics world. This is implemented as
5//! a [Bounding Volume Hierarchy (BVH)][BVH], which accelerates querying for [AABB](crate::collision::collider::ColliderAabb)
6//! overlaps, ray intersections, and more.
7//!
8//! Colliders of dynamic, kinematic, and static bodies are all stored in a separate [`ColliderTree`]
9//! to allow efficiently querying for specific subsets of colliders and to optimize tree updates based on body type.
10//! Trees for dynamic and kinematic bodies are rebuilt every physics step, while the static tree is incrementally updated
11//! when static colliders are added, removed, or modified. The trees are stored in the [`ColliderTrees`] resource.
12//!
13//! [BVH]: https://en.wikipedia.org/wiki/Bounding_volume_hierarchy
14//!
15//! # Usage
16//!
17//! The collider trees are fairly low-level, and not intended to be used directly.
18//! For spatial queries, consider using the higher-level [`SpatialQuery`] API instead,
19//! and for broad phase collision detection, consider using the [`BvhBroadPhasePlugin`].
20//!
21//! [`SpatialQuery`]: crate::spatial_query::SpatialQuery
22//! [`BvhBroadPhasePlugin`]: crate::collision::broad_phase::BvhBroadPhasePlugin
23
24mod diagnostics;
25mod obvhs_ext;
26mod optimization;
27mod proxy_key;
28mod traverse;
29mod tree;
30mod update;
31
32pub use diagnostics::ColliderTreeDiagnostics;
33pub use obvhs_ext::Bvh2Ext;
34pub(crate) use obvhs_ext::obvhs_ray;
35pub use optimization::{ColliderTreeOptimization, TreeOptimizationMode};
36pub use proxy_key::{ColliderTreeProxyKey, ColliderTreeType, ProxyId};
37pub use tree::{
38    ColliderTree, ColliderTreeProxy, ColliderTreeProxyFlags, ColliderTreeWorkspace, MovedProxyList,
39};
40pub use update::{MovedProxies, update_moved_collider_aabbs};
41
42use optimization::ColliderTreeOptimizationPlugin;
43use update::ColliderTreeUpdatePlugin;
44
45use core::marker::PhantomData;
46
47use crate::prelude::*;
48use bevy::prelude::*;
49
50/// A plugin that manages [collider trees](crate::collider_tree) for a collider type `C`.
51pub struct ColliderTreePlugin<C: AnyCollider>(PhantomData<C>);
52
53impl<C: AnyCollider> Default for ColliderTreePlugin<C> {
54    fn default() -> Self {
55        Self(PhantomData)
56    }
57}
58
59impl<C: AnyCollider> Plugin for ColliderTreePlugin<C> {
60    fn build(&self, app: &mut App) {
61        // Register required components.
62        let _ = app.try_register_required_components_with::<C, ColliderTreeProxyKey>(|| {
63            // Use a default proxy key. This will be overwritten when the proxy is actually created.
64            ColliderTreeProxyKey::PLACEHOLDER
65        });
66
67        // Add plugin for updating trees as colliders move.
68        app.add_plugins(ColliderTreeUpdatePlugin::<C>::default());
69
70        // Add plugin for optimizing trees tp maintain good query performance.
71        if !app.is_plugin_added::<ColliderTreeOptimizationPlugin>() {
72            app.add_plugins(ColliderTreeOptimizationPlugin);
73        }
74
75        // Initialize resources.
76        app.init_resource::<ColliderTrees>()
77            .init_resource::<MovedProxies>();
78
79        // Configure system sets.
80        app.configure_sets(
81            PhysicsSchedule,
82            ColliderTreeSystems::UpdateAabbs
83                .in_set(PhysicsStepSystems::BroadPhase)
84                .after(BroadPhaseSystems::First)
85                .before(BroadPhaseSystems::CollectCollisions),
86        );
87        app.configure_sets(
88            PhysicsSchedule,
89            ColliderTreeSystems::BeginOptimize.in_set(BroadPhaseSystems::Last),
90        );
91        app.configure_sets(
92            PhysicsSchedule,
93            // TODO: What's the optimal place for this? It needs to be before spatial queries and collision events.
94            ColliderTreeSystems::EndOptimize.in_set(SolverSystems::Finalize),
95        );
96    }
97
98    fn finish(&self, app: &mut App) {
99        // Register timer diagnostics for collider trees.
100        app.register_physics_diagnostics::<ColliderTreeDiagnostics>();
101    }
102}
103
104/// System sets for managing [`ColliderTrees`].
105#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
106pub enum ColliderTreeSystems {
107    /// Updates the AABBs of colliders.
108    UpdateAabbs,
109    /// Begins optimizing acceleration structures to keep their query performance good.
110    ///
111    /// This runs concurrently with the simulation step as an async task.
112    BeginOptimize,
113    /// Completes the optimization of acceleration structures started in [`ColliderTreeSystems::BeginOptimize`].
114    ///
115    /// This runs at the end of the simulation step.
116    EndOptimize,
117}
118
119/// Trees for accelerating queries on a set of colliders.
120///
121/// See the [`collider_tree`](crate::collider_tree) module for more information.
122#[derive(Resource, Default, Clone)]
123pub struct ColliderTrees {
124    /// A tree for the colliders of dynamic bodies.
125    pub dynamic_tree: ColliderTree,
126    /// A tree for the colliders of kinematic bodies.
127    pub kinematic_tree: ColliderTree,
128    /// A tree for the colliders of static bodies.
129    pub static_tree: ColliderTree,
130    /// A tree for standalone colliders with no associated rigid body.
131    pub standalone_tree: ColliderTree,
132}
133
134impl ColliderTrees {
135    /// Returns the tree for the given [`ColliderTreeType`].
136    #[inline]
137    pub const fn tree_for_type(&self, tree_type: ColliderTreeType) -> &ColliderTree {
138        match tree_type {
139            ColliderTreeType::Dynamic => &self.dynamic_tree,
140            ColliderTreeType::Kinematic => &self.kinematic_tree,
141            ColliderTreeType::Static => &self.static_tree,
142            ColliderTreeType::Standalone => &self.standalone_tree,
143        }
144    }
145
146    /// Returns a mutable reference to the tree for the given [`ColliderTreeType`].
147    #[inline]
148    pub const fn tree_for_type_mut(&mut self, tree_type: ColliderTreeType) -> &mut ColliderTree {
149        match tree_type {
150            ColliderTreeType::Dynamic => &mut self.dynamic_tree,
151            ColliderTreeType::Kinematic => &mut self.kinematic_tree,
152            ColliderTreeType::Static => &mut self.static_tree,
153            ColliderTreeType::Standalone => &mut self.standalone_tree,
154        }
155    }
156
157    /// Returns an iterator over all collider trees.
158    #[inline]
159    pub fn iter_trees(&self) -> impl Iterator<Item = &ColliderTree> {
160        [
161            &self.dynamic_tree,
162            &self.kinematic_tree,
163            &self.static_tree,
164            &self.standalone_tree,
165        ]
166        .into_iter()
167    }
168
169    /// Returns a mutable iterator over all collider trees.
170    #[inline]
171    pub fn iter_trees_mut(&mut self) -> impl Iterator<Item = &mut ColliderTree> {
172        [
173            &mut self.dynamic_tree,
174            &mut self.kinematic_tree,
175            &mut self.static_tree,
176            &mut self.standalone_tree,
177        ]
178        .into_iter()
179    }
180
181    /// Returns the proxy with the given [`ColliderTreeProxyKey`], if it exists.
182    #[inline]
183    pub fn get_proxy(&self, key: ColliderTreeProxyKey) -> Option<&ColliderTreeProxy> {
184        self.tree_for_type(key.tree_type())
185            .proxies
186            .get(key.id().index())
187    }
188
189    /// Returns a mutable reference to the proxy with the given [`ColliderTreeProxyKey`], if it exists.
190    #[inline]
191    pub fn get_proxy_mut(&mut self, key: ColliderTreeProxyKey) -> Option<&mut ColliderTreeProxy> {
192        self.tree_for_type_mut(key.tree_type())
193            .proxies
194            .get_mut(key.id().index())
195    }
196}