light_gizmos/
light_gizmos.rs

1//! This example demonstrates how to visualize lights properties through the gizmo API.
2
3use std::f32::consts::{FRAC_PI_2, PI};
4
5use bevy::{
6    color::palettes::css::{DARK_CYAN, GOLD, GRAY, PURPLE},
7    prelude::*,
8};
9
10fn main() {
11    App::new()
12        .add_plugins(DefaultPlugins)
13        .add_systems(Startup, setup)
14        .add_systems(Update, rotate_camera)
15        .add_systems(Update, update_config)
16        .run();
17}
18
19#[derive(Component)]
20struct GizmoColorText;
21
22fn gizmo_color_text(config: &LightGizmoConfigGroup) -> String {
23    match config.color {
24        LightGizmoColor::Manual(color) => format!("Manual {}", Srgba::from(color).to_hex()),
25        LightGizmoColor::Varied => "Random from entity".to_owned(),
26        LightGizmoColor::MatchLightColor => "Match light color".to_owned(),
27        LightGizmoColor::ByLightType => {
28            format!(
29                "Point {}, Spot {}, Directional {}",
30                Srgba::from(config.point_light_color).to_hex(),
31                Srgba::from(config.spot_light_color).to_hex(),
32                Srgba::from(config.directional_light_color).to_hex()
33            )
34        }
35    }
36}
37
38fn setup(
39    mut commands: Commands,
40    mut meshes: ResMut<Assets<Mesh>>,
41    mut materials: ResMut<Assets<StandardMaterial>>,
42    mut config_store: ResMut<GizmoConfigStore>,
43) {
44    // Circular base.
45    commands.spawn((
46        Mesh3d(meshes.add(Circle::new(4.0))),
47        MeshMaterial3d(materials.add(Color::WHITE)),
48        Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)),
49    ));
50
51    // Cubes.
52    {
53        let mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
54        let material = materials.add(Color::srgb_u8(124, 144, 255));
55        for x in [-2.0, 0.0, 2.0] {
56            commands.spawn((
57                Mesh3d(mesh.clone()),
58                MeshMaterial3d(material.clone()),
59                Transform::from_xyz(x, 0.5, 0.0),
60            ));
61        }
62    }
63
64    // Lights.
65    {
66        commands.spawn((
67            PointLight {
68                shadows_enabled: true,
69                range: 2.0,
70                color: DARK_CYAN.into(),
71                ..default()
72            },
73            Transform::from_xyz(0.0, 1.5, 0.0),
74        ));
75        commands.spawn((
76            SpotLight {
77                shadows_enabled: true,
78                range: 3.5,
79                color: PURPLE.into(),
80                outer_angle: PI / 4.0,
81                inner_angle: PI / 4.0 * 0.8,
82                ..default()
83            },
84            Transform::from_xyz(4.0, 2.0, 0.0).looking_at(Vec3::X * 1.5, Vec3::Y),
85        ));
86        commands.spawn((
87            DirectionalLight {
88                color: GOLD.into(),
89                illuminance: DirectionalLight::default().illuminance * 0.05,
90                shadows_enabled: true,
91                ..default()
92            },
93            Transform::from_xyz(-4.0, 2.0, 0.0).looking_at(Vec3::NEG_X * 1.5, Vec3::Y),
94        ));
95    }
96
97    // Camera.
98    commands.spawn((
99        Camera3d::default(),
100        Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
101    ));
102
103    // Example instructions and gizmo config.
104    {
105        commands.spawn((
106            Text::new(
107                "Press 'D' to toggle drawing gizmos on top of everything else in the scene\n\
108            Hold 'Left' or 'Right' to change the line width of the gizmos\n\
109            Press 'A' to toggle drawing of the light gizmos\n\
110            Press 'C' to cycle between the light gizmos coloring modes",
111            ),
112            Node {
113                position_type: PositionType::Absolute,
114                top: px(12),
115                left: px(12),
116                ..default()
117            },
118        ));
119
120        let (_, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
121        light_config.draw_all = true;
122        light_config.color = LightGizmoColor::MatchLightColor;
123
124        commands
125            .spawn((
126                Text::new("Gizmo color mode: "),
127                GizmoColorText,
128                Node {
129                    position_type: PositionType::Absolute,
130                    bottom: px(12),
131                    left: px(12),
132                    ..default()
133                },
134            ))
135            .with_child(TextSpan(gizmo_color_text(light_config)));
136    }
137}
138
139fn rotate_camera(mut transform: Single<&mut Transform, With<Camera>>, time: Res<Time>) {
140    transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(time.delta_secs() / 2.));
141}
142
143fn update_config(
144    mut config_store: ResMut<GizmoConfigStore>,
145    keyboard: Res<ButtonInput<KeyCode>>,
146    time: Res<Time>,
147    color_text_query: Single<Entity, With<GizmoColorText>>,
148    mut writer: TextUiWriter,
149) {
150    if keyboard.just_pressed(KeyCode::KeyD) {
151        for (_, config, _) in config_store.iter_mut() {
152            config.depth_bias = if config.depth_bias == 0. { -1. } else { 0. };
153        }
154    }
155
156    let (config, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
157    if keyboard.pressed(KeyCode::ArrowRight) {
158        config.line.width += 5. * time.delta_secs();
159        config.line.width = config.line.width.clamp(0., 50.);
160    }
161    if keyboard.pressed(KeyCode::ArrowLeft) {
162        config.line.width -= 5. * time.delta_secs();
163        config.line.width = config.line.width.clamp(0., 50.);
164    }
165    if keyboard.just_pressed(KeyCode::KeyA) {
166        config.enabled ^= true;
167    }
168    if keyboard.just_pressed(KeyCode::KeyC) {
169        light_config.color = match light_config.color {
170            LightGizmoColor::Manual(_) => LightGizmoColor::Varied,
171            LightGizmoColor::Varied => LightGizmoColor::MatchLightColor,
172            LightGizmoColor::MatchLightColor => LightGizmoColor::ByLightType,
173            LightGizmoColor::ByLightType => LightGizmoColor::Manual(GRAY.into()),
174        };
175        *writer.text(*color_text_query, 1) = gizmo_color_text(light_config);
176    }
177}