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