Files
aee-arcade/source/core/rendering/effects/scan_reveal.hpp
T

97 lines
3.6 KiB
C++

#pragma once
#include <cstdint>
#include <random>
class Surface;
namespace Effects {
// Revelat tipus "scanline CRT" d'una Surface. Una banda lluminosa
// baixa des de dalt cap a baix de la imatge: les files damunt de la
// banda es renderitzen normalment (ja revelades), les files sota
// queden invisibles, i a la banda mateixa s'aplica un overlay
// blanc semi-transparent. Al voltant de la banda les files reben un
// jitter horitzontal aleatori (falloff lineal amb la distància) i
// damunt la part ja revelada apareixen ocasionalment "bursts VHS" —
// franges horitzontals desplaçades durant uns ms — per a un toc
// demoscene/VHS deteriorat.
//
// Quan la banda surt per sota del logo i s'esgota `duration_ms`,
// dispara un flash blanc breu. Si `tail_glitch_ms > 0`, després del
// flash el logo queda totalment visible mentre els bursts VHS
// continuen saltant durant aquest temps (glitch tail). Quan el tail
// s'exhaureix `isFinished()=true`. El caller decideix què fa amb el
// control de fase a partir d'aquí.
//
// Ús:
// ScanReveal sr;
// sr.begin({.duration_ms = 1500, ...});
// // cada frame:
// sr.tick(delta_ms);
// sr.render(*logo, x, y);
// if (sr.isFinished()) { ... canvi de fase ... }
class ScanReveal {
public:
struct Params {
int duration_ms;
int band_height_px;
int band_alpha;
float band_pulse_hz;
float band_pulse_depth;
int glitch_amplitude_px;
int glitch_band_radius_px;
int vhs_burst_per_sec;
int vhs_burst_duration_ms;
int vhs_burst_height_px;
int vhs_burst_amplitude_px;
int flash_ms;
// Glitch tail post-flash: el logo queda visible sencer i els
// bursts VHS continuen saltant durant aquest temps. 0 ho
// desactiva (efecte acaba al flash).
int tail_glitch_ms;
};
ScanReveal();
void begin(const Params& params);
void tick(int delta_ms);
// Renderitza un frame del revelat sobre la Surface `src`. El
// caller s'encarrega de pintar negre per sota (les files no
// revelades simplement no es dibuixen — queda el que hi haja al
// canvas, normalment negre).
void render(const Surface& src, int x, int y) const;
[[nodiscard]] auto isActive() const -> bool;
[[nodiscard]] auto isFinished() const -> bool;
private:
Params params_{};
int elapsed_ms_{0};
// Flash post-scan: arranca quan el scan toca fons; decreix amb
// `delta_ms` fins a 0. Mentre `flash_active_` és true encara no
// hem reportat finished (donant marge per pintar el flash sencer).
int flash_remaining_ms_{0};
bool flash_started_{false};
// Burst VHS actiu (com a màxim un alhora — simplifica i ja és
// suficient per al ritme visual). y_norm ∈ [0,1] es resol en
// píxels al `render()` perquè aquí no coneixem l'alçada del logo.
int burst_remaining_ms_{0};
float burst_y_norm_{0.0F};
int burst_dx_{0};
// Glitch tail post-flash: arranca quan el flash s'esgota i
// permet que els bursts continuen saltant amb el logo ja del tot
// visible. `isFinished()` espera al final d'aquest tail.
bool tail_started_{false};
int tail_remaining_ms_{0};
// RNG per als bursts. Seed des de random_device a `begin()`
// perquè cada execució sigui visualment diferent.
std::minstd_rand rng_;
};
} // namespace Effects