feat(lighting): corba additive separada al config

This commit is contained in:
JailDesigner committed 2026-05-13 17:53:32 +02:00
1 parent 771ff1f6cd
commit dacc4a9909
7 files changed
+100 -34

No files matched your search

+18 -2
View File
@@ -12,8 +12,13 @@
# mid_ratio..1.0 → outer_intensity (anell exterior)
# fora del radi → light_level de la sala
#
# Les intensitats són factors 0..1 que es multipliquen sobre el píxel
# final del canvas. inner=1.0 vol dir "píxel sense alterar".
# Hi ha dues corbes paral·leles, una per mode, perquè la mateixa escala
# 0..1 vol dir coses molt diferents:
# `gradient` → subtractive: 1.0 = "no fosqueix" el píxel
# (l'emissor "trenca" una sala fosca).
# `gradient_additive` → additive: 1.0 = "afegeix el color sencer"
# (satura). Valors molt més baixos donen un
# halo cromàtic subtil sobre una sala normal.
gradient:
inner_ratio: 0.5
mid_ratio: 0.85
@@ -21,6 +26,17 @@ gradient:
mid_intensity: 0.8
outer_intensity: 0.6
# Corba del mode additive. Valors petits a propòsit: només volem un
# toc de tint cromàtic prop de l'emissor — fora del nucli, la sala
# original ha de quedar pràcticament intacta. Si pugeu massa l'inner,
# els colors de l'emissor "inunden" la sala (additiu satura ràpid).
gradient_additive:
inner_ratio: 0.5
mid_ratio: 0.85
inner_intensity: 0.15
mid_intensity: 0.10
outer_intensity: 0.05
# Modulació del radi quan una entitat declara `flicker: true` al seu
# bloc `light:`. El radi efectiu per frame és:
# r_eff = r_base + A1·sin(2π·f1·t + φ1) + A2·sin(2π·f2·t + φ2)
+11 -11
View File
@@ -15,20 +15,20 @@ tileset: data/tilesets/pyramid_1.gif
tile_size: 16
origin: [-8, 0]
# Banc de proves del mode `additive` d'il·luminació. La sala queda
# lleugerament fosca (level 0.9) i els emissors hi sumen color per
# damunt — antorxes càlides, jugador en blanc-blau fred, mòmies amb
# halo verd-fantasma. En mode additive el `color` de cada emissor entra
# en joc (en subtractive s'ignora). Veure docs al header de
# `core/rendering/lighting.hpp` per a la fórmula i el comportament al
# solape (suma → tendència al blanc, físicament correcte).
# Banc de proves del mode `additive` d'il·luminació. La sala es veu
# "normal" (level 1.0, el GIF intacte fora del cercle de les torxes)
# i només les antorxes hi sumen un halo càlid suau. Player i mummies
# desactivats explícitament perquè aquí no volem cap halo al voltant
# d'actors — el toc cromàtic és només l'antorxa. La corba additiva
# (intensitats baixes a propòsit perquè additive satura ràpid) viu a
# `gradient_additive:` de data/config/lighting.yaml.
lighting:
mode: additive
level: 0.9
level: 1.0
emitters:
torches: { color: [255, 170, 60] }
players: { color: [180, 220, 255] }
mummies: { color: [120, 220, 140] }
torches: { radius: 24, color: [255, 170, 60] }
players: false
mummies: false
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
# pintable. Un índex per jugador perquè a multijugador local el recorregut
+40 -14
View File
@@ -26,9 +26,9 @@ namespace Lighting {
return buf_;
}
// Configuració global, inicialitzada amb els defaults compilats.
// loadConfig() la reemplaça si troba el fitxer.
auto activeConfig() -> GradientConfig& {
// Corba activa del mode subtractive. loadConfig() la reemplaça
// si troba el bloc `gradient:` al YAML.
auto activeSubtractiveConfig() -> GradientConfig& {
static GradientConfig cfg_{
.inner_ratio = Defaults::Lighting::INNER_RATIO,
.mid_ratio = Defaults::Lighting::MID_RATIO,
@@ -39,6 +39,22 @@ namespace Lighting {
return cfg_;
}
// Corba activa del mode additive. Defaults baixos a propòsit:
// en additive la intensitat és l'alfa del color afegit, així
// que valors petits (0.05..0.15) donen el "halo subtil" que
// sol voler-se. loadConfig() la reemplaça si troba el bloc
// `gradient_additive:` al YAML.
auto activeAdditiveConfig() -> GradientConfig& {
static GradientConfig cfg_{
.inner_ratio = Defaults::Lighting::INNER_RATIO_ADDITIVE,
.mid_ratio = Defaults::Lighting::MID_RATIO_ADDITIVE,
.inner_intensity = Defaults::Lighting::INNER_INTENSITY_ADDITIVE,
.mid_intensity = Defaults::Lighting::MID_INTENSITY_ADDITIVE,
.outer_intensity = Defaults::Lighting::OUTER_INTENSITY_ADDITIVE,
};
return cfg_;
}
auto activeFlicker() -> FlickerConfig& {
static FlickerConfig fc_{
.wave_amplitude_px = Defaults::Lighting::FLICKER_WAVE_AMPLITUDE_PX,
@@ -232,9 +248,22 @@ namespace Lighting {
} // namespace
auto config() -> const GradientConfig& {
return activeConfig();
return activeSubtractiveConfig();
}
namespace {
// Llegeix els 5 camps de `GradientConfig` d'un node YAML — qualsevol
// camp absent conserva el valor compilat dels defaults. Compartit
// entre els blocs `gradient:` i `gradient_additive:`.
void readGradient(const fkyaml::node& g, GradientConfig& cfg) {
readField(g, "inner_ratio", cfg.inner_ratio);
readField(g, "mid_ratio", cfg.mid_ratio);
readField(g, "inner_intensity", cfg.inner_intensity);
readField(g, "mid_intensity", cfg.mid_intensity);
readField(g, "outer_intensity", cfg.outer_intensity);
}
} // namespace
void loadConfig(const std::string& yaml_path) {
auto bytes = Resource::Helper::loadFile(yaml_path);
if (bytes.empty()) {
@@ -246,13 +275,10 @@ namespace Lighting {
const auto* end = begin + bytes.size();
auto yaml = fkyaml::node::deserialize(begin, end);
if (yaml.contains("gradient")) {
const auto& g = yaml["gradient"];
auto& cfg = activeConfig();
readField(g, "inner_ratio", cfg.inner_ratio);
readField(g, "mid_ratio", cfg.mid_ratio);
readField(g, "inner_intensity", cfg.inner_intensity);
readField(g, "mid_intensity", cfg.mid_intensity);
readField(g, "outer_intensity", cfg.outer_intensity);
readGradient(yaml["gradient"], activeSubtractiveConfig());
}
if (yaml.contains("gradient_additive")) {
readGradient(yaml["gradient_additive"], activeAdditiveConfig());
}
if (yaml.contains("flicker")) {
const auto& f = yaml["flicker"];
@@ -285,7 +311,7 @@ namespace Lighting {
if (lum.size() != TOTAL) { lum.resize(TOTAL); }
std::ranges::fill(lum, room);
const auto& cfg = activeConfig();
const auto& cfg = activeSubtractiveConfig();
const auto& fc = activeFlicker();
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
for (const auto& e : emitters) {
@@ -303,7 +329,7 @@ namespace Lighting {
// canvas, sense buffer intermig.
void applyAdditive(std::uint32_t* canvas, int width, int height, std::uint8_t room, std::span<const Emitter> emitters) {
darkenCanvas(canvas, width, height, room);
const auto& cfg = activeConfig();
const auto& cfg = activeAdditiveConfig();
const auto& fc = activeFlicker();
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
for (const auto& e : emitters) {
@@ -327,7 +353,7 @@ namespace Lighting {
const std::uint8_t ROOM = toByte(room_light);
if (ROOM == 255 && emitters.empty()) { return; }
if (mode == Mode::Additive) {
if (mode == Mode::ADDITIVE) {
applyAdditive(canvas, width, height, ROOM, emitters);
} else {
applySubtractive(canvas, width, height, ROOM, emitters);
+14 -3
View File
@@ -33,6 +33,14 @@
// inner_ratio..mid_ratio → mid_intensity
// mid_ratio..1.0 → outer_intensity
//
// La corba viu en dos blocs separats al YAML perquè la mateixa escala
// 0..1 té significats molt diferents per mode:
// `gradient:` → corba del mode subtractive (1.0 = "no fosqueix")
// `gradient_additive:` → corba del mode additive (1.0 = "afegeix
// el color sencer i satura"). Es vol molt més
// baixa per a no inundar la sala de color.
// Cada mode llegeix la seva pròpia corba — no es comparteixen valors.
//
// Combinació entre emissors:
// SUBTRACTIVE → per a cada píxel es pren el màxim de la intensitat
// aportada per cada emissor que el solapa (no se sumen intensitats).
@@ -45,8 +53,8 @@ namespace Lighting {
// Mode de composició. La sala el selecciona via `lighting.mode:
// additive | subtractive` (default subtractive si manca, retrocompat).
enum class Mode : std::uint8_t {
Subtractive,
Additive,
SUBTRACTIVE,
ADDITIVE,
};
// Color emès. Default blanc (sense modificació quan s'usa en mode
@@ -104,7 +112,10 @@ namespace Lighting {
// simplement reemplaça la configuració activa.
void loadConfig(const std::string& yaml_path);
// Configuració activa (per a debug/inspectors). Inicialitzada amb els
// Configuració activa (per a debug/inspectors). Retorna sempre la
// corba subtractive — la corba additive viu en paral·lel al cpp i,
// si algun dia es necessita inspeccionar des de fora, caldrà afegir
// una variant `configAdditive()`. Inicialitzada amb els
// Defaults::Lighting fins que loadConfig() es crida.
[[nodiscard]] auto config() -> const GradientConfig&;
+3 -3
View File
@@ -73,7 +73,7 @@ namespace Map {
// d'una sala anterior.
void loadLightingBlock(const fkyaml::node& yaml) {
g_room_emitters.clear();
lighting_mode = Lighting::Mode::Subtractive;
lighting_mode = Lighting::Mode::SUBTRACTIVE;
if (!yaml.contains("lighting")) { return; }
const auto& lighting = yaml["lighting"];
if (lighting.contains("level")) {
@@ -82,9 +82,9 @@ namespace Map {
if (lighting.contains("mode")) {
const auto MODE_STR = lighting["mode"].get_value<std::string>();
if (MODE_STR == "additive") {
lighting_mode = Lighting::Mode::Additive;
lighting_mode = Lighting::Mode::ADDITIVE;
} else if (MODE_STR == "subtractive") {
lighting_mode = Lighting::Mode::Subtractive;
lighting_mode = Lighting::Mode::SUBTRACTIVE;
} else {
Log::warn("Map", "lighting.mode '{}' desconegut — s'usa subtractive", MODE_STR);
}
+1 -1
View File
@@ -69,7 +69,7 @@ namespace Map {
// Veure el comentari de `core/rendering/lighting.hpp` per a la
// semàntica de cada mode. En subtractive, els colors dels emissors
// s'ignoren — el camí és el de sempre.
inline Lighting::Mode lighting_mode = Lighting::Mode::Subtractive;
inline Lighting::Mode lighting_mode = Lighting::Mode::SUBTRACTIVE;
// Override d'emissor declarat al bloc opcional `lighting.emitters.<tipus>`
// de la sala. Tres formes acceptades al YAML:
+13
View File
@@ -111,11 +111,24 @@ namespace Defaults {
// fitxer falta o li manca alguna clau. La forma final (3 anells) es
// descriu al YAML; veure `Lighting::GradientConfig` per a la semàntica.
namespace Lighting {
// Defaults del gradient subtractive (mode històric). Les
// intensitats són multiplicadors 0..1 que es multipliquen sobre
// el píxel: 1.0 = no toca, 0.0 = negre.
constexpr float INNER_RATIO = 0.8F;
constexpr float MID_RATIO = 0.9F;
constexpr float INNER_INTENSITY = 1.0F;
constexpr float MID_INTENSITY = 0.9F;
constexpr float OUTER_INTENSITY = 0.8F;
// Defaults del gradient additive. Les intensitats són "alfa"
// del color afegit (0..1): 1.0 = afegeix el color sencer i
// satura, 0.0 = no afegeix res. Valors baixos a propòsit perquè
// l'additive es nota fort molt fàcilment — només un toc de tint
// cromàtic al voltant de l'emissor.
constexpr float INNER_RATIO_ADDITIVE = 0.5F;
constexpr float MID_RATIO_ADDITIVE = 0.85F;
constexpr float INNER_INTENSITY_ADDITIVE = 0.15F;
constexpr float MID_INTENSITY_ADDITIVE = 0.10F;
constexpr float OUTER_INTENSITY_ADDITIVE = 0.05F;
// Nivell d'il·luminació de sala per defecte. Una sala sense la
// clau `light_level` queda totalment il·luminada (com hui).
constexpr float ROOM_LIGHT_DEFAULT = 1.0F;