1use std::fmt;
4use std::ops::Range;
5
6use bevy::{
7 anti_alias::taa::TemporalAntiAliasing,
8 camera::Hdr,
9 color::palettes::css::{BLACK, WHITE},
10 image::{
11 ImageAddressMode, ImageFilterMode, ImageLoaderSettings, ImageSampler,
12 ImageSamplerDescriptor,
13 },
14 input::mouse::MouseWheel,
15 light::Skybox,
16 math::{vec3, vec4},
17 pbr::{
18 DefaultOpaqueRendererMethod, ExtendedMaterial, MaterialExtension,
19 ScreenSpaceAmbientOcclusion, ScreenSpaceReflections,
20 },
21 prelude::*,
22 render::render_resource::{AsBindGroup, ShaderType},
23 shader::ShaderRef,
24};
25
26#[path = "../helpers/widgets.rs"]
27mod widgets;
28
29use widgets::{
30 handle_ui_interactions, main_ui_node, option_buttons, update_ui_radio_button,
31 update_ui_radio_button_text, RadioButton, RadioButtonText, WidgetClickEvent, WidgetClickSender,
32 BUTTON_BORDER, BUTTON_BORDER_COLOR, BUTTON_BORDER_RADIUS_SIZE, BUTTON_PADDING,
33};
34
35const SHADER_ASSET_PATH: &str = "shaders/water_material.wgsl";
37
38const CAMERA_KEYBOARD_ZOOM_SPEED: f32 = 0.1;
40const CAMERA_KEYBOARD_ORBIT_SPEED: f32 = 0.02;
41const CAMERA_MOUSE_WHEEL_ZOOM_SPEED: f32 = 0.25;
42
43const CAMERA_ZOOM_RANGE: Range<f32> = 2.0..12.0;
45
46#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
48struct Water {
49 #[texture(100)]
53 #[sampler(101)]
54 normals: Handle<Image>,
55
56 #[uniform(102)]
58 settings: WaterSettings,
59}
60
61#[derive(ShaderType, Debug, Clone)]
63struct WaterSettings {
64 octave_vectors: [Vec4; 2],
67 octave_scales: Vec4,
69 octave_strengths: Vec4,
71}
72
73#[derive(Resource)]
75struct AppSettings {
76 ssr_on: bool,
78 displayed_model: DisplayedModel,
80 displayed_base: DisplayedBase,
82 min_perceptual_roughness: Range<f32>,
84 max_perceptual_roughness: Range<f32>,
86 edge_fadeout: Range<f32>,
88}
89
90#[derive(Default, PartialEq, Copy, Clone)]
92enum DisplayedModel {
93 #[default]
95 Cube,
96 FlightHelmet,
98 Capsules,
100}
101
102#[derive(Default, PartialEq, Copy, Clone)]
104enum DisplayedBase {
105 #[default]
107 Water,
108 Metallic,
110 RedPlane,
112}
113
114impl fmt::Display for DisplayedModel {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 let name = match self {
117 DisplayedModel::Cube => "Cube",
118 DisplayedModel::FlightHelmet => "Flight Helmet",
119 DisplayedModel::Capsules => "Capsules",
120 };
121 write!(f, "{}", name)
122 }
123}
124
125impl fmt::Display for DisplayedBase {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 let name = match self {
128 DisplayedBase::Water => "Water",
129 DisplayedBase::Metallic => "Metallic",
130 DisplayedBase::RedPlane => "Red Plane",
131 };
132 write!(f, "{}", name)
133 }
134}
135
136#[derive(Clone, Copy, PartialEq)]
137enum ExampleSetting {
138 Ssr(bool),
139 Model(DisplayedModel),
140 Base(DisplayedBase),
141 MinRoughnessStart(Adjustment),
142 MinRoughnessEnd(Adjustment),
143 MaxRoughnessStart(Adjustment),
144 MaxRoughnessEnd(Adjustment),
145 EdgeFadeoutStart(Adjustment),
146 EdgeFadeoutEnd(Adjustment),
147}
148
149#[derive(Clone, Copy, PartialEq)]
150enum Adjustment {
151 Increase,
152 Decrease,
153}
154
155#[derive(Component)]
157struct CubeModel;
158
159#[derive(Component)]
161struct FlightHelmetModel;
162
163#[derive(Component)]
165struct CapsuleModel;
166
167#[derive(Component)]
169struct CapsulesParent;
170
171#[derive(Component)]
173struct MetallicBaseModel;
174
175#[derive(Component)]
177struct RedPlaneBaseModel;
178
179#[derive(Component)]
181struct WaterModel;
182
183#[derive(Component)]
185enum RangeValueText {
186 MinRoughnessStart,
187 MinRoughnessEnd,
188 MaxRoughnessStart,
189 MaxRoughnessEnd,
190 EdgeFadeoutStart,
191 EdgeFadeoutEnd,
192}
193
194#[derive(bevy::ecs::system::SystemParam)]
195struct ModelQueries<'w, 's> {
196 cube_models: Query<'w, 's, Entity, With<CubeModel>>,
197 flight_helmet_models: Query<'w, 's, Entity, With<FlightHelmetModel>>,
198 capsule_models: Query<'w, 's, Entity, Or<(With<CapsuleModel>, With<CapsulesParent>)>>,
199 metallic_base_models: Query<'w, 's, Entity, With<MetallicBaseModel>>,
200 non_metallic_base_models: Query<'w, 's, Entity, With<RedPlaneBaseModel>>,
201 water_models: Query<'w, 's, Entity, With<WaterModel>>,
202}
203
204fn main() {
205 App::new()
209 .insert_resource(DefaultOpaqueRendererMethod::deferred())
210 .init_resource::<AppSettings>()
211 .add_plugins(DefaultPlugins.set(WindowPlugin {
212 primary_window: Some(Window {
213 title: "Bevy Screen Space Reflections Example".into(),
214 ..default()
215 }),
216 ..default()
217 }))
218 .add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, Water>>::default())
219 .add_message::<WidgetClickEvent<ExampleSetting>>()
220 .add_systems(Startup, setup)
221 .add_systems(Update, rotate_model)
222 .add_systems(Update, move_camera)
223 .add_systems(Update, adjust_app_settings)
224 .add_systems(Update, handle_ui_interactions::<ExampleSetting>)
225 .run();
226}
227
228fn setup(
230 mut commands: Commands,
231 mut meshes: ResMut<Assets<Mesh>>,
232 mut standard_materials: ResMut<Assets<StandardMaterial>>,
233 mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, Water>>>,
234 asset_server: Res<AssetServer>,
235 app_settings: Res<AppSettings>,
236) {
237 spawn_cube(
238 &mut commands,
239 &asset_server,
240 &mut meshes,
241 &mut standard_materials,
242 );
243 spawn_flight_helmet(&mut commands, &asset_server);
244 spawn_capsules(&mut commands, &mut meshes, &mut standard_materials);
245 spawn_metallic_base(&mut commands, &mut meshes, &mut standard_materials);
246 spawn_non_metallic_base(&mut commands, &mut meshes, &mut standard_materials);
247 spawn_water(
248 &mut commands,
249 &asset_server,
250 &mut meshes,
251 &mut water_materials,
252 );
253 spawn_camera(&mut commands, &asset_server, &app_settings);
254 spawn_buttons(&mut commands, &app_settings);
255}
256
257fn spawn_cube(
259 commands: &mut Commands,
260 asset_server: &AssetServer,
261 meshes: &mut Assets<Mesh>,
262 standard_materials: &mut Assets<StandardMaterial>,
263) {
264 commands
265 .spawn((
266 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
267 MeshMaterial3d(standard_materials.add(StandardMaterial {
268 base_color: Color::from(WHITE),
269 base_color_texture: Some(asset_server.load("branding/icon.png")),
270 ..default()
271 })),
272 Transform::from_xyz(0.0, 0.5, 0.0),
273 ))
274 .insert(CubeModel);
275}
276
277fn spawn_flight_helmet(commands: &mut Commands, asset_server: &AssetServer) {
279 commands.spawn((
280 WorldAssetRoot(
281 asset_server
282 .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
283 ),
284 Transform::from_scale(Vec3::splat(2.5)),
285 FlightHelmetModel,
286 Visibility::Hidden,
287 ));
288}
289
290fn spawn_capsules(
292 commands: &mut Commands,
293 meshes: &mut Assets<Mesh>,
294 standard_materials: &mut Assets<StandardMaterial>,
295) {
296 let capsule_mesh = meshes.add(Capsule3d::new(0.4, 0.5));
297 let parent = commands
298 .spawn((
299 Transform::from_xyz(0.0, 0.5, 0.0),
300 Visibility::Hidden,
301 CapsulesParent,
302 ))
303 .id();
304
305 for i in 0..5 {
306 let roughness = i as f32 * 0.25;
307 let child = commands
308 .spawn((
309 Mesh3d(capsule_mesh.clone()),
310 MeshMaterial3d(standard_materials.add(StandardMaterial {
311 base_color: Color::BLACK,
312 perceptual_roughness: roughness.max(0.08),
313 ..default()
314 })),
315 Transform::from_xyz(i as f32 * 1.1 - (1.1 * 2.0), 0.5, 0.0),
316 CapsuleModel,
317 ))
318 .id();
319 commands.entity(parent).add_child(child);
320 }
321}
322
323fn spawn_metallic_base(
325 commands: &mut Commands,
326 meshes: &mut Assets<Mesh>,
327 standard_materials: &mut Assets<StandardMaterial>,
328) {
329 commands.spawn((
330 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
331 MeshMaterial3d(standard_materials.add(StandardMaterial {
332 base_color: Color::from(bevy::color::palettes::css::DARK_GRAY),
333 metallic: 1.0,
334 perceptual_roughness: 0.3,
335 ..default()
336 })),
337 Transform::from_scale(Vec3::splat(100.0)),
338 MetallicBaseModel,
339 Visibility::Hidden,
340 ));
341}
342
343fn spawn_non_metallic_base(
345 commands: &mut Commands,
346 meshes: &mut Assets<Mesh>,
347 standard_materials: &mut Assets<StandardMaterial>,
348) {
349 commands.spawn((
350 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
351 MeshMaterial3d(standard_materials.add(StandardMaterial {
352 base_color: Color::from(bevy::color::palettes::css::RED),
353 metallic: 0.0,
354 perceptual_roughness: 0.2,
355 ..default()
356 })),
357 Transform::from_scale(Vec3::splat(100.0)),
358 RedPlaneBaseModel,
359 Visibility::Hidden,
360 ));
361}
362
363fn spawn_water(
365 commands: &mut Commands,
366 asset_server: &AssetServer,
367 meshes: &mut Assets<Mesh>,
368 water_materials: &mut Assets<ExtendedMaterial<StandardMaterial, Water>>,
369) {
370 commands.spawn((
371 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
372 MeshMaterial3d(
373 water_materials.add(ExtendedMaterial {
374 base: StandardMaterial {
375 base_color: BLACK.into(),
376 perceptual_roughness: 0.09,
377 ..default()
378 },
379 extension: Water {
380 normals: asset_server
381 .load_builder()
382 .with_settings::<ImageLoaderSettings>(|settings| {
383 settings.is_srgb = false;
384 settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
385 address_mode_u: ImageAddressMode::Repeat,
386 address_mode_v: ImageAddressMode::Repeat,
387 mag_filter: ImageFilterMode::Linear,
388 min_filter: ImageFilterMode::Linear,
389 ..default()
390 });
391 })
392 .load("textures/water_normals.png"),
393 settings: WaterSettings {
396 octave_vectors: [
397 vec4(0.080, 0.059, 0.073, -0.062),
398 vec4(0.153, 0.138, -0.149, -0.195),
399 ],
400 octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 5.0,
401 octave_strengths: vec4(0.16, 0.18, 0.093, 0.044),
402 },
403 },
404 }),
405 ),
406 Transform::from_scale(Vec3::splat(100.0)),
407 WaterModel,
408 ));
409}
410
411fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer, app_settings: &AppSettings) {
413 commands.spawn((
418 Camera3d::default(),
419 Transform::from_translation(vec3(-1.25, 2.25, 4.5)).looking_at(Vec3::ZERO, Vec3::Y),
420 Hdr,
421 Msaa::Off,
422 TemporalAntiAliasing::default(),
423 ScreenSpaceReflections {
424 min_perceptual_roughness: app_settings.min_perceptual_roughness.clone(),
425 max_perceptual_roughness: app_settings.max_perceptual_roughness.clone(),
426 edge_fadeout: app_settings.edge_fadeout.clone(),
427 ..default()
428 },
429 ScreenSpaceAmbientOcclusion::default(),
430 EnvironmentMapLight {
431 diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
432 specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
433 intensity: 5000.0,
434 ..default()
435 },
436 Skybox {
437 image: Some(asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2")),
438 brightness: 5000.0,
439 ..default()
440 },
441 ));
442}
443
444fn spawn_buttons(commands: &mut Commands, app_settings: &AppSettings) {
445 commands.spawn(main_ui_node()).with_children(|parent| {
446 parent.spawn(option_buttons(
447 "SSR",
448 &[
449 (ExampleSetting::Ssr(true), "On"),
450 (ExampleSetting::Ssr(false), "Off"),
451 ],
452 ));
453
454 parent.spawn(option_buttons(
455 "Model",
456 &[
457 (ExampleSetting::Model(DisplayedModel::Cube), "Cube"),
458 (
459 ExampleSetting::Model(DisplayedModel::FlightHelmet),
460 "Flight Helmet",
461 ),
462 (ExampleSetting::Model(DisplayedModel::Capsules), "Capsules"),
463 ],
464 ));
465
466 parent.spawn(option_buttons(
467 "Base",
468 &[
469 (ExampleSetting::Base(DisplayedBase::Water), "Water"),
470 (ExampleSetting::Base(DisplayedBase::Metallic), "Metallic"),
471 (ExampleSetting::Base(DisplayedBase::RedPlane), "Red Plane"),
472 ],
473 ));
474
475 parent.spawn(range_row(
476 "Min Roughness",
477 app_settings.min_perceptual_roughness.start,
478 app_settings.min_perceptual_roughness.end,
479 RangeValueText::MinRoughnessStart,
480 RangeValueText::MinRoughnessEnd,
481 ExampleSetting::MinRoughnessStart(Adjustment::Decrease),
482 ExampleSetting::MinRoughnessStart(Adjustment::Increase),
483 ExampleSetting::MinRoughnessEnd(Adjustment::Decrease),
484 ExampleSetting::MinRoughnessEnd(Adjustment::Increase),
485 ));
486
487 parent.spawn(range_row(
488 "Max Roughness",
489 app_settings.max_perceptual_roughness.start,
490 app_settings.max_perceptual_roughness.end,
491 RangeValueText::MaxRoughnessStart,
492 RangeValueText::MaxRoughnessEnd,
493 ExampleSetting::MaxRoughnessStart(Adjustment::Decrease),
494 ExampleSetting::MaxRoughnessStart(Adjustment::Increase),
495 ExampleSetting::MaxRoughnessEnd(Adjustment::Decrease),
496 ExampleSetting::MaxRoughnessEnd(Adjustment::Increase),
497 ));
498
499 parent.spawn(range_row(
500 "Edge Fadeout",
501 app_settings.edge_fadeout.start,
502 app_settings.edge_fadeout.end,
503 RangeValueText::EdgeFadeoutStart,
504 RangeValueText::EdgeFadeoutEnd,
505 ExampleSetting::EdgeFadeoutStart(Adjustment::Decrease),
506 ExampleSetting::EdgeFadeoutStart(Adjustment::Increase),
507 ExampleSetting::EdgeFadeoutEnd(Adjustment::Decrease),
508 ExampleSetting::EdgeFadeoutEnd(Adjustment::Increase),
509 ));
510 });
511}
512
513fn range_row(
514 title: &str,
515 start_value: f32,
516 end_value: f32,
517 start_marker: RangeValueText,
518 end_marker: RangeValueText,
519 start_dec: ExampleSetting,
520 start_inc: ExampleSetting,
521 end_dec: ExampleSetting,
522 end_inc: ExampleSetting,
523) -> impl Bundle {
524 (
525 Node {
526 align_items: AlignItems::Center,
527 ..default()
528 },
529 Children::spawn((
530 Spawn((
531 widgets::ui_text(title, Color::WHITE),
532 Node {
533 width: px(150),
534 ..default()
535 },
536 )),
537 Spawn(range_controls(
538 start_value,
539 start_marker,
540 start_dec,
541 start_inc,
542 )),
543 Spawn((
544 widgets::ui_text("to", Color::WHITE),
545 Node {
546 margin: UiRect::horizontal(px(10)),
547 ..default()
548 },
549 )),
550 Spawn(range_controls(end_value, end_marker, end_dec, end_inc)),
551 )),
552 )
553}
554
555fn range_controls(
556 value: f32,
557 marker: RangeValueText,
558 dec_setting: ExampleSetting,
559 inc_setting: ExampleSetting,
560) -> impl Bundle {
561 (
562 Node {
563 align_items: AlignItems::Center,
564 ..default()
565 },
566 Children::spawn((
567 Spawn(adjustment_button(dec_setting, "<", Some(true))),
568 Spawn((
569 Node {
570 width: px(50),
571 height: px(33),
572 justify_content: JustifyContent::Center,
573 align_items: AlignItems::Center,
574 border: BUTTON_BORDER.with_left(px(0)).with_right(px(0)),
575 ..default()
576 },
577 BackgroundColor(Color::WHITE),
578 BUTTON_BORDER_COLOR,
579 marker,
580 children![(widgets::ui_text(&format!("{:.2}", value), Color::BLACK))],
581 )),
582 Spawn(adjustment_button(inc_setting, ">", Some(false))),
583 )),
584 )
585}
586
587fn adjustment_button(
588 setting: ExampleSetting,
589 label: &str,
590 is_left_right: Option<bool>,
591) -> impl Bundle {
592 (
593 Button,
594 Node {
595 height: px(33),
596 border: if let Some(is_left) = is_left_right {
597 if is_left {
598 BUTTON_BORDER.with_right(px(0))
599 } else {
600 BUTTON_BORDER.with_left(px(0))
601 }
602 } else {
603 BUTTON_BORDER
604 },
605 justify_content: JustifyContent::Center,
606 align_items: AlignItems::Center,
607 padding: BUTTON_PADDING,
608 border_radius: match is_left_right {
609 Some(true) => BorderRadius::ZERO.with_left(BUTTON_BORDER_RADIUS_SIZE),
610 Some(false) => BorderRadius::ZERO.with_right(BUTTON_BORDER_RADIUS_SIZE),
611 None => BorderRadius::all(BUTTON_BORDER_RADIUS_SIZE),
612 },
613 ..default()
614 },
615 BUTTON_BORDER_COLOR,
616 BackgroundColor(Color::BLACK),
617 RadioButton,
618 WidgetClickSender(setting),
619 children![(widgets::ui_text(label, Color::WHITE), RadioButtonText)],
620 )
621}
622
623fn rotate_model(
624 mut query: Query<&mut Transform, Or<(With<CubeModel>, With<FlightHelmetModel>)>>,
625 time: Res<Time>,
626) {
627 for mut transform in query.iter_mut() {
628 transform.rotation = Quat::from_rotation_y(time.elapsed_secs());
630 }
631}
632
633fn move_camera(
635 keyboard_input: Res<ButtonInput<KeyCode>>,
636 mut mouse_wheel_reader: MessageReader<MouseWheel>,
637 mut cameras: Query<&mut Transform, With<Camera>>,
638) {
639 let (mut distance_delta, mut theta_delta) = (0.0, 0.0);
640
641 if keyboard_input.pressed(KeyCode::KeyW) {
643 distance_delta -= CAMERA_KEYBOARD_ZOOM_SPEED;
644 }
645 if keyboard_input.pressed(KeyCode::KeyS) {
646 distance_delta += CAMERA_KEYBOARD_ZOOM_SPEED;
647 }
648 if keyboard_input.pressed(KeyCode::KeyA) {
649 theta_delta += CAMERA_KEYBOARD_ORBIT_SPEED;
650 }
651 if keyboard_input.pressed(KeyCode::KeyD) {
652 theta_delta -= CAMERA_KEYBOARD_ORBIT_SPEED;
653 }
654
655 for mouse_wheel in mouse_wheel_reader.read() {
657 distance_delta -= mouse_wheel.y * CAMERA_MOUSE_WHEEL_ZOOM_SPEED;
658 }
659
660 for mut camera_transform in cameras.iter_mut() {
662 let local_z = camera_transform.local_z().as_vec3().normalize_or_zero();
663 if distance_delta != 0.0 {
664 camera_transform.translation = (camera_transform.translation.length() + distance_delta)
665 .clamp(CAMERA_ZOOM_RANGE.start, CAMERA_ZOOM_RANGE.end)
666 * local_z;
667 }
668 if theta_delta != 0.0 {
669 camera_transform
670 .translate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, theta_delta));
671 camera_transform.look_at(Vec3::ZERO, Vec3::Y);
672 }
673 }
674}
675
676fn adjust_app_settings(
678 mut commands: Commands,
679 mut app_settings: ResMut<AppSettings>,
680 mut cameras: Query<Entity, With<Camera>>,
681 mut visibilities: Query<&mut Visibility>,
682 model_queries: ModelQueries,
683 mut widget_click_events: MessageReader<WidgetClickEvent<ExampleSetting>>,
684 mut background_colors: Query<&mut BackgroundColor>,
685 radio_buttons: Query<
686 (
687 Entity,
688 Has<BackgroundColor>,
689 Has<Text>,
690 &WidgetClickSender<ExampleSetting>,
691 ),
692 Or<(With<RadioButton>, With<RadioButtonText>)>,
693 >,
694 range_value_text: Query<(Entity, &RangeValueText)>,
695 text_children: Query<&Children>,
696 mut writer: TextUiWriter,
697 text_query: Query<Entity, With<Text>>,
698) {
699 let mut any_changes = false;
700
701 for event in widget_click_events.read() {
702 any_changes = true;
703 match **event {
704 ExampleSetting::Ssr(on) => app_settings.ssr_on = on,
705 ExampleSetting::Model(model) => app_settings.displayed_model = model,
706 ExampleSetting::Base(base) => app_settings.displayed_base = base,
707 ExampleSetting::MinRoughnessStart(adj) => {
708 app_settings.min_perceptual_roughness.start =
709 adjust(app_settings.min_perceptual_roughness.start, adj, 0.005);
710 }
711 ExampleSetting::MinRoughnessEnd(adj) => {
712 app_settings.min_perceptual_roughness.end =
713 adjust(app_settings.min_perceptual_roughness.end, adj, 0.005);
714 }
715 ExampleSetting::MaxRoughnessStart(adj) => {
716 app_settings.max_perceptual_roughness.start =
717 adjust(app_settings.max_perceptual_roughness.start, adj, 0.005);
718 }
719 ExampleSetting::MaxRoughnessEnd(adj) => {
720 app_settings.max_perceptual_roughness.end =
721 adjust(app_settings.max_perceptual_roughness.end, adj, 0.005);
722 }
723 ExampleSetting::EdgeFadeoutStart(adj) => {
724 app_settings.edge_fadeout.start =
725 adjust(app_settings.edge_fadeout.start, adj, 0.001);
726 }
727 ExampleSetting::EdgeFadeoutEnd(adj) => {
728 app_settings.edge_fadeout.end = adjust(app_settings.edge_fadeout.end, adj, 0.001);
729 }
730 }
731 }
732
733 if !any_changes {
734 return;
735 }
736
737 for camera in cameras.iter_mut() {
739 if app_settings.ssr_on {
740 commands.entity(camera).insert(ScreenSpaceReflections {
741 min_perceptual_roughness: app_settings.min_perceptual_roughness.clone(),
742 max_perceptual_roughness: app_settings.max_perceptual_roughness.clone(),
743 edge_fadeout: app_settings.edge_fadeout.clone(),
744 ..default()
745 });
746 } else {
747 commands.entity(camera).remove::<ScreenSpaceReflections>();
748 }
749 }
750
751 for entity in model_queries.cube_models.iter() {
753 if let Ok(mut visibility) = visibilities.get_mut(entity) {
754 *visibility = if app_settings.displayed_model == DisplayedModel::Cube {
755 Visibility::Visible
756 } else {
757 Visibility::Hidden
758 };
759 }
760 }
761 for entity in model_queries.flight_helmet_models.iter() {
762 if let Ok(mut visibility) = visibilities.get_mut(entity) {
763 *visibility = if app_settings.displayed_model == DisplayedModel::FlightHelmet {
764 Visibility::Visible
765 } else {
766 Visibility::Hidden
767 };
768 }
769 }
770 for entity in model_queries.capsule_models.iter() {
771 if let Ok(mut visibility) = visibilities.get_mut(entity) {
772 *visibility = if app_settings.displayed_model == DisplayedModel::Capsules {
773 Visibility::Visible
774 } else {
775 Visibility::Hidden
776 };
777 }
778 }
779 for entity in model_queries.metallic_base_models.iter() {
780 if let Ok(mut visibility) = visibilities.get_mut(entity) {
781 *visibility = if app_settings.displayed_base == DisplayedBase::Metallic {
782 Visibility::Visible
783 } else {
784 Visibility::Hidden
785 };
786 }
787 }
788 for entity in model_queries.non_metallic_base_models.iter() {
789 if let Ok(mut visibility) = visibilities.get_mut(entity) {
790 *visibility = if app_settings.displayed_base == DisplayedBase::RedPlane {
791 Visibility::Visible
792 } else {
793 Visibility::Hidden
794 };
795 }
796 }
797 for entity in model_queries.water_models.iter() {
798 if let Ok(mut visibility) = visibilities.get_mut(entity) {
799 *visibility = if app_settings.displayed_base == DisplayedBase::Water {
800 Visibility::Visible
801 } else {
802 Visibility::Hidden
803 };
804 }
805 }
806
807 for (entity, has_background, has_text, sender) in radio_buttons.iter() {
809 let selected = match **sender {
810 ExampleSetting::Ssr(on) => app_settings.ssr_on == on,
811 ExampleSetting::Model(model) => app_settings.displayed_model == model,
812 ExampleSetting::Base(base) => app_settings.displayed_base == base,
813 _ => {
814 if has_background
815 && let Ok(mut background_color) = background_colors.get_mut(entity)
816 {
817 *background_color = BackgroundColor(Color::BLACK);
818 }
819 if has_text {
820 update_ui_radio_button_text(entity, &mut writer, false);
821 }
822 continue;
823 }
824 };
825
826 if has_background && let Ok(mut background_color) = background_colors.get_mut(entity) {
827 update_ui_radio_button(&mut background_color, selected);
828 }
829 if has_text {
830 update_ui_radio_button_text(entity, &mut writer, selected);
831 }
832 }
833
834 for (parent, marker) in range_value_text.iter() {
836 let val = match marker {
837 RangeValueText::MinRoughnessStart => app_settings.min_perceptual_roughness.start,
838 RangeValueText::MinRoughnessEnd => app_settings.min_perceptual_roughness.end,
839 RangeValueText::MaxRoughnessStart => app_settings.max_perceptual_roughness.start,
840 RangeValueText::MaxRoughnessEnd => app_settings.max_perceptual_roughness.end,
841 RangeValueText::EdgeFadeoutStart => app_settings.edge_fadeout.start,
842 RangeValueText::EdgeFadeoutEnd => app_settings.edge_fadeout.end,
843 };
844 if let Ok(children) = text_children.get(parent) {
845 for child in children.iter() {
846 if text_query.get(child).is_ok() {
847 *writer.text(child, 0) = format!("{:.2}", val);
848 writer.for_each_color(child, |mut color| {
849 color.0 = Color::BLACK;
850 });
851 }
852 }
853 }
854 }
855}
856
857impl MaterialExtension for Water {
858 fn deferred_fragment_shader() -> ShaderRef {
859 SHADER_ASSET_PATH.into()
860 }
861}
862
863fn adjust(val: f32, adj: Adjustment, amount: f32) -> f32 {
864 match adj {
865 Adjustment::Increase => (val + amount).min(1.0),
866 Adjustment::Decrease => (val - amount).max(0.0),
867 }
868}
869
870impl Default for AppSettings {
871 fn default() -> Self {
872 Self {
873 ssr_on: true,
874 displayed_model: default(),
875 displayed_base: default(),
876 min_perceptual_roughness: 0.0..0.01,
877 max_perceptual_roughness: 0.99..1.0,
878 edge_fadeout: 0.0..0.0,
879 }
880 }
881}