// postfx_config.cpp - Implementación del cargador de YAML del postpro. #include "core/config/postfx_config.hpp" #include #include "core/resources/resource_helper.hpp" #include "external/fkyaml_node.hpp" namespace Config::PostFx { namespace { // Helper: lee `key` en `node` solo si existe; deja `dst` intacto en caso // contrario. Así, un YAML parcial sigue funcionando con los defaults del // struct para los campos que falten. template void readField(const fkyaml::node& node, const char* key, T& dst) { if (node.contains(key)) { dst = node[key].get_value(); } } // Lee un array RGB [r, g, b] (0..255) y lo normaliza a [0..1] sobre tres // destinos floats. Si la clave no existe o no es secuencia de 3, deja los // destinos como están. void readRgb255(const fkyaml::node& node, const char* key, float& dst_r, float& dst_g, float& dst_b) { if (!node.contains(key)) { return; } const auto& arr = node[key]; if (!arr.is_sequence() || arr.size() < 3) { return; } try { const auto R = arr[0].get_value(); const auto G = arr[1].get_value(); const auto B = arr[2].get_value(); dst_r = static_cast(R) / 255.0F; dst_g = static_cast(G) / 255.0F; dst_b = static_cast(B) / 255.0F; } catch (...) { // @INTENTIONAL // Mantiene los defaults si algún elemento del RGB no es entero parseable // (el YAML viene de archivo, así que es razonable degradar a los defaults // en vez de propagar la excepción y abortar el load del postpro entero). } } } // namespace auto load(const std::string& path) -> Rendering::GPU::PostFxParams { Rendering::GPU::PostFxParams params{}; // valores por defecto del struct auto bytes = Resource::Helper::loadFile(path); if (bytes.empty()) { std::cerr << "[PostFxConfig] No se pudo cargar " << path << " — usando defaults built-in\n"; return params; } try { const auto* begin = reinterpret_cast(bytes.data()); const auto* end = begin + bytes.size(); auto yaml = fkyaml::node::deserialize(begin, end); if (yaml.contains("bloom") && yaml["bloom"].is_mapping()) { const auto& node = yaml["bloom"]; readField(node, "enabled", params.bloom_enabled); readField(node, "intensity", params.bloom_intensity); readField(node, "threshold", params.bloom_threshold); // sigma_px és el paràmetre canònic des del separable blur; acceptem // també `radius_px` com a alias per a configs antigues (s'interpreta // com sigma directament — els valors útils estan al mateix rang ~2-5). readField(node, "sigma_px", params.bloom_sigma_px); readField(node, "radius_px", params.bloom_sigma_px); } if (yaml.contains("flicker") && yaml["flicker"].is_mapping()) { const auto& node = yaml["flicker"]; readField(node, "enabled", params.flicker_enabled); readField(node, "amplitude", params.flicker_amplitude); readField(node, "frequency_hz", params.flicker_frequency_hz); } if (yaml.contains("background") && yaml["background"].is_mapping()) { const auto& node = yaml["background"]; readField(node, "enabled", params.background_enabled); readRgb255(node, "color_min", params.background_min_r, params.background_min_g, params.background_min_b); readRgb255(node, "color_max", params.background_max_r, params.background_max_g, params.background_max_b); readField(node, "pulse_frequency_hz", params.background_pulse_freq_hz); } std::cout << "[PostFxConfig] Cargado " << path << " (bloom=" << (params.bloom_enabled ? "on" : "off") << " intensity=" << params.bloom_intensity << ", flicker=" << (params.flicker_enabled ? "on" : "off") << " amp=" << params.flicker_amplitude << ", bg=" << (params.background_enabled ? "on" : "off") << ")\n"; } catch (const fkyaml::exception& e) { std::cerr << "[PostFxConfig] Error parseando " << path << ": " << e.what() << " — usando defaults built-in\n"; } return params; } } // namespace Config::PostFx