Progress toward the dissolve animation

Not sure why the base coat is gray instead of transparent...
main
Elijah C. Voigt 2 years ago
parent eea2f879d0
commit 52efde3756

@ -0,0 +1,81 @@
#import bevy_pbr::{
pbr_fragment::pbr_input_from_standard_material,
forward_io::{VertexOutput, FragmentOutput},
mesh_view_bindings::globals,
pbr_functions::{
alpha_discard,
apply_pbr_lighting,
main_pass_post_lighting_processing
},
}
struct DissovleExtension {
percentage: f32,
}
@group(1) @binding(100)
var<uniform> dissolve: DissovleExtension;
fn random(st: vec2<f32>) -> f32 {
var a = vec2(12.9898, 78.233);
var b = 43758.5453123;
return fract(sin(dot(st.xy, a)) * b);
}
fn noise(st: vec2<f32>) -> f32 {
var i = floor(st);
var f = fract(st);
var a = random(i);
var b = random(i + vec2(1.0, 0.0));
var c = random(i + vec2(0.0, 1.0));
var d = random(i + vec2(1.0, 1.0));
var u = smoothstep(vec2(0.0), vec2(1.0), f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
const octaves: i32 = 6;
fn fbm(st: vec2<f32>) -> f32 {
var _st = st;
var value = 0.0;
var amplitude = 0.5;
var frequency = 0.0;
for (var i = 0 ; i < octaves; i++) {
value += amplitude * noise(_st);
_st *= 2.0;
amplitude *= 0.5;
}
return value;
}
@fragment
fn fragment(
in: VertexOutput,
@builtin(front_facing) is_front: bool,
) -> FragmentOutput {
var pbr_input = pbr_input_from_standard_material(in, is_front);
var out: FragmentOutput;
out.color = apply_pbr_lighting(pbr_input);
var pos = vec2(in.uv * 2.0);
var n = fbm(pos);
var cutoff = dissolve.percentage;
if n > cutoff {
discard;
}
if n > (cutoff - 0.01) {
out.color = vec4(0.0, 1.0, 1.0, 1.0);
}
return out;
}

@ -12,9 +12,9 @@ use bevy::{
Skybox,
},
input::mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
pbr::{ScreenSpaceAmbientOcclusionBundle, ScreenSpaceAmbientOcclusionSettings},
pbr::{ScreenSpaceAmbientOcclusionBundle, ScreenSpaceAmbientOcclusionSettings, MaterialExtension, ExtendedMaterial, OpaqueRendererMethod},
render::{
render_resource::{TextureViewDescriptor, TextureViewDimension},
render_resource::{TextureViewDescriptor, TextureViewDimension, ShaderRef,AsBindGroup},
view::ColorGrading,
},
window::PrimaryWindow,
@ -25,7 +25,10 @@ pub(crate) struct Display3dPlugin;
impl Plugin for Display3dPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(TemporalAntiAliasPlugin)
app.add_plugins((
TemporalAntiAliasPlugin,
MaterialPlugin::<DissolveMaterial>::default(),
))
.insert_resource(Msaa::Off)
.add_systems(
OnExit(GameState::Loading),
@ -1246,43 +1249,90 @@ pub(super) mod tweaks {
}
}
/// Type expressing the extended material of standardMaterial + dissolveMaterial
type DissolveMaterial = ExtendedMaterial<StandardMaterial, DissolveExtension>;
/// Material extension for dissolving effect
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
struct DissolveExtension {
#[uniform(100)]
percentage: f32,
}
impl MaterialExtension for DissolveExtension {
fn fragment_shader() -> ShaderRef {
"shaders/dissolve.wgsl".into()
}
}
// Component for 'backing up' components which are temporarily not used
#[derive(Debug, Component, Clone)]
struct Backup<T: Component>(T);
/// When a piece is captured...
/// 1. Play a cool "captured" animation and a neat sound
/// 2. Move the piece to the side of the board
/// 3. Play the same "captured" animation in reverse
/// The animation is like a 'beam me up scotty' sorta thing.
fn capture_piece(
mut events: Query<Entity, (With<Display3d>, Added<game::Captured>)>,
events: Query<Entity, (With<Display3d>, Added<game::Captured>)>,
mut query: Query<(&mut Visibility, &mut Transform, &Side), (With<Display3d>, With<game::Captured>)>,
mut state: Local<Option<game::CaptureFlow>>,
standard_materials: ResMut<Assets<StandardMaterial>>,
mut dissolve_materials: ResMut<Assets<DissolveMaterial>>,
object_standard_materials: Query<&Handle<StandardMaterial>>,
object_dissolve_materials: Query<&Handle<DissolveMaterial>>,
backup_material: Query<&Backup<Handle<StandardMaterial>>>,
children: Query<&Children>,
mut commands: Commands,
time: Res<Time>,
) {
let duration: f32 = 2.0;
match *state {
Some(s) => {
match s {
game::CaptureFlow::FadeOut(entity) => {
let (mut v, _, _) = query
// TODO: Do we need this?
let (_, _, _) = query
.get_mut(entity)
.expect("Visibility and Transform of captured piece");
// Play fade-out animation
{
error!("Run fade-out animation");
object_dissolve_materials
.iter()
.for_each(|handle| {
let extended_material = dissolve_materials
.get_mut(handle)
.expect("Get the dissolve material");
// Calculate how much of the animation has passed
let delta = time.delta_seconds() / duration;
// Change the material's value to create animation
extended_material.extension.percentage -= delta; // TODO: Tweak this timing
debug!("Play fade out animation {:?} {:?}", delta, extended_material.extension.percentage);
// Move to next state now that animation is done
*state = s.next();
if extended_material.extension.percentage <= 0.0 {
// Set to exactly 0 for simplicity
extended_material.extension.percentage = 0.0;
// Hide piece now that animation is done
*v = Visibility::Hidden;
// Move to next state now that animation is done
*state = s.next();
}
});
}
},
game::CaptureFlow::Store(entity) => {
let (_, mut t, side) = query
let (mut v, mut t, side) = query
.get_mut(entity)
.expect("Visibility and Transform of captured piece");
// Move piece to next spot at side of table
error!("Move piece to side of table");
// Hide piece now that animation is done
*v = Visibility::Hidden;
// TODO: Dynamic number based on side's score
t.translation = capture_translation(side, 1);
@ -1298,18 +1348,67 @@ fn capture_piece(
// Play fade-in animation
{
error!("Run fade-in animation");
// When animation is done, move to next phase of flow
*state = s.next();
}
object_dissolve_materials
.iter()
.for_each(|handle| {
let extended_material = dissolve_materials
.get_mut(handle)
.expect("Get the dissolve material");
// Calculate how much of the animation has passed
let delta = time.delta_seconds() / duration;
// Change the material's value to create animation
extended_material.extension.percentage += delta; // TODO: Tweak this timing
debug!("Play fade in animation {:?} {:?}", delta, extended_material.extension.percentage);
if extended_material.extension.percentage >= 1.0 {
// Move to next state now that animation is done
*state = s.next();
// Remove the captured component for bookkeeping
commands.entity(entity).remove::<game::Captured>();
// Remove the captured component for bookkeeping
commands.entity(entity).remove::<game::Captured>();
// Remove the dissolve material
commands.entity(entity).remove::<Handle<DissolveMaterial>>();
// Re-add the original material
let orig = backup_material.get(entity).expect("Entity has original material");
commands.entity(entity).insert(orig.0.clone());
commands.entity(entity).remove::<Backup<Handle<StandardMaterial>>>();
}
});
}
}
}
},
None => {
*state = events.iter().next().map(|entity| {
children
.iter_descendants(entity)
.filter_map(|e| {
object_standard_materials.get(e).ok().map(|h| (e, h))
})
.for_each(|(child, handle)| {
// Extension we will add to existing gltf-sourced materials
let extension = DissolveExtension { percentage: 1.0 };
// Base material we will extend for the duration of the dissolve effect
let mut base = standard_materials.get(handle).expect("Resolve material data").clone();
base.opaque_render_method = OpaqueRendererMethod::Auto;
base.alpha_mode = AlphaMode::Mask(0.5);
commands.entity(child).insert(
dissolve_materials.add(ExtendedMaterial {
base,
extension,
})
).insert(Backup(handle.clone()))
.remove::<Handle<StandardMaterial>>();
});
// Set the next state to start fading out
game::CaptureFlow::FadeOut(entity)
});
}

@ -50,7 +50,7 @@ pub(crate) enum TurnState {
SideB,
}
#[derive(Debug, Component, Clone, PartialEq, Copy)]
#[derive(Debug, Component, Clone, PartialEq, Copy, Hash)]
pub(crate) enum Piece {
Pawn,
Drone,

Loading…
Cancel
Save