feat(lighting): corba additive separada al config
This commit is contained in:
1 parent
771ff1f6cd
commit
dacc4a9909
7 files changed
+100
-34
No files matched your search
@@ -12,8 +12,13 @@
|
|||||||
# mid_ratio..1.0 → outer_intensity (anell exterior)
|
# mid_ratio..1.0 → outer_intensity (anell exterior)
|
||||||
# fora del radi → light_level de la sala
|
# fora del radi → light_level de la sala
|
||||||
#
|
#
|
||||||
# Les intensitats són factors 0..1 que es multipliquen sobre el píxel
|
# Hi ha dues corbes paral·leles, una per mode, perquè la mateixa escala
|
||||||
# final del canvas. inner=1.0 vol dir "píxel sense alterar".
|
# 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:
|
gradient:
|
||||||
inner_ratio: 0.5
|
inner_ratio: 0.5
|
||||||
mid_ratio: 0.85
|
mid_ratio: 0.85
|
||||||
@@ -21,6 +26,17 @@ gradient:
|
|||||||
mid_intensity: 0.8
|
mid_intensity: 0.8
|
||||||
outer_intensity: 0.6
|
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
|
# Modulació del radi quan una entitat declara `flicker: true` al seu
|
||||||
# bloc `light:`. El radi efectiu per frame és:
|
# bloc `light:`. El radi efectiu per frame és:
|
||||||
# r_eff = r_base + A1·sin(2π·f1·t + φ1) + A2·sin(2π·f2·t + φ2)
|
# r_eff = r_base + A1·sin(2π·f1·t + φ1) + A2·sin(2π·f2·t + φ2)
|
||||||
|
|||||||
+11
-11
@@ -15,20 +15,20 @@ tileset: data/tilesets/pyramid_1.gif
|
|||||||
tile_size: 16
|
tile_size: 16
|
||||||
origin: [-8, 0]
|
origin: [-8, 0]
|
||||||
|
|
||||||
# Banc de proves del mode `additive` d'il·luminació. La sala queda
|
# Banc de proves del mode `additive` d'il·luminació. La sala es veu
|
||||||
# lleugerament fosca (level 0.9) i els emissors hi sumen color per
|
# "normal" (level 1.0, el GIF intacte fora del cercle de les torxes)
|
||||||
# damunt — antorxes càlides, jugador en blanc-blau fred, mòmies amb
|
# i només les antorxes hi sumen un halo càlid suau. Player i mummies
|
||||||
# halo verd-fantasma. En mode additive el `color` de cada emissor entra
|
# desactivats explícitament perquè aquí no volem cap halo al voltant
|
||||||
# en joc (en subtractive s'ignora). Veure docs al header de
|
# d'actors — el toc cromàtic és només l'antorxa. La corba additiva
|
||||||
# `core/rendering/lighting.hpp` per a la fórmula i el comportament al
|
# (intensitats baixes a propòsit perquè additive satura ràpid) viu a
|
||||||
# solape (suma → tendència al blanc, físicament correcte).
|
# `gradient_additive:` de data/config/lighting.yaml.
|
||||||
lighting:
|
lighting:
|
||||||
mode: additive
|
mode: additive
|
||||||
level: 0.9
|
level: 1.0
|
||||||
emitters:
|
emitters:
|
||||||
torches: { color: [255, 170, 60] }
|
torches: { radius: 24, color: [255, 170, 60] }
|
||||||
players: { color: [180, 220, 255] }
|
players: false
|
||||||
mummies: { color: [120, 220, 140] }
|
mummies: false
|
||||||
|
|
||||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
# Í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
|
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ namespace Lighting {
|
|||||||
return buf_;
|
return buf_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuració global, inicialitzada amb els defaults compilats.
|
// Corba activa del mode subtractive. loadConfig() la reemplaça
|
||||||
// loadConfig() la reemplaça si troba el fitxer.
|
// si troba el bloc `gradient:` al YAML.
|
||||||
auto activeConfig() -> GradientConfig& {
|
auto activeSubtractiveConfig() -> GradientConfig& {
|
||||||
static GradientConfig cfg_{
|
static GradientConfig cfg_{
|
||||||
.inner_ratio = Defaults::Lighting::INNER_RATIO,
|
.inner_ratio = Defaults::Lighting::INNER_RATIO,
|
||||||
.mid_ratio = Defaults::Lighting::MID_RATIO,
|
.mid_ratio = Defaults::Lighting::MID_RATIO,
|
||||||
@@ -39,6 +39,22 @@ namespace Lighting {
|
|||||||
return cfg_;
|
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& {
|
auto activeFlicker() -> FlickerConfig& {
|
||||||
static FlickerConfig fc_{
|
static FlickerConfig fc_{
|
||||||
.wave_amplitude_px = Defaults::Lighting::FLICKER_WAVE_AMPLITUDE_PX,
|
.wave_amplitude_px = Defaults::Lighting::FLICKER_WAVE_AMPLITUDE_PX,
|
||||||
@@ -232,9 +248,22 @@ namespace Lighting {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
auto config() -> const GradientConfig& {
|
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) {
|
void loadConfig(const std::string& yaml_path) {
|
||||||
auto bytes = Resource::Helper::loadFile(yaml_path);
|
auto bytes = Resource::Helper::loadFile(yaml_path);
|
||||||
if (bytes.empty()) {
|
if (bytes.empty()) {
|
||||||
@@ -246,13 +275,10 @@ namespace Lighting {
|
|||||||
const auto* end = begin + bytes.size();
|
const auto* end = begin + bytes.size();
|
||||||
auto yaml = fkyaml::node::deserialize(begin, end);
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
if (yaml.contains("gradient")) {
|
if (yaml.contains("gradient")) {
|
||||||
const auto& g = yaml["gradient"];
|
readGradient(yaml["gradient"], activeSubtractiveConfig());
|
||||||
auto& cfg = activeConfig();
|
}
|
||||||
readField(g, "inner_ratio", cfg.inner_ratio);
|
if (yaml.contains("gradient_additive")) {
|
||||||
readField(g, "mid_ratio", cfg.mid_ratio);
|
readGradient(yaml["gradient_additive"], activeAdditiveConfig());
|
||||||
readField(g, "inner_intensity", cfg.inner_intensity);
|
|
||||||
readField(g, "mid_intensity", cfg.mid_intensity);
|
|
||||||
readField(g, "outer_intensity", cfg.outer_intensity);
|
|
||||||
}
|
}
|
||||||
if (yaml.contains("flicker")) {
|
if (yaml.contains("flicker")) {
|
||||||
const auto& f = yaml["flicker"];
|
const auto& f = yaml["flicker"];
|
||||||
@@ -285,7 +311,7 @@ namespace Lighting {
|
|||||||
if (lum.size() != TOTAL) { lum.resize(TOTAL); }
|
if (lum.size() != TOTAL) { lum.resize(TOTAL); }
|
||||||
std::ranges::fill(lum, room);
|
std::ranges::fill(lum, room);
|
||||||
|
|
||||||
const auto& cfg = activeConfig();
|
const auto& cfg = activeSubtractiveConfig();
|
||||||
const auto& fc = activeFlicker();
|
const auto& fc = activeFlicker();
|
||||||
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
||||||
for (const auto& e : emitters) {
|
for (const auto& e : emitters) {
|
||||||
@@ -303,7 +329,7 @@ namespace Lighting {
|
|||||||
// canvas, sense buffer intermig.
|
// canvas, sense buffer intermig.
|
||||||
void applyAdditive(std::uint32_t* canvas, int width, int height, std::uint8_t room, std::span<const Emitter> emitters) {
|
void applyAdditive(std::uint32_t* canvas, int width, int height, std::uint8_t room, std::span<const Emitter> emitters) {
|
||||||
darkenCanvas(canvas, width, height, room);
|
darkenCanvas(canvas, width, height, room);
|
||||||
const auto& cfg = activeConfig();
|
const auto& cfg = activeAdditiveConfig();
|
||||||
const auto& fc = activeFlicker();
|
const auto& fc = activeFlicker();
|
||||||
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
const float T_S = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
||||||
for (const auto& e : emitters) {
|
for (const auto& e : emitters) {
|
||||||
@@ -327,7 +353,7 @@ namespace Lighting {
|
|||||||
const std::uint8_t ROOM = toByte(room_light);
|
const std::uint8_t ROOM = toByte(room_light);
|
||||||
if (ROOM == 255 && emitters.empty()) { return; }
|
if (ROOM == 255 && emitters.empty()) { return; }
|
||||||
|
|
||||||
if (mode == Mode::Additive) {
|
if (mode == Mode::ADDITIVE) {
|
||||||
applyAdditive(canvas, width, height, ROOM, emitters);
|
applyAdditive(canvas, width, height, ROOM, emitters);
|
||||||
} else {
|
} else {
|
||||||
applySubtractive(canvas, width, height, ROOM, emitters);
|
applySubtractive(canvas, width, height, ROOM, emitters);
|
||||||
|
|||||||
@@ -33,6 +33,14 @@
|
|||||||
// inner_ratio..mid_ratio → mid_intensity
|
// inner_ratio..mid_ratio → mid_intensity
|
||||||
// mid_ratio..1.0 → outer_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:
|
// Combinació entre emissors:
|
||||||
// SUBTRACTIVE → per a cada píxel es pren el màxim de la intensitat
|
// 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).
|
// 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:
|
// Mode de composició. La sala el selecciona via `lighting.mode:
|
||||||
// additive | subtractive` (default subtractive si manca, retrocompat).
|
// additive | subtractive` (default subtractive si manca, retrocompat).
|
||||||
enum class Mode : std::uint8_t {
|
enum class Mode : std::uint8_t {
|
||||||
Subtractive,
|
SUBTRACTIVE,
|
||||||
Additive,
|
ADDITIVE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Color emès. Default blanc (sense modificació quan s'usa en mode
|
// Color emès. Default blanc (sense modificació quan s'usa en mode
|
||||||
@@ -104,7 +112,10 @@ namespace Lighting {
|
|||||||
// simplement reemplaça la configuració activa.
|
// simplement reemplaça la configuració activa.
|
||||||
void loadConfig(const std::string& yaml_path);
|
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.
|
// Defaults::Lighting fins que loadConfig() es crida.
|
||||||
[[nodiscard]] auto config() -> const GradientConfig&;
|
[[nodiscard]] auto config() -> const GradientConfig&;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace Map {
|
|||||||
// d'una sala anterior.
|
// d'una sala anterior.
|
||||||
void loadLightingBlock(const fkyaml::node& yaml) {
|
void loadLightingBlock(const fkyaml::node& yaml) {
|
||||||
g_room_emitters.clear();
|
g_room_emitters.clear();
|
||||||
lighting_mode = Lighting::Mode::Subtractive;
|
lighting_mode = Lighting::Mode::SUBTRACTIVE;
|
||||||
if (!yaml.contains("lighting")) { return; }
|
if (!yaml.contains("lighting")) { return; }
|
||||||
const auto& lighting = yaml["lighting"];
|
const auto& lighting = yaml["lighting"];
|
||||||
if (lighting.contains("level")) {
|
if (lighting.contains("level")) {
|
||||||
@@ -82,9 +82,9 @@ namespace Map {
|
|||||||
if (lighting.contains("mode")) {
|
if (lighting.contains("mode")) {
|
||||||
const auto MODE_STR = lighting["mode"].get_value<std::string>();
|
const auto MODE_STR = lighting["mode"].get_value<std::string>();
|
||||||
if (MODE_STR == "additive") {
|
if (MODE_STR == "additive") {
|
||||||
lighting_mode = Lighting::Mode::Additive;
|
lighting_mode = Lighting::Mode::ADDITIVE;
|
||||||
} else if (MODE_STR == "subtractive") {
|
} else if (MODE_STR == "subtractive") {
|
||||||
lighting_mode = Lighting::Mode::Subtractive;
|
lighting_mode = Lighting::Mode::SUBTRACTIVE;
|
||||||
} else {
|
} else {
|
||||||
Log::warn("Map", "lighting.mode '{}' desconegut — s'usa subtractive", MODE_STR);
|
Log::warn("Map", "lighting.mode '{}' desconegut — s'usa subtractive", MODE_STR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace Map {
|
|||||||
// Veure el comentari de `core/rendering/lighting.hpp` per a la
|
// Veure el comentari de `core/rendering/lighting.hpp` per a la
|
||||||
// semàntica de cada mode. En subtractive, els colors dels emissors
|
// semàntica de cada mode. En subtractive, els colors dels emissors
|
||||||
// s'ignoren — el camí és el de sempre.
|
// 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>`
|
// Override d'emissor declarat al bloc opcional `lighting.emitters.<tipus>`
|
||||||
// de la sala. Tres formes acceptades al YAML:
|
// de la sala. Tres formes acceptades al YAML:
|
||||||
|
|||||||
@@ -111,11 +111,24 @@ namespace Defaults {
|
|||||||
// fitxer falta o li manca alguna clau. La forma final (3 anells) es
|
// fitxer falta o li manca alguna clau. La forma final (3 anells) es
|
||||||
// descriu al YAML; veure `Lighting::GradientConfig` per a la semàntica.
|
// descriu al YAML; veure `Lighting::GradientConfig` per a la semàntica.
|
||||||
namespace Lighting {
|
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 INNER_RATIO = 0.8F;
|
||||||
constexpr float MID_RATIO = 0.9F;
|
constexpr float MID_RATIO = 0.9F;
|
||||||
constexpr float INNER_INTENSITY = 1.0F;
|
constexpr float INNER_INTENSITY = 1.0F;
|
||||||
constexpr float MID_INTENSITY = 0.9F;
|
constexpr float MID_INTENSITY = 0.9F;
|
||||||
constexpr float OUTER_INTENSITY = 0.8F;
|
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
|
// Nivell d'il·luminació de sala per defecte. Una sala sense la
|
||||||
// clau `light_level` queda totalment il·luminada (com hui).
|
// clau `light_level` queda totalment il·luminada (com hui).
|
||||||
constexpr float ROOM_LIGHT_DEFAULT = 1.0F;
|
constexpr float ROOM_LIGHT_DEFAULT = 1.0F;
|
||||||
|
|||||||
Reference in new issue
Block a user