diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index 8a24d98..e80aa26 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -81,12 +81,12 @@ namespace GlobalInputs { if (ss) consumed = true; ss_prev = ss; - // F7 — Canviar shader (PostFX ↔ CrtPi) + // F7 — Canviar tipus de shader (PostFX ↔ CrtPi) bool next_shader = JI_KeyPressed(Options::keys_gui.next_shader); if (next_shader && !next_shader_prev) { - Screen::get()->nextShaderPreset(); - char msg[32]; - snprintf(msg, sizeof(msg), "SHADER: %s", Screen::get()->getActiveShaderName()); + Screen::get()->nextShaderType(); + char msg[64]; + snprintf(msg, sizeof(msg), "%s: %s", Screen::get()->getActiveShaderName(), Screen::get()->getCurrentPresetName()); Overlay::showNotification(msg); } if (next_shader) consumed = true; @@ -95,8 +95,10 @@ namespace GlobalInputs { // F8 — Pròxim preset del shader actiu bool next_preset = JI_KeyPressed(Options::keys_gui.next_shader_preset); if (next_preset && !next_preset_prev) { - // TODO: ciclar presets quan estiguen implementats (YAML) - Overlay::showNotification("PRESET: DEFAULT"); + Screen::get()->nextPreset(); + char msg[64]; + snprintf(msg, sizeof(msg), "PRESET: %s", Screen::get()->getCurrentPresetName()); + Overlay::showNotification(msg); } if (next_preset) consumed = true; next_preset_prev = next_preset; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index c474d74..5b7543d 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -187,10 +187,9 @@ void Screen::toggleStretchFilter() { } } -void Screen::nextShaderPreset() { +void Screen::nextShaderType() { if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return; - // Cicla entre PostFX i CrtPi if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) { shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI); applyCurrentCrtPiPreset(); @@ -200,6 +199,32 @@ void Screen::nextShaderPreset() { } } +void Screen::nextPreset() { + if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return; + + if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) { + if (Options::postfx_presets.empty()) return; + Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast(Options::postfx_presets.size()); + applyCurrentPostFXPreset(); + } else { + if (Options::crtpi_presets.empty()) return; + Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % static_cast(Options::crtpi_presets.size()); + applyCurrentCrtPiPreset(); + } +} + +auto Screen::getCurrentPresetName() const -> const char* { + if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "---"; + if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) { + if (Options::current_postfx_preset < static_cast(Options::postfx_presets.size())) + return Options::postfx_presets[Options::current_postfx_preset].name.c_str(); + } else { + if (Options::current_crtpi_preset < static_cast(Options::crtpi_presets.size())) + return Options::crtpi_presets[Options::current_crtpi_preset].name.c_str(); + } + return "---"; +} + void Screen::setActiveShader(Rendering::ShaderType type) { if (shader_backend_) { shader_backend_->setActiveShader(type); @@ -207,24 +232,38 @@ void Screen::setActiveShader(Rendering::ShaderType type) { } void Screen::applyCurrentPostFXPreset() { - if (!shader_backend_) return; - // Preset per defecte "CRT" — futur: carregar des de YAML + if (!shader_backend_ || Options::postfx_presets.empty()) return; + const auto& preset = Options::postfx_presets[Options::current_postfx_preset]; Rendering::PostFXParams p; - p.vignette = 0.4F; - p.scanlines = 0.5F; - p.chroma = 0.1F; - p.mask = 0.0F; - p.gamma = 0.0F; - p.curvature = 0.0F; - p.bleeding = 0.0F; - p.flicker = 0.0F; + p.vignette = preset.vignette; + p.scanlines = preset.scanlines; + p.chroma = preset.chroma; + p.mask = preset.mask; + p.gamma = preset.gamma; + p.curvature = preset.curvature; + p.bleeding = preset.bleeding; + p.flicker = preset.flicker; shader_backend_->setPostFXParams(p); } void Screen::applyCurrentCrtPiPreset() { - if (!shader_backend_) return; - // Preset per defecte — futur: carregar des de YAML + if (!shader_backend_ || Options::crtpi_presets.empty()) return; + const auto& preset = Options::crtpi_presets[Options::current_crtpi_preset]; Rendering::CrtPiParams p; + p.scanline_weight = preset.scanline_weight; + p.scanline_gap_brightness = preset.scanline_gap_brightness; + p.bloom_factor = preset.bloom_factor; + p.input_gamma = preset.input_gamma; + p.output_gamma = preset.output_gamma; + p.mask_brightness = preset.mask_brightness; + p.curvature_x = preset.curvature_x; + p.curvature_y = preset.curvature_y; + p.mask_type = preset.mask_type; + p.enable_scanlines = preset.enable_scanlines; + p.enable_multisample = preset.enable_multisample; + p.enable_gamma = preset.enable_gamma; + p.enable_curvature = preset.enable_curvature; + p.enable_sharper = preset.enable_sharper; shader_backend_->setCrtPiParams(p); } diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index bb8f63a..5b12cac 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -28,7 +28,9 @@ class Screen { void toggleAspectRatio(); void toggleIntegerScale(); void toggleStretchFilter(); - void nextShaderPreset(); + void nextShaderType(); // Cicla PostFX ↔ CrtPi (F7) + void nextPreset(); // Cicla presets del shader actiu (F8) + [[nodiscard]] auto getCurrentPresetName() const -> const char*; void setActiveShader(Rendering::ShaderType type); void applyCurrentPostFXPreset(); void applyCurrentCrtPiPreset(); diff --git a/source/game/options.cpp b/source/game/options.cpp index df3a72e..2a1f3f6 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -197,4 +197,154 @@ namespace Options { return true; } + // --- Helper per a parsejar floats de YAML --- + static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) { + if (node.contains(key)) { + try { + target = node[key].get_value(); + } catch (...) {} + } + } + + // --- Presets PostFX --- + + void setPostFXFile(const std::string& path) { postfx_file_path = path; } + + auto loadPostFXFromFile() -> bool { + postfx_presets.clear(); + + std::ifstream file(postfx_file_path); + if (!file.good()) { + std::cout << "PostFX file not found, creating defaults: " << postfx_file_path << '\n'; + // Escriure defaults + std::ofstream out(postfx_file_path); + if (out.is_open()) { + out << "# Aventures En Egipte - PostFX Shader Presets\n\n"; + out << "presets:\n"; + out << " - name: \"CRT\"\n vignette: 0.6\n scanlines: 0.7\n chroma: 0.15\n mask: 0.6\n gamma: 0.8\n"; + out << " - name: \"NTSC\"\n vignette: 0.4\n scanlines: 0.5\n chroma: 0.2\n mask: 0.4\n gamma: 0.5\n bleeding: 0.6\n"; + out << " - name: \"CURVED\"\n vignette: 0.5\n scanlines: 0.6\n chroma: 0.1\n mask: 0.5\n gamma: 0.7\n curvature: 0.8\n"; + out << " - name: \"SCANLINES\"\n scanlines: 0.8\n vignette: 0.0\n chroma: 0.0\n"; + out << " - name: \"SUBTLE\"\n vignette: 0.3\n scanlines: 0.4\n chroma: 0.05\n gamma: 0.3\n"; + out << " - name: \"CRT LIVE\"\n vignette: 0.5\n scanlines: 0.6\n chroma: 0.3\n mask: 0.3\n gamma: 0.4\n curvature: 0.3\n bleeding: 0.4\n flicker: 0.8\n"; + out.close(); + } + postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F}); + postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F}); + postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F}); + postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F}); + postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F}); + postfx_presets.push_back({"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F}); + current_postfx_preset = 0; + return true; + } + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + try { + auto yaml = fkyaml::node::deserialize(content); + if (yaml.contains("presets")) { + for (const auto& p : yaml["presets"]) { + PostFXPreset preset; + if (p.contains("name")) preset.name = p["name"].get_value(); + parseFloatField(p, "vignette", preset.vignette); + parseFloatField(p, "scanlines", preset.scanlines); + parseFloatField(p, "chroma", preset.chroma); + parseFloatField(p, "mask", preset.mask); + parseFloatField(p, "gamma", preset.gamma); + parseFloatField(p, "curvature", preset.curvature); + parseFloatField(p, "bleeding", preset.bleeding); + parseFloatField(p, "flicker", preset.flicker); + postfx_presets.push_back(preset); + } + } + current_postfx_preset = 0; + std::cout << "PostFX loaded: " << postfx_presets.size() << " preset(s)\n"; + return true; + } catch (const fkyaml::exception& e) { + std::cerr << "Error parsing PostFX YAML: " << e.what() << '\n'; + postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F}); + current_postfx_preset = 0; + return false; + } + } + + // --- Presets CrtPi --- + + void setCrtPiFile(const std::string& path) { crtpi_file_path = path; } + + auto loadCrtPiFromFile() -> bool { + crtpi_presets.clear(); + + std::ifstream file(crtpi_file_path); + if (!file.good()) { + std::cout << "CrtPi file not found, creating defaults: " << crtpi_file_path << '\n'; + std::ofstream out(crtpi_file_path); + if (out.is_open()) { + out << "# Aventures En Egipte - CrtPi Shader Presets\n\n"; + out << "presets:\n"; + out << " - name: \"DEFAULT\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: true\n enable_gamma: true\n enable_curvature: false\n enable_sharper: false\n"; + out << " - name: \"CURVED\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: true\n enable_gamma: true\n enable_curvature: true\n enable_sharper: false\n"; + out << " - name: \"SHARP\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: false\n enable_gamma: true\n enable_curvature: false\n enable_sharper: true\n"; + out << " - name: \"MINIMAL\"\n scanline_weight: 8.0\n scanline_gap_brightness: 0.05\n bloom_factor: 2.0\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 1.00\n curvature_x: 0.0\n curvature_y: 0.0\n mask_type: 0\n enable_scanlines: true\n enable_multisample: false\n enable_gamma: false\n enable_curvature: false\n enable_sharper: false\n"; + out.close(); + } + crtpi_presets.push_back({"DEFAULT", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false}); + crtpi_presets.push_back({"CURVED", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, true, false}); + crtpi_presets.push_back({"SHARP", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, false, true, false, true}); + crtpi_presets.push_back({"MINIMAL", 8.0F, 0.05F, 2.0F, 2.4F, 2.2F, 1.00F, 0.0F, 0.0F, 0, true, false, false, false, false}); + current_crtpi_preset = 0; + return true; + } + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + try { + auto yaml = fkyaml::node::deserialize(content); + if (yaml.contains("presets")) { + for (const auto& p : yaml["presets"]) { + CrtPiPreset preset; + if (p.contains("name")) preset.name = p["name"].get_value(); + parseFloatField(p, "scanline_weight", preset.scanline_weight); + parseFloatField(p, "scanline_gap_brightness", preset.scanline_gap_brightness); + parseFloatField(p, "bloom_factor", preset.bloom_factor); + parseFloatField(p, "input_gamma", preset.input_gamma); + parseFloatField(p, "output_gamma", preset.output_gamma); + parseFloatField(p, "mask_brightness", preset.mask_brightness); + parseFloatField(p, "curvature_x", preset.curvature_x); + parseFloatField(p, "curvature_y", preset.curvature_y); + if (p.contains("mask_type")) try { + preset.mask_type = p["mask_type"].get_value(); + } catch (...) {} + if (p.contains("enable_scanlines")) try { + preset.enable_scanlines = p["enable_scanlines"].get_value(); + } catch (...) {} + if (p.contains("enable_multisample")) try { + preset.enable_multisample = p["enable_multisample"].get_value(); + } catch (...) {} + if (p.contains("enable_gamma")) try { + preset.enable_gamma = p["enable_gamma"].get_value(); + } catch (...) {} + if (p.contains("enable_curvature")) try { + preset.enable_curvature = p["enable_curvature"].get_value(); + } catch (...) {} + if (p.contains("enable_sharper")) try { + preset.enable_sharper = p["enable_sharper"].get_value(); + } catch (...) {} + crtpi_presets.push_back(preset); + } + } + current_crtpi_preset = 0; + std::cout << "CrtPi loaded: " << crtpi_presets.size() << " preset(s)\n"; + return true; + } catch (const fkyaml::exception& e) { + std::cerr << "Error parsing CrtPi YAML: " << e.what() << '\n'; + crtpi_presets.push_back({"DEFAULT", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false}); + current_crtpi_preset = 0; + return false; + } + } + } // namespace Options diff --git a/source/game/options.hpp b/source/game/options.hpp index 3907a91..690b524 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "game/defaults.hpp" #include "game/defines.hpp" @@ -63,6 +64,38 @@ namespace Options { int vides{Defaults::Game::VIDES}; }; + // Preset PostFX + struct PostFXPreset { + std::string name; + float vignette{0.6F}; + float scanlines{0.7F}; + float chroma{0.15F}; + float mask{0.0F}; + float gamma{0.0F}; + float curvature{0.0F}; + float bleeding{0.0F}; + float flicker{0.0F}; + }; + + // Preset CrtPi + struct CrtPiPreset { + std::string name; + float scanline_weight{6.0F}; + float scanline_gap_brightness{0.12F}; + float bloom_factor{3.5F}; + float input_gamma{2.4F}; + float output_gamma{2.2F}; + float mask_brightness{0.80F}; + float curvature_x{0.05F}; + float curvature_y{0.10F}; + int mask_type{2}; + bool enable_scanlines{true}; + bool enable_multisample{true}; + bool enable_gamma{true}; + bool enable_curvature{false}; + bool enable_sharper{false}; + }; + // --- Variables globals --- inline std::string version{}; inline KeysGUI keys_gui{}; @@ -73,9 +106,24 @@ namespace Options { inline Game game{}; inline std::string config_file_path{}; + // Presets de shaders + inline std::vector postfx_presets{}; + inline std::string postfx_file_path{}; + inline int current_postfx_preset{0}; + + inline std::vector crtpi_presets{}; + inline std::string crtpi_file_path{}; + inline int current_crtpi_preset{0}; + // --- API --- void setConfigFile(const std::string& path); auto loadFromFile() -> bool; auto saveToFile() -> bool; + void setPostFXFile(const std::string& path); + auto loadPostFXFromFile() -> bool; + + void setCrtPiFile(const std::string& path); + auto loadCrtPiFromFile() -> bool; + } // namespace Options diff --git a/source/main.cpp b/source/main.cpp index f227a8a..910032d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -22,6 +22,12 @@ int main(int argc, char* args[]) { Options::setConfigFile(std::string(file_getconfigfolder()) + "config.yaml"); Options::loadFromFile(); + // Carrega presets de shaders + Options::setPostFXFile(std::string(file_getconfigfolder()) + "postfx.yaml"); + Options::loadPostFXFromFile(); + Options::setCrtPiFile(std::string(file_getconfigfolder()) + "crtpi.yaml"); + Options::loadCrtPiFromFile(); + JG_Init(); Screen::init(); JD8_Init();