Skip to main content

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, ORANGE, 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 {}, Rect {}",
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                Srgba::from(config.rect_light_color).to_hex()
34            )
35        }
36    }
37}
38
39fn setup(
40    mut commands: Commands,
41    mut meshes: ResMut<Assets<Mesh>>,
42    mut materials: ResMut<Assets<StandardMaterial>>,
43    mut config_store: ResMut<GizmoConfigStore>,
44) {
45    // Circular base.
46    commands.spawn((
47        Mesh3d(meshes.add(Circle::new(4.0))),
48        MeshMaterial3d(materials.add(Color::WHITE)),
49        Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)),
50    ));
51
52    // Cubes.
53    {
54        let mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
55        let material = materials.add(Color::srgb_u8(124, 144, 255));
56        for x in [-2.0, 0.0, 2.0] {
57            commands.spawn((
58                Mesh3d(mesh.clone()),
59                MeshMaterial3d(material.clone()),
60                Transform::from_xyz(x, 0.5, 0.0),
61            ));
62        }
63    }
64
65    // Lights.
66    {
67        commands.spawn((
68            PointLight {
69                shadow_maps_enabled: true,
70                range: 2.0,
71                color: DARK_CYAN.into(),
72                ..default()
73            },
74            Transform::from_xyz(0.0, 1.5, 0.0),
75        ));
76        commands.spawn((
77            SpotLight {
78                shadow_maps_enabled: true,
79                range: 3.5,
80                color: PURPLE.into(),
81                outer_angle: PI / 4.0,
82                inner_angle: PI / 4.0 * 0.8,
83                ..default()
84            },
85            Transform::from_xyz(4.0, 2.0, 0.0).looking_at(Vec3::X * 1.5, Vec3::Y),
86        ));
87        commands.spawn((
88            DirectionalLight {
89                color: GOLD.into(),
90                illuminance: DirectionalLight::default().illuminance * 0.05,
91                shadow_maps_enabled: true,
92                ..default()
93            },
94            Transform::from_xyz(-4.0, 2.0, 0.0).looking_at(Vec3::NEG_X * 1.5, Vec3::Y),
95        ));
96        commands.spawn((
97            RectLight {
98                color: ORANGE.into(),
99                intensity: 200_000.0,
100                width: 1.5,
101                height: 0.8,
102                range: 20.0,
103            },
104            Transform::from_xyz(0.0, 3.0, -3.0).looking_at(Vec3::ZERO, Vec3::Y),
105        ));
106    }
107
108    // Camera.
109    commands.spawn((
110        Camera3d::default(),
111        Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
112    ));
113
114    // Example instructions and gizmo config.
115    {
116        commands.spawn((
117            Text::new(
118                "Press 'D' to toggle drawing gizmos on top of everything else in the scene\n\
119            Hold 'Left' or 'Right' to change the line width of the gizmos\n\
120            Press 'A' to toggle drawing of the light gizmos\n\
121            Press 'C' to cycle between the light gizmos coloring modes",
122            ),
123            Node {
124                position_type: PositionType::Absolute,
125                top: px(12),
126                left: px(12),
127                ..default()
128            },
129        ));
130
131        let (_, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
132        light_config.draw_all = true;
133        light_config.color = LightGizmoColor::MatchLightColor;
134
135        commands
136            .spawn((
137                Text::new("Gizmo color mode: "),
138                GizmoColorText,
139                Node {
140                    position_type: PositionType::Absolute,
141                    bottom: px(12),
142                    left: px(12),
143                    ..default()
144                },
145            ))
146            .with_child(TextSpan(gizmo_color_text(light_config)));
147    }
148}
149
150fn rotate_camera(mut transform: Single<&mut Transform, With<Camera>>, time: Res<Time>) {
151    transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(time.delta_secs() / 2.));
152}
153
154fn update_config(
155    mut config_store: ResMut<GizmoConfigStore>,
156    keyboard: Res<ButtonInput<KeyCode>>,
157    time: Res<Time>,
158    color_text_query: Single<Entity, With<GizmoColorText>>,
159    mut writer: TextUiWriter,
160) {
161    if keyboard.just_pressed(KeyCode::KeyD) {
162        for (_, config, _) in config_store.iter_mut() {
163            config.depth_bias = if config.depth_bias == 0. { -1. } else { 0. };
164        }
165    }
166
167    let (config, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
168    if keyboard.pressed(KeyCode::ArrowRight) {
169        config.line.width += 5. * time.delta_secs();
170        config.line.width = config.line.width.clamp(0., 50.);
171    }
172    if keyboard.pressed(KeyCode::ArrowLeft) {
173        config.line.width -= 5. * time.delta_secs();
174        config.line.width = config.line.width.clamp(0., 50.);
175    }
176    if keyboard.just_pressed(KeyCode::KeyA) {
177        config.enabled ^= true;
178    }
179    if keyboard.just_pressed(KeyCode::KeyC) {
180        light_config.color = match light_config.color {
181            LightGizmoColor::Manual(_) => LightGizmoColor::Varied,
182            LightGizmoColor::Varied => LightGizmoColor::MatchLightColor,
183            LightGizmoColor::MatchLightColor => LightGizmoColor::ByLightType,
184            LightGizmoColor::ByLightType => LightGizmoColor::Manual(GRAY.into()),
185        };
186        *writer.text(*color_text_query, 1) = gizmo_color_text(light_config);
187    }
188}