1use bevy::{
4 camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
5 light::ShadowFilteringMethod,
6 prelude::*,
7};
8
9fn main() {
10 App::new()
11 .add_plugins(DefaultPlugins)
12 .add_plugins(FreeCameraPlugin)
13 .add_systems(Startup, setup)
14 .add_systems(
15 Update,
16 (
17 cycle_filter_methods,
18 adjust_light_position,
19 adjust_point_light_biases,
20 toggle_light,
21 adjust_directional_light_biases,
22 ),
23 )
24 .run();
25}
26
27#[derive(Component)]
28struct Lights;
29
30fn setup(
32 mut commands: Commands,
33 mut meshes: ResMut<Assets<Mesh>>,
34 mut materials: ResMut<Assets<StandardMaterial>>,
35) {
36 let spawn_plane_depth = 300.0f32;
37 let spawn_height = 2.0;
38 let sphere_radius = 0.25;
39
40 let white_handle = materials.add(StandardMaterial {
41 base_color: Color::WHITE,
42 perceptual_roughness: 1.0,
43 ..default()
44 });
45 let sphere_handle = meshes.add(Sphere::new(sphere_radius));
46
47 let light_transform = Transform::from_xyz(5.0, 5.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y);
48 commands.spawn((
49 light_transform,
50 Visibility::default(),
51 Lights,
52 children![
53 (PointLight {
54 intensity: 0.0,
55 range: spawn_plane_depth,
56 color: Color::WHITE,
57 shadow_maps_enabled: true,
58 ..default()
59 }),
60 (DirectionalLight {
61 shadow_maps_enabled: true,
62 ..default()
63 })
64 ],
65 ));
66
67 commands.spawn((
69 Camera3d::default(),
70 Transform::from_xyz(-1.0, 1.0, 1.0).looking_at(Vec3::new(-1.0, 1.0, 0.0), Vec3::Y),
71 FreeCamera::default(),
72 ShadowFilteringMethod::Hardware2x2,
73 ));
74
75 for z_i32 in (-spawn_plane_depth as i32..=0).step_by(2) {
76 commands.spawn((
77 Mesh3d(sphere_handle.clone()),
78 MeshMaterial3d(white_handle.clone()),
79 Transform::from_xyz(
80 0.0,
81 if z_i32 % 4 == 0 {
82 spawn_height
83 } else {
84 sphere_radius
85 },
86 z_i32 as f32,
87 ),
88 ));
89 }
90
91 let plane_size = 2.0 * spawn_plane_depth;
93 commands.spawn((
94 Mesh3d(meshes.add(Plane3d::default().mesh().size(plane_size, plane_size))),
95 MeshMaterial3d(white_handle),
96 ));
97
98 commands.spawn((
99 Node {
100 position_type: PositionType::Absolute,
101 padding: UiRect::all(px(5)),
102 ..default()
103 },
104 BackgroundColor(Color::BLACK.with_alpha(0.75)),
105 GlobalZIndex(i32::MAX),
106 children![(
107 Text::default(),
108 children![
109 (TextSpan::new("Controls:\n")),
110 (TextSpan::new("R / Z - reset biases to default / zero\n")),
111 (TextSpan::new("L - switch between directional and point lights [",)),
112 (TextSpan::new("DirectionalLight")),
113 (TextSpan::new("]\n")),
114 (TextSpan::new("F - switch directional light filter methods [",)),
115 (TextSpan::new("Hardware2x2")),
116 (TextSpan::new("]\n")),
117 (TextSpan::new("1/2 - change point light depth bias [")),
118 (TextSpan::new("0.00")),
119 (TextSpan::new("]\n")),
120 (TextSpan::new("3/4 - change point light normal bias [")),
121 (TextSpan::new("0.0")),
122 (TextSpan::new("]\n")),
123 (TextSpan::new("5/6 - change direction light depth bias [")),
124 (TextSpan::new("0.00")),
125 (TextSpan::new("]\n")),
126 (TextSpan::new("7/8 - change direction light normal bias [",)),
127 (TextSpan::new("0.0")),
128 (TextSpan::new("]\n")),
129 (TextSpan::new(
130 "left/right/up/down/pgup/pgdown - adjust light position (looking at 0,0,0) [",
131 )),
132 (TextSpan(format!("{:.1},", light_transform.translation.x))),
133 (TextSpan(format!(" {:.1},", light_transform.translation.y))),
134 (TextSpan(format!(" {:.1}", light_transform.translation.z))),
135 (TextSpan::new("]\n")),
136 ]
137 )],
138 ));
139}
140
141fn toggle_light(
142 input: Res<ButtonInput<KeyCode>>,
143 mut point_lights: Query<&mut PointLight>,
144 mut directional_lights: Query<&mut DirectionalLight>,
145 example_text: Single<Entity, With<Text>>,
146 mut writer: TextUiWriter,
147) {
148 if input.just_pressed(KeyCode::KeyL) {
149 for mut light in &mut point_lights {
150 light.intensity = if light.intensity == 0.0 {
151 *writer.text(*example_text, 4) = "PointLight".to_string();
152 light_consts::lumens::VERY_LARGE_CINEMA_LIGHT
153 } else {
154 0.0
155 };
156 }
157 for mut light in &mut directional_lights {
158 light.illuminance = if light.illuminance == 0.0 {
159 *writer.text(*example_text, 4) = "DirectionalLight".to_string();
160 light_consts::lux::AMBIENT_DAYLIGHT
161 } else {
162 0.0
163 };
164 }
165 }
166}
167
168fn adjust_light_position(
169 input: Res<ButtonInput<KeyCode>>,
170 mut lights: Query<&mut Transform, With<Lights>>,
171 example_text: Single<Entity, With<Text>>,
172 mut writer: TextUiWriter,
173) {
174 let mut offset = Vec3::ZERO;
175 if input.just_pressed(KeyCode::ArrowLeft) {
176 offset.x -= 1.0;
177 }
178 if input.just_pressed(KeyCode::ArrowRight) {
179 offset.x += 1.0;
180 }
181 if input.just_pressed(KeyCode::ArrowUp) {
182 offset.z -= 1.0;
183 }
184 if input.just_pressed(KeyCode::ArrowDown) {
185 offset.z += 1.0;
186 }
187 if input.just_pressed(KeyCode::PageDown) {
188 offset.y -= 1.0;
189 }
190 if input.just_pressed(KeyCode::PageUp) {
191 offset.y += 1.0;
192 }
193 if offset != Vec3::ZERO {
194 let example_text = *example_text;
195 for mut light in &mut lights {
196 light.translation += offset;
197 light.look_at(Vec3::ZERO, Vec3::Y);
198 *writer.text(example_text, 22) = format!("{:.1},", light.translation.x);
199 *writer.text(example_text, 23) = format!(" {:.1},", light.translation.y);
200 *writer.text(example_text, 24) = format!(" {:.1}", light.translation.z);
201 }
202 }
203}
204
205fn cycle_filter_methods(
206 input: Res<ButtonInput<KeyCode>>,
207 mut filter_methods: Query<&mut ShadowFilteringMethod>,
208 example_text: Single<Entity, With<Text>>,
209 mut writer: TextUiWriter,
210) {
211 if input.just_pressed(KeyCode::KeyF) {
212 for mut filter_method in &mut filter_methods {
213 let filter_method_string;
214 *filter_method = match *filter_method {
215 ShadowFilteringMethod::Hardware2x2 => {
216 filter_method_string = "Gaussian".to_string();
217 ShadowFilteringMethod::Gaussian
218 }
219 ShadowFilteringMethod::Gaussian => {
220 filter_method_string = "Temporal".to_string();
221 ShadowFilteringMethod::Temporal
222 }
223 ShadowFilteringMethod::Temporal => {
224 filter_method_string = "Hardware2x2".to_string();
225 ShadowFilteringMethod::Hardware2x2
226 }
227 };
228 *writer.text(*example_text, 7) = filter_method_string;
229 }
230 }
231}
232
233fn adjust_point_light_biases(
234 input: Res<ButtonInput<KeyCode>>,
235 mut query: Query<&mut PointLight>,
236 example_text: Single<Entity, With<Text>>,
237 mut writer: TextUiWriter,
238) {
239 let depth_bias_step_size = 0.01;
240 let normal_bias_step_size = 0.1;
241 for mut light in &mut query {
242 if input.just_pressed(KeyCode::Digit1) {
243 light.shadow_depth_bias -= depth_bias_step_size;
244 }
245 if input.just_pressed(KeyCode::Digit2) {
246 light.shadow_depth_bias += depth_bias_step_size;
247 }
248 if input.just_pressed(KeyCode::Digit3) {
249 light.shadow_normal_bias -= normal_bias_step_size;
250 }
251 if input.just_pressed(KeyCode::Digit4) {
252 light.shadow_normal_bias += normal_bias_step_size;
253 }
254 if input.just_pressed(KeyCode::KeyR) {
255 light.shadow_depth_bias = PointLight::DEFAULT_SHADOW_DEPTH_BIAS;
256 light.shadow_normal_bias = PointLight::DEFAULT_SHADOW_NORMAL_BIAS;
257 }
258 if input.just_pressed(KeyCode::KeyZ) {
259 light.shadow_depth_bias = 0.0;
260 light.shadow_normal_bias = 0.0;
261 }
262
263 *writer.text(*example_text, 10) = format!("{:.2}", light.shadow_depth_bias);
264 *writer.text(*example_text, 13) = format!("{:.1}", light.shadow_normal_bias);
265 }
266}
267
268fn adjust_directional_light_biases(
269 input: Res<ButtonInput<KeyCode>>,
270 mut query: Query<&mut DirectionalLight>,
271 example_text: Single<Entity, With<Text>>,
272 mut writer: TextUiWriter,
273) {
274 let depth_bias_step_size = 0.01;
275 let normal_bias_step_size = 0.1;
276 for mut light in &mut query {
277 if input.just_pressed(KeyCode::Digit5) {
278 light.shadow_depth_bias -= depth_bias_step_size;
279 }
280 if input.just_pressed(KeyCode::Digit6) {
281 light.shadow_depth_bias += depth_bias_step_size;
282 }
283 if input.just_pressed(KeyCode::Digit7) {
284 light.shadow_normal_bias -= normal_bias_step_size;
285 }
286 if input.just_pressed(KeyCode::Digit8) {
287 light.shadow_normal_bias += normal_bias_step_size;
288 }
289 if input.just_pressed(KeyCode::KeyR) {
290 light.shadow_depth_bias = DirectionalLight::DEFAULT_SHADOW_DEPTH_BIAS;
291 light.shadow_normal_bias = DirectionalLight::DEFAULT_SHADOW_NORMAL_BIAS;
292 }
293 if input.just_pressed(KeyCode::KeyZ) {
294 light.shadow_depth_bias = 0.0;
295 light.shadow_normal_bias = 0.0;
296 }
297
298 *writer.text(*example_text, 16) = format!("{:.2}", light.shadow_depth_bias);
299 *writer.text(*example_text, 19) = format!("{:.1}", light.shadow_normal_bias);
300 }
301}