bevy_gizmos/
aabb.rs

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