bevy_gizmos/
aabb.rs

1//! A module adding debug visualization of [`Aabb`]s.
2
3use bevy_app::{Plugin, PostUpdate};
4use bevy_camera::primitives::Aabb;
5use bevy_color::{Color, Oklcha};
6use bevy_ecs::{
7    component::Component,
8    entity::Entity,
9    query::Without,
10    reflect::ReflectComponent,
11    schedule::IntoScheduleConfigs,
12    system::{Query, Res},
13};
14use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15use bevy_transform::{
16    components::{GlobalTransform, Transform},
17    TransformSystems,
18};
19
20use crate::{
21    config::{GizmoConfigGroup, GizmoConfigStore},
22    gizmos::Gizmos,
23    AppGizmoBuilder,
24};
25
26/// A [`Plugin`] that provides visualization of [`Aabb`]s for debugging.
27pub struct AabbGizmoPlugin;
28
29impl Plugin for AabbGizmoPlugin {
30    fn build(&self, app: &mut bevy_app::App) {
31        app.init_gizmo_group::<AabbGizmoConfigGroup>().add_systems(
32            PostUpdate,
33            (
34                draw_aabbs,
35                draw_all_aabbs.run_if(|config: Res<GizmoConfigStore>| {
36                    config.config::<AabbGizmoConfigGroup>().1.draw_all
37                }),
38            )
39                .after(bevy_camera::visibility::VisibilitySystems::CalculateBounds)
40                .after(TransformSystems::Propagate),
41        );
42    }
43}
44/// The [`GizmoConfigGroup`] used for debug visualizations of [`Aabb`] components on entities
45#[derive(Clone, Default, Reflect, GizmoConfigGroup)]
46#[reflect(Clone, Default)]
47pub struct AabbGizmoConfigGroup {
48    /// Draws all bounding boxes in the scene when set to `true`.
49    ///
50    /// To draw a specific entity's bounding box, you can add the [`ShowAabbGizmo`] component.
51    ///
52    /// Defaults to `false`.
53    pub draw_all: bool,
54    /// The default color for bounding box gizmos.
55    ///
56    /// A random color is chosen per box if `None`.
57    ///
58    /// Defaults to `None`.
59    pub default_color: Option<Color>,
60}
61
62/// Add this [`Component`] to an entity to draw its [`Aabb`] component.
63#[derive(Component, Reflect, Default, Debug)]
64#[reflect(Component, Default, Debug)]
65pub struct ShowAabbGizmo {
66    /// The color of the box.
67    ///
68    /// The default color from the [`AabbGizmoConfigGroup`] config is used if `None`,
69    pub color: Option<Color>,
70}
71
72fn draw_aabbs(
73    query: Query<(Entity, &Aabb, &GlobalTransform, &ShowAabbGizmo)>,
74    mut gizmos: Gizmos<AabbGizmoConfigGroup>,
75) {
76    for (entity, &aabb, &transform, gizmo) in &query {
77        let color = gizmo
78            .color
79            .or(gizmos.config_ext.default_color)
80            .unwrap_or_else(|| color_from_entity(entity));
81        gizmos.cuboid(aabb_transform(aabb, transform), color);
82    }
83}
84
85fn draw_all_aabbs(
86    query: Query<(Entity, &Aabb, &GlobalTransform), Without<ShowAabbGizmo>>,
87    mut gizmos: Gizmos<AabbGizmoConfigGroup>,
88) {
89    for (entity, &aabb, &transform) in &query {
90        let color = gizmos
91            .config_ext
92            .default_color
93            .unwrap_or_else(|| color_from_entity(entity));
94        gizmos.cuboid(aabb_transform(aabb, transform), color);
95    }
96}
97
98fn color_from_entity(entity: Entity) -> Color {
99    Oklcha::sequential_dispersed(entity.index()).into()
100}
101
102fn aabb_transform(aabb: Aabb, transform: GlobalTransform) -> GlobalTransform {
103    transform
104        * GlobalTransform::from(
105            Transform::from_translation(aabb.center.into())
106                .with_scale((aabb.half_extents * 2.).into()),
107        )
108}