diff --git a/crates/benilla-app/src/post/grading.rs b/crates/benilla-app/src/post/grading.rs index 5a4dc42a..7d2b28d6 100644 --- a/crates/benilla-app/src/post/grading.rs +++ b/crates/benilla-app/src/post/grading.rs @@ -25,7 +25,7 @@ use bevy::{ renderer::{RenderContext, RenderDevice, RenderQueue}, texture::GpuImage, view::ViewTarget, - RenderApp, RenderStartup, + Render, RenderApp, RenderStartup, RenderSystems, }, }; use std::collections::{HashMap, HashSet}; @@ -43,10 +43,66 @@ struct GradeCatalog { struct GradeView { day: Handle, night: Handle, - // x = authored strength, y = night blend. + /// MONKEY (polish): the pair being faded out (identity once the fade is done). + prev_day: Handle, + prev_night: Handle, + // x = authored strength, y = night blend, z = crossfade weight of the current pair (1 = done), + // w = the previous pair's strength. control: Vec4, } +/// MONKEY (polish): the zone crossfade's length; a border crossing no longer snaps the grade. +const GRADE_FADE_S: f32 = 2.0; + +/// One zone's LUT pair at its authored strength; a zone with no row grades at strength 0. +#[derive(Clone, PartialEq, Debug)] +struct GradePair { + day: Handle, + night: Handle, + strength: f32, +} + +/// MONKEY (polish): the crossfade from the previous zone's pair to the current one. +#[derive(Resource, Default)] +struct GradeBlend { + current: Option, + previous: Option, + /// The current pair's weight, 0..1. + fade: f32, + primed: bool, +} + +impl GradeBlend { + /// Steps toward `target`. The first graded zone after login (or re-enabling) and a disabled + /// grade snap; a return to the + /// pair still fading out reverses the fade instead of restarting it. + fn step(&mut self, target: Option, dt: f32, snap: bool) { + if snap || !self.primed { + *self = Self { + primed: !snap && target.is_some(), + current: target, + previous: None, + fade: 1.0, + }; + return; + } + if target != self.current { + if self.fade < 1.0 && target == self.previous { + std::mem::swap(&mut self.current, &mut self.previous); + self.fade = 1.0 - self.fade; + } else { + self.previous = self.current.take(); + self.current = target; + self.fade = 0.0; + } + } + self.fade = (self.fade + dt / GRADE_FADE_S).min(1.0); + if self.fade >= 1.0 { + self.previous = None; + } + } +} + #[derive(Clone, Copy, ShaderType)] struct GradeUniform { control: Vec4, @@ -55,6 +111,7 @@ struct GradeUniform { impl Plugin for GradingPlugin { fn build(&self, app: &mut App) { app.add_plugins(ExtractComponentPlugin::::default()) + .init_resource::() .add_systems(Last, update_views); if !app.is_plugin_added::() { return; @@ -73,6 +130,10 @@ impl Plugin for GradingPlugin { render_app .insert_resource(GradeShader(shader)) .add_systems(RenderStartup, init_pipeline) + .add_systems( + Render, + prepare_uniforms.in_set(RenderSystems::PrepareResources), + ) .add_render_graph_node::>(Core3d, GradeLabel) .add_render_graph_edges( Core3d, @@ -215,6 +276,7 @@ fn volume_image(cube: Vec) -> Image { ) } +#[allow(clippy::too_many_arguments)] fn update_views( mut commands: Commands, video: Res, @@ -222,6 +284,8 @@ fn update_views( area: Res, areas: Option>, time: Res, + clock: Res