Post-Effekte über Action::SetPost statt Engine-Zustand
Die PostChain lag als engine::post im Kern, nur damit die Konsole
`post` durch den exec-Trichter schalten konnte — Zustand, der allein
das Render-Frontend angeht und nichts mit der Simulation zu tun hat.
Jetzt kennt der Kern Post-Effekte nur als deferred Action (wie
play_sound): das `post <name> on|off`-Verb queued Action::SetPost,
die Session reicht sie als take_post()-Queue durch, das Fenster wendet
sie auf seine eigene PostChain an. Damit schalten Trigger und Ink-Tags
Effekte, die vorher nur der Konsolenbefehl erreichte.
- engine/post.rs entfällt; PostChain wandert nach render::post
- Session verliert das post-Feld, bekommt pending_post + take_post
- der `post`-Konsolenbefehl ist ein Shim wie `mode`; Ketten-Listing
und nackter Toggle fallen weg (letzterer bewusst, vgl. 8bee832)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196AM4UyAJEXVtPvp5DdKr8
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b3d08d4a8c
commit
247f7ef9fe
+1
-2
@@ -16,10 +16,9 @@ use std::sync::Arc;
|
||||
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::engine::post::PostChain;
|
||||
use crate::engine::tga::Image;
|
||||
use crate::render::math::Mat4;
|
||||
use crate::render::post::Post;
|
||||
use crate::render::post::{Post, PostChain};
|
||||
use crate::render::scene::{Mesh, ScenePass};
|
||||
use crate::render::sprite::SpritePass;
|
||||
use crate::render::ui::Ui;
|
||||
|
||||
+17
-5
@@ -45,10 +45,6 @@ const EYE_SMOOTH_TAU: f32 = 0.06;
|
||||
const EYE_MAX_LAG: f32 = 0.6;
|
||||
|
||||
pub fn run(mut session: Session) {
|
||||
// Post-Effekte anmelden: die Kette gehört dem Renderer, geschaltet wird
|
||||
// sie über denselben exec-Trichter wie alles andere (`post`).
|
||||
for (name, on) in post::effects() { session.post.register(name, on); }
|
||||
|
||||
// Init-Signal feuern, bevor das Fenster steht (kann bereits einen
|
||||
// Dialog öffnen — dann startet die Welt eben pausiert).
|
||||
for line in session.start() { println!("{line}"); }
|
||||
@@ -242,6 +238,9 @@ struct App {
|
||||
/// melden, nicht jeden Klick).
|
||||
audio: Option<audio::Audio>,
|
||||
sfx_cache: HashMap<String, Option<audio::Sound>>,
|
||||
/// Post-Effekt-Kette (Renderer-Zustand, nicht im Kern). Pro Frame aus
|
||||
/// `session.take_post()` nachgeführt, dann an `gpu.frame`.
|
||||
post: post::PostChain,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -271,6 +270,10 @@ impl App {
|
||||
fonts,
|
||||
audio,
|
||||
sfx_cache: HashMap::new(),
|
||||
// Die Post-Kette gehört dem Renderer, nicht dem Kern: geschaltet
|
||||
// wird sie über `Action::SetPost` (Trigger/Ink/Konsole), die die
|
||||
// Session als `take_post`-Queue durchreicht.
|
||||
post: post::PostChain::defaults(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +440,15 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
// Post-Effekt-Schaltungen (SetPost-Actions von Trigger/Ink/Konsole)
|
||||
// auf die eigene Kette anwenden; unbekannter Name wird gemeldet, wie
|
||||
// ein fehlendes WAV bei play_sound.
|
||||
for (name, on) in self.session.take_post() {
|
||||
if !self.post.set(&name, on) {
|
||||
eprintln!("[post] unbekannter Effekt {name:?}");
|
||||
}
|
||||
}
|
||||
|
||||
// Am Boden gleitet die Augenhöhe sanft nach (siehe EYE_SMOOTH_TAU); in
|
||||
// der Luft und außerhalb des Spiels folgt sie exakt.
|
||||
let target_eye = self.session.player.pos[1] + player::EYE_HEIGHT;
|
||||
@@ -494,7 +506,7 @@ impl App {
|
||||
}
|
||||
|
||||
if let Some(gpu) = &mut self.gpu {
|
||||
gpu.frame(&view, &screen.ui, &self.session.post);
|
||||
gpu.frame(&view, &screen.ui, &self.post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+59
-7
@@ -13,15 +13,14 @@
|
||||
//! Effekt über den Frame wissen kann; beide darf ein Effekt ignorieren. Kein
|
||||
//! Effekt kennt einen anderen, keiner darf einen anderen voraussetzen. Ein
|
||||
//! Effekt wird hinzugefügt, indem er in [`EFFECTS`] auftaucht; geschaltet
|
||||
//! wird er über `engine::post` (Konsole: `post`).
|
||||
//! wird er über [`PostChain`], die Trigger, Ink-Tags und die Konsole per
|
||||
//! `game::Action::SetPost` erreichen. Der Kern kennt die Kette nicht.
|
||||
//!
|
||||
//! Gerechnet wird auf zwei Ping-Pong-Targets in interner Auflösung. Bei
|
||||
//! 640×480 kostet ein zusätzlicher Fullscreen-Pass praktisch nichts —
|
||||
//! deshalb darf jeder Effekt seinen eigenen behalten, statt sie zu einem
|
||||
//! Über-Shader zu verschmelzen.
|
||||
|
||||
use crate::engine::post::PostChain;
|
||||
|
||||
/// Die Kette in Ausführungsreihenfolge, mit ihrem Startzustand. `dither`
|
||||
/// quantisiert die Farbtiefe und muss deshalb zuletzt laufen.
|
||||
const EFFECTS: &[(&str, bool, &str)] = &[
|
||||
@@ -30,10 +29,34 @@ const EFFECTS: &[(&str, bool, &str)] = &[
|
||||
("dither", true, include_str!("dither.wgsl")),
|
||||
];
|
||||
|
||||
/// Effektnamen samt Startzustand — das Frontend meldet sie beim Start in der
|
||||
/// Session an.
|
||||
pub fn effects() -> impl Iterator<Item = (&'static str, bool)> {
|
||||
EFFECTS.iter().map(|(n, on, _)| (*n, *on))
|
||||
/// An/Aus je Post-Effekt, in Ketten-Reihenfolge. Das Fenster hält sie als
|
||||
/// eigenen Zustand (nicht der Kern), wendet die per `Session::take_post`
|
||||
/// aufgelaufenen `SetPost`-Schaltungen jeden Frame an und gibt die Kette an
|
||||
/// [`Post::run`].
|
||||
pub struct PostChain {
|
||||
effects: Vec<(String, bool)>,
|
||||
}
|
||||
|
||||
impl PostChain {
|
||||
/// Die Kette aus [`EFFECTS`] mit ihren Startzuständen.
|
||||
pub fn defaults() -> Self {
|
||||
Self {
|
||||
effects: EFFECTS.iter().map(|(n, on, _)| ((*n).to_string(), *on)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enabled(&self, name: &str) -> bool {
|
||||
self.effects.iter().any(|(n, on)| n == name && *on)
|
||||
}
|
||||
|
||||
/// Effekt `name` auf `on` setzen. Rückgabe: `false`, wenn kein Effekt so
|
||||
/// heißt — der Aufrufer meldet das (wie ein fehlendes WAV bei `play_sound`).
|
||||
pub fn set(&mut self, name: &str, on: bool) -> bool {
|
||||
match self.effects.iter_mut().find(|(n, _)| n == name) {
|
||||
Some(e) => { e.1 = on; true }
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spiegelt `Uniforms` in den Effekt-Shadern. `near`/`far` müssen zur
|
||||
@@ -304,3 +327,32 @@ fn make_bind(
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn defaults_follow_effects_table() {
|
||||
let c = PostChain::defaults();
|
||||
// Startzustände aus EFFECTS.
|
||||
assert!(!c.enabled("normals"));
|
||||
assert!(c.enabled("outline"));
|
||||
assert!(c.enabled("dither"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_switches_and_reports_unknown() {
|
||||
let mut c = PostChain::defaults();
|
||||
assert!(c.set("outline", false));
|
||||
assert!(!c.enabled("outline"));
|
||||
assert!(c.set("outline", true));
|
||||
assert!(c.enabled("outline"));
|
||||
assert!(!c.set("bloom", true), "unbekannter Effekt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_effect_is_off() {
|
||||
assert!(!PostChain::defaults().enabled("bloom"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user