use crate::prelude::*; type MyMat = ExtendedMaterial; fn main() { App::new() .add_plugins(( DefaultPlugins.set(ImagePlugin::default_nearest()), MaterialPlugin::::default(), )) .insert_resource(Msaa::Off) .add_systems(Startup, setup) .add_systems(Update, apply_skybox) .add_systems(Update, set_scene) .add_systems(Update, rotate) .add_systems( Update, toggle_material .run_if(|keys: Res>| -> bool { keys.just_pressed(KeyCode::Space) }), ) .add_systems( Update, init_materials .run_if(|events: Query>| -> bool { !events.is_empty() }), ) .run(); } #[derive(Component)] struct Root; fn setup(mut commands: Commands, assets: Res) { commands.spawn((SceneBundle { ..default() }, Root)); commands.spawn(PointLightBundle { point_light: PointLight { intensity: 10.0, ..default() }, transform: Transform::from_xyz(-1.0, 4.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }); commands.spawn((Camera3dBundle { transform: Transform::from_xyz(-1.0, 1.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() },)); } fn apply_skybox( cameras: Query>, mut images: ResMut>, assets: Res, mut commands: Commands, mut done: Local, mut handle: Local>, ) { if !*done { info!("Applying skybox..."); *handle = assets.load("images/skybox.png"); if let Some(image) = images.get_mut(handle.clone()) { info!("Loaded skybox image"); // NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture, // so they appear as one texture. The following code reconfigures the texture as necessary. if image.texture_descriptor.array_layer_count() == 1 { image.reinterpret_stacked_2d_as_array( image.texture_descriptor.size.height / image.texture_descriptor.size.width, ); image.texture_view_descriptor = Some(TextureViewDescriptor { dimension: Some(TextureViewDimension::Cube), ..default() }); cameras.iter().for_each(|e| { commands.entity(e).insert(Skybox { image: handle.clone(), brightness: 1.0 }); }); *done = true; } } else { *done = false; } } } fn set_scene( mut events: Query<&mut Handle, (With, Added)>, assets: Res, ) { events.iter_mut().for_each(|mut scene| { *scene = assets.load("models/Martian Chess.glb#Scene0"); }); } #[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] struct MatExt { #[uniform(100)] cutoff: f32, } impl MaterialExtension for MatExt { fn fragment_shader() -> ShaderRef { "examples/shaders/dissolve.wgsl".into() } } #[derive(Debug, Component)] struct Backup(T); fn init_materials( events: Query<(Entity, &Handle), Added>>, standard_materials: Res>, mut materials: ResMut>, mut commands: Commands, ) { info!("initializing materials"); events.iter().for_each(|(entity, std_handle)| { // Extension we will add to existing gltf-sourced materials let extension = MatExt { cutoff: 1.0 }; // Base material we will extend for the duration of the dissolve effect let mut base = standard_materials .get(std_handle) .expect("Resolve material data") .clone(); base.opaque_render_method = OpaqueRendererMethod::Auto; let ext_handle = materials.add(ExtendedMaterial { base, extension }); commands.entity(entity).insert(Backup(ext_handle.clone())); }); } fn toggle_material( query: Query>, With>)>>, std_mat: Query<(&Handle, Option<&Backup>>)>, ext_mat: Query<(&Handle, Option<&Backup>>)>, mut commands: Commands, ) { query.iter().for_each(|entity| { // Entity has standard material, and had extended material // Swap these materials if let Ok((std_handle, Some(Backup(ext_handle)))) = std_mat.get(entity) { info!("Swapping standard material for extended material"); commands .entity(entity) .insert(ext_handle.clone()) .insert(Backup(std_handle.clone())) .remove::>>() .remove::>(); return; } // In this branch we have the extended material assigned to the object if let Ok((ext_handle, Some(Backup(std_handle)))) = ext_mat.get(entity) { // Entity has standard material, and had extended material // Swap these materials info!("Swapping extended material for standard material"); commands .entity(entity) .insert(std_handle.clone()) .insert(Backup(ext_handle.clone())) .remove::>>() .remove::>(); return; } panic!("What is happening?") }); } fn rotate( mut query: Query<&mut Transform, With>>, time: Res