//! This examples compares Tonemapping options use bevy::{ core_pipeline::tonemapping::Tonemapping, math::vec2, pbr::CascadeShadowConfigBuilder, prelude::*, reflect::TypePath, render::{ render_asset::RenderAssetPersistencePolicy, render_resource::{AsBindGroup, Extent3d, ShaderRef, TextureDimension, TextureFormat}, texture::{ImageSampler, ImageSamplerDescriptor}, view::ColorGrading, }, utils::HashMap, }; use std::f32::consts::PI; fn main() { App::new() .add_plugins(( DefaultPlugins, MaterialPlugin::::default(), )) .insert_resource(CameraTransform( Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), )) .init_resource::() .insert_resource(CurrentScene(1)) .insert_resource(SelectedParameter { value: 0, max: 4 }) .add_systems( Startup, ( setup, setup_basic_scene, setup_color_gradient_scene, setup_image_viewer_scene, ), ) .add_systems( Update, ( update_image_viewer, toggle_scene, toggle_tonemapping_method, update_color_grading_settings, update_ui, ), ) .run(); } fn setup( mut commands: Commands, asset_server: Res, camera_transform: Res, ) { // camera commands.spawn(( Camera3dBundle { camera: Camera { hdr: true, ..default() }, transform: camera_transform.0, ..default() }, FogSettings { color: Color::rgba_u8(43, 44, 47, 255), falloff: FogFalloff::Linear { start: 1.0, end: 8.0, }, ..default() }, EnvironmentMapLight { diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 150.0, }, )); // ui commands.spawn( TextBundle::from_section( "", TextStyle { font_size: 18.0, ..default() }, ) .with_style(Style { position_type: PositionType::Absolute, top: Val::Px(10.0), left: Val::Px(10.0), ..default() }), ); } fn setup_basic_scene( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, mut images: ResMut>, asset_server: Res, ) { // plane commands.spawn(( PbrBundle { mesh: meshes.add(shape::Plane::from_size(50.0)), material: materials.add(Color::rgb(0.1, 0.2, 0.1)), ..default() }, SceneNumber(1), )); // cubes let cube_material = materials.add(StandardMaterial { base_color_texture: Some(images.add(uv_debug_texture())), ..default() }); let cube_mesh = meshes.add(shape::Cube { size: 0.25 }); for i in 0..5 { commands.spawn(( PbrBundle { mesh: cube_mesh.clone(), material: cube_material.clone(), transform: Transform::from_xyz(i as f32 * 0.25 - 1.0, 0.125, -i as f32 * 0.5), ..default() }, SceneNumber(1), )); } // spheres let sphere_mesh = meshes.add(shape::UVSphere { radius: 0.125, ..default() }); for i in 0..6 { let j = i % 3; let s_val = if i < 3 { 0.0 } else { 0.2 }; let material = if j == 0 { materials.add(StandardMaterial { base_color: Color::rgb(s_val, s_val, 1.0), perceptual_roughness: 0.089, metallic: 0.0, ..default() }) } else if j == 1 { materials.add(StandardMaterial { base_color: Color::rgb(s_val, 1.0, s_val), perceptual_roughness: 0.089, metallic: 0.0, ..default() }) } else { materials.add(StandardMaterial { base_color: Color::rgb(1.0, s_val, s_val), perceptual_roughness: 0.089, metallic: 0.0, ..default() }) }; commands.spawn(( PbrBundle { mesh: sphere_mesh.clone(), material, transform: Transform::from_xyz( j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } - 0.4, 0.125, -j as f32 * 0.25 + if i < 3 { -0.15 } else { 0.15 } + 0.4, ), ..default() }, SceneNumber(1), )); } // Flight Helmet commands.spawn(( SceneBundle { scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"), transform: Transform::from_xyz(0.5, 0.0, -0.5) .with_rotation(Quat::from_rotation_y(-0.15 * PI)), ..default() }, SceneNumber(1), )); // light commands.spawn(( DirectionalLightBundle { directional_light: DirectionalLight { shadows_enabled: true, illuminance: 3000.0, ..default() }, transform: Transform::from_rotation(Quat::from_euler( EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15, )), cascade_shadow_config: CascadeShadowConfigBuilder { maximum_distance: 3.0, first_cascade_far_bound: 0.9, ..default() } .into(), ..default() }, SceneNumber(1), )); } fn setup_color_gradient_scene( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, camera_transform: Res, ) { let mut transform = camera_transform.0; transform.translation += transform.forward(); commands.spawn(( MaterialMeshBundle { mesh: meshes.add(shape::Quad { size: vec2(1.0, 1.0) * 0.7, flip: false, }), material: materials.add(ColorGradientMaterial {}), transform, visibility: Visibility::Hidden, ..default() }, SceneNumber(2), )); } fn setup_image_viewer_scene( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, camera_transform: Res, ) { let mut transform = camera_transform.0; transform.translation += transform.forward(); // exr/hdr viewer (exr requires enabling bevy feature) commands.spawn(( PbrBundle { mesh: meshes.add(shape::Quad { size: vec2(1.0, 1.0), flip: false, }), material: materials.add(StandardMaterial { base_color_texture: None, unlit: true, ..default() }), transform, visibility: Visibility::Hidden, ..default() }, SceneNumber(3), HDRViewer, )); commands .spawn(( TextBundle::from_section( "Drag and drop an HDR or EXR file", TextStyle { font_size: 36.0, color: Color::BLACK, ..default() }, ) .with_text_justify(JustifyText::Center) .with_style(Style { align_self: AlignSelf::Center, margin: UiRect::all(Val::Auto), ..default() }), SceneNumber(3), )) .insert(Visibility::Hidden); } // ---------------------------------------------------------------------------- #[allow(clippy::too_many_arguments)] fn update_image_viewer( image_mesh: Query<(&Handle, &Handle), With>, text: Query, With)>, mut materials: ResMut>, mut meshes: ResMut>, images: Res>, mut drop_events: EventReader, mut drop_hovered: Local, asset_server: Res, mut image_events: EventReader>, mut commands: Commands, ) { let mut new_image: Option> = None; for event in drop_events.read() { match event { FileDragAndDrop::DroppedFile { path_buf, .. } => { new_image = Some(asset_server.load(&path_buf.to_string_lossy().to_string())); *drop_hovered = false; } FileDragAndDrop::HoveredFile { .. } => *drop_hovered = true, FileDragAndDrop::HoveredFileCanceled { .. } => *drop_hovered = false, } } for (mat_h, mesh_h) in &image_mesh { if let Some(mat) = materials.get_mut(mat_h) { if let Some(ref new_image) = new_image { mat.base_color_texture = Some(new_image.clone()); if let Ok(text_entity) = text.get_single() { commands.entity(text_entity).despawn(); } } for event in image_events.read() { let image_changed_id = *match event { AssetEvent::Added { id } | AssetEvent::Modified { id } => id, _ => continue, }; if let Some(base_color_texture) = mat.base_color_texture.clone() { if image_changed_id == base_color_texture.id() { if let Some(image_changed) = images.get(image_changed_id) { let size = image_changed.size_f32().normalize_or_zero() * 1.4; // Resize Mesh let quad = Mesh::from(shape::Quad::new(size)); meshes.insert(mesh_h, quad); } } } } } } } fn toggle_scene( keys: Res>, mut query: Query<(&mut Visibility, &SceneNumber)>, mut current_scene: ResMut, ) { let mut pressed = None; if keys.just_pressed(KeyCode::KeyQ) { pressed = Some(1); } else if keys.just_pressed(KeyCode::KeyW) { pressed = Some(2); } else if keys.just_pressed(KeyCode::KeyE) { pressed = Some(3); } if let Some(pressed) = pressed { current_scene.0 = pressed; for (mut visibility, scene) in query.iter_mut() { if scene.0 == pressed { *visibility = Visibility::Visible; } else { *visibility = Visibility::Hidden; } } } } fn toggle_tonemapping_method( keys: Res>, mut tonemapping: Query<&mut Tonemapping>, mut color_grading: Query<&mut ColorGrading>, per_method_settings: Res, ) { let mut method = tonemapping.single_mut(); let mut color_grading = color_grading.single_mut(); if keys.just_pressed(KeyCode::Digit1) { *method = Tonemapping::None; } else if keys.just_pressed(KeyCode::Digit2) { *method = Tonemapping::Reinhard; } else if keys.just_pressed(KeyCode::Digit3) { *method = Tonemapping::ReinhardLuminance; } else if keys.just_pressed(KeyCode::Digit4) { *method = Tonemapping::AcesFitted; } else if keys.just_pressed(KeyCode::Digit5) { *method = Tonemapping::AgX; } else if keys.just_pressed(KeyCode::Digit6) { *method = Tonemapping::SomewhatBoringDisplayTransform; } else if keys.just_pressed(KeyCode::Digit7) { *method = Tonemapping::TonyMcMapface; } else if keys.just_pressed(KeyCode::Digit8) { *method = Tonemapping::BlenderFilmic; } *color_grading = *per_method_settings .settings .get::(&method) .unwrap(); } #[derive(Resource)] struct SelectedParameter { value: i32, max: i32, } impl SelectedParameter { fn next(&mut self) { self.value = (self.value + 1).rem_euclid(self.max); } fn prev(&mut self) { self.value = (self.value - 1).rem_euclid(self.max); } } fn update_color_grading_settings( keys: Res>, time: Res