Skip to main content

bevy_gizmos/
aabb.rs

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