#pragma once #include #include #include // Sistema d'il·luminació CPU. Treballa sobre el mateix canvas ARGB del // joc (320×240) que tot el pipeline de render: després que GameScene // haja pintat tilemap, paint layer, sombres i actors, una pasada final // multiplica cada píxel del canvas per un factor de luminància 0..1 // derivat de la combinació de "llum de la sala" + cercles emissors al // voltant de certes entitats. // // Forma de cada emissor (radial, 3 anells configurables al fitxer // data/config/lighting.yaml; veure `GradientConfig`): // 0..inner_ratio del radi → inner_intensity // inner_ratio..mid_ratio → mid_intensity // mid_ratio..1.0 → outer_intensity // fora del radi → light_level de la sala // // Combinació entre emissors: per a cada píxel es pren el màxim del // llum de sala i de la intensitat aportada per cada emissor que el // solapa — no se "sumen" intensitats. Coherent amb la idea de "centre // 1.0": el píxel que cau dins de qualsevol nucli no s'enfosqueix. namespace Lighting { // Posició al canvas (en píxels, mateixa unitat que la resta del // motor) i radi efectiu de l'emissor. El centre és el del sprite // visible: és l'entitat qui sap on cau (veure `Entity::getLightCenter`). // `flicker` activa la modulació del radi per frame; `seed` és un // identificador estable per emissor (no canvia amb la posició) del // qual es deriva la fase del flicker — així els emissors mòbils // mantenen un patró propi que no "deriva" pel moviment. struct Emitter { float cx{0.0F}; float cy{0.0F}; int radius{0}; int seed{0}; bool flicker{false}; }; // Forma del gradient. Compartida per tots els emissors per evitar // duplicar la corba a cada YAML d'entitat. Càrrega via loadConfig. struct GradientConfig { float inner_ratio{0.8F}; float mid_ratio{0.9F}; float inner_intensity{1.0F}; float mid_intensity{0.9F}; float outer_intensity{0.8F}; }; // Paràmetres del flicker per a emissors amb `flicker=true`. La // forma final del radi és la suma d'una sinusoide "respiració" // (wave_*) i una sinusoide secundària de freqüència més alta // (noise_*) que aporta la sensació d'inquietud. Les fases es // deriven de la posició de cada emissor — sense estat per emissor. struct FlickerConfig { float wave_amplitude_px{2.0F}; float wave_frequency_hz{3.0F}; float noise_amplitude_px{1.0F}; float noise_frequency_hz{7.0F}; }; // Llegeix `yaml_path` (data/config/lighting.yaml) i pobla la // configuració global. Qualsevol clau absent cau als Defaults::Lighting. // No es crida més d'una vegada en un run normal; tornar-la a cridar // simplement reemplaça la configuració activa. void loadConfig(const std::string& yaml_path); // Configuració activa (per a debug/inspectors). Inicialitzada amb els // Defaults::Lighting fins que loadConfig() es crida. [[nodiscard]] auto config() -> const GradientConfig&; // Aplica la pasada d'il·luminació al canvas in-place. `room_light` // és l'atenuació base de la sala (0..1, 1 = sense atenuació). El // span d'emissors pot ser buit — en aquest cas tota la sala queda // multiplicada per `room_light`. Aquesta funció no fa cap allocació // si es crida amb la mateixa mida de canvas múltiples cops: manté // un buffer intern reutilitzable. void apply(std::uint32_t* canvas, int width, int height, float room_light, std::span emitters); } // namespace Lighting