Presets PostFX carregats de shaders/postfx.yaml; Screen aplica params (v0.5.0)

This commit is contained in:
JailDesigner committed 2026-04-18 23:02:06 +02:00
1 parent 6c8ef9ff44
commit 26b4d9e610
7 files changed
+192 -2

No files matched your search

+6
View File
@@ -136,6 +136,12 @@ void Screen::createSdl() {
SDL_SetTextureScaleMode(game_texture_, toSdlScaleMode(config_.texture_filter));
}
void Screen::applyPostFXParams(const Rendering::PostFXParams& params) {
if (shader_backend_ != nullptr) {
shader_backend_->setPostFXParams(params);
}
}
void Screen::clear(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
const std::uint32_t COLOR = (0xFFU << 24) | (static_cast<std::uint32_t>(r) << 16) | (static_cast<std::uint32_t>(g) << 8) | static_cast<std::uint32_t>(b);
std::ranges::fill(canvas_, COLOR);
+4
View File
@@ -44,6 +44,10 @@ class Screen {
// i tanca el subsistema de vídeo. El Director el crida ABANS de SDL_Quit.
void shutdown();
// Reenvia paràmetres PostFX al backend GPU. No-op si estem al path
// SDL_Renderer (fallback) o si encara no hi ha backend.
void applyPostFXParams(const Rendering::PostFXParams& params);
void clear(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0);
void present();
+11
View File
@@ -16,6 +16,7 @@
#include "core/system/event_buffer.hpp"
#include "game/options.hpp"
#include "game/scenes/game_scene.hpp"
#include "utils/defaults.hpp"
Director::Director() {
if (!SDL_Init(0)) {
@@ -30,9 +31,17 @@ Director::Director() {
std::cerr << "Director: no s'ha pogut carregar ni desar config.yaml a " << system_folder_ << '\n';
}
// Carrega els presets de PostFX des de shaders/postfx.yaml; si no existeix
// es crea amb presets per defecte (off, crt, ntsc).
Options::setPostFXFile(system_folder_ + Defaults::Video::POSTFX_FILE);
if (!Options::loadPostFXFromFile()) {
std::cerr << "Director: no s'ha pogut carregar ni desar postfx.yaml.\n";
}
// Screen es configura ara (subsystem + finestra + GPU/renderer); la seua
// destrucció la fa aquest Director (veure ~Director). Res de static dtor.
Screen::get().configure(Options::buildScreenConfig());
Screen::get().applyPostFXParams(Options::buildPostFXParams());
(void)Resource::Cache::get();
current_scene_ = std::make_unique<GameScene>();
@@ -65,6 +74,8 @@ void Director::createSystemFolder() {
std::error_code error;
std::filesystem::create_directories(system_folder_, error);
// Subcarpeta per als presets de shaders. std::filesystem és tolerant si ja existeix.
std::filesystem::create_directories(system_folder_ + "shaders/", error);
if (error) {
std::cerr << "Director: no s'ha pogut crear " << system_folder_ << ": " << error.message() << '\n';
}
+139 -1
View File
@@ -1,5 +1,6 @@
#include "game/options.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
@@ -71,6 +72,15 @@ namespace {
if (node.contains("preferred_driver")) { Options::video.gpu.preferred_driver = node["preferred_driver"].get_value<std::string>(); }
}
void parseShader(const fkyaml::node& video_node) {
if (!video_node.contains("shader")) { return; }
const auto& node = video_node["shader"];
if (node.contains("enabled")) { Options::video.shader.enabled = node["enabled"].get_value<bool>(); }
if (node.contains("current_postfx_preset")) {
Options::video.shader.current_postfx_preset = node["current_postfx_preset"].get_value<std::string>();
}
}
void parseVideo(const fkyaml::node& yaml) {
if (!yaml.contains("video")) { return; }
const auto& node = yaml["video"];
@@ -82,6 +92,7 @@ namespace {
Options::video.texture_filter = filterFromString(node["texture_filter"].get_value<std::string>());
}
parseGpu(node);
parseShader(node);
}
void parseMusic(const fkyaml::node& audio_node) {
@@ -107,6 +118,55 @@ namespace {
parseSound(node);
}
// Presets per defecte de PostFX, usats quan el fitxer postfx.yaml no existeix.
auto defaultPostFXPresets() -> std::vector<Options::PostFXPreset> {
return {
{.name = "off"}, // passthrough (tot a 0)
{.name = "crt",
.vignette = 0.6F,
.scanlines = 0.7F,
.chroma = 0.15F,
.mask = 0.6F,
.gamma = 0.8F,
.curvature = 0.0F,
.bleeding = 0.0F,
.flicker = 0.0F},
{.name = "ntsc",
.vignette = 0.4F,
.scanlines = 0.5F,
.chroma = 0.25F,
.mask = 0.3F,
.gamma = 0.6F,
.curvature = 0.0F,
.bleeding = 0.4F,
.flicker = 0.1F},
};
}
void parsePostFXPreset(const fkyaml::node& node, Options::PostFXPreset& preset) {
if (node.contains("name")) { preset.name = node["name"].get_value<std::string>(); }
if (node.contains("vignette")) { preset.vignette = node["vignette"].get_value<float>(); }
if (node.contains("scanlines")) { preset.scanlines = node["scanlines"].get_value<float>(); }
if (node.contains("chroma")) { preset.chroma = node["chroma"].get_value<float>(); }
if (node.contains("mask")) { preset.mask = node["mask"].get_value<float>(); }
if (node.contains("gamma")) { preset.gamma = node["gamma"].get_value<float>(); }
if (node.contains("curvature")) { preset.curvature = node["curvature"].get_value<float>(); }
if (node.contains("bleeding")) { preset.bleeding = node["bleeding"].get_value<float>(); }
if (node.contains("flicker")) { preset.flicker = node["flicker"].get_value<float>(); }
}
void writePostFXPreset(std::ostringstream& out, const Options::PostFXPreset& preset) {
out << " - name: \"" << preset.name << "\"\n";
out << " vignette: " << preset.vignette << "\n";
out << " scanlines: " << preset.scanlines << "\n";
out << " chroma: " << preset.chroma << "\n";
out << " mask: " << preset.mask << "\n";
out << " gamma: " << preset.gamma << "\n";
out << " curvature: " << preset.curvature << "\n";
out << " bleeding: " << preset.bleeding << "\n";
out << " flicker: " << preset.flicker << "\n";
}
} // namespace
namespace Options {
@@ -166,7 +226,10 @@ namespace Options {
out << " texture_filter: \"" << filterToString(video.texture_filter) << "\"\n";
out << " gpu:\n";
out << " acceleration: " << (video.gpu.acceleration ? "true" : "false") << "\n";
out << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\n\n";
out << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\n";
out << " shader:\n";
out << " enabled: " << (video.shader.enabled ? "true" : "false") << "\n";
out << " current_postfx_preset: \"" << video.shader.current_postfx_preset << "\"\n\n";
out << "audio:\n";
out << " enabled: " << (audio.enabled ? "true" : "false") << "\n";
@@ -193,4 +256,79 @@ namespace Options {
return config;
}
void setPostFXFile(const std::string& path) {
postfx_file_path = path;
}
auto loadPostFXFromFile() -> bool {
if (postfx_file_path.empty()) { return false; }
const std::string CONTENT = readFileOrEmpty(postfx_file_path);
if (CONTENT.empty()) {
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
try {
auto yaml = fkyaml::node::deserialize(CONTENT);
if (!yaml.contains("presets") || !yaml["presets"].is_sequence()) {
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
postfx_presets.clear();
for (const auto& preset_node : yaml["presets"]) {
PostFXPreset preset;
parsePostFXPreset(preset_node, preset);
postfx_presets.push_back(preset);
}
if (postfx_presets.empty()) {
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
return true;
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant postfx.yaml: " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb presets per defecte.\n";
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
}
auto savePostFXToFile() -> bool {
if (postfx_file_path.empty()) { return false; }
std::ostringstream out;
out << "# Presets PostFX per a AEE 2026. Editable a mà.\n";
out << "# Els noms han de coincidir amb config.yaml > video.shader.current_postfx_preset.\n";
out << "presets:\n";
for (const auto& preset : postfx_presets) {
writePostFXPreset(out, preset);
}
return writeFile(postfx_file_path, out.str());
}
auto buildPostFXParams() -> Rendering::PostFXParams {
// Si el shader està deshabilitat, retornem params a zero → passthrough.
if (!video.shader.enabled) { return {}; }
const auto IT = std::ranges::find_if(postfx_presets,
[](const PostFXPreset& preset) { return preset.name == video.shader.current_postfx_preset; });
if (IT == postfx_presets.end()) {
std::cerr << "Options: preset PostFX \"" << video.shader.current_postfx_preset << "\" no trobat; aplicant passthrough.\n";
return {};
}
return {
.vignette = IT->vignette,
.scanlines = IT->scanlines,
.chroma = IT->chroma,
.mask = IT->mask,
.gamma = IT->gamma,
.curvature = IT->curvature,
.bleeding = IT->bleeding,
.flicker = IT->flicker,
};
}
} // namespace Options
+28
View File
@@ -1,8 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include "core/rendering/screen.hpp"
#include "core/rendering/shader_backend.hpp"
#include "utils/defaults.hpp"
// Gestor de configuració del joc. És l'ÚNIC lloc que coneix YAML i la ruta
@@ -21,11 +23,30 @@ namespace Options {
std::string preferred_driver{Defaults::Video::GPU_PREFERRED_DRIVER};
};
struct Shader {
bool enabled{Defaults::Video::SHADER_ENABLED};
std::string current_postfx_preset{Defaults::Video::POSTFX_DEFAULT_PRESET};
};
struct Video {
bool vsync{Defaults::Video::VSYNC};
Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE};
Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER};
Gpu gpu{};
Shader shader{};
};
// Preset d'efectes PostFX (un per entrada a postfx.yaml).
struct PostFXPreset {
std::string name;
float vignette{0.0F};
float scanlines{0.0F};
float chroma{0.0F};
float mask{0.0F};
float gamma{0.0F};
float curvature{0.0F};
float bleeding{0.0F};
float flicker{0.0F};
};
struct Music {
@@ -49,16 +70,23 @@ namespace Options {
// el programa sense necessitar un .cpp extra per a cada una) ---
inline std::string config_version{};
inline std::string config_file_path{};
inline std::string postfx_file_path{};
inline Window window{};
inline Video video{};
inline Audio audio{};
inline std::vector<PostFXPreset> postfx_presets{};
// --- API pública ---
void setConfigFile(const std::string& path);
auto loadFromFile() -> bool;
auto saveToFile() -> bool;
void setPostFXFile(const std::string& path);
auto loadPostFXFromFile() -> bool;
auto savePostFXToFile() -> bool;
// --- Builders per als subsistemes ---
[[nodiscard]] auto buildScreenConfig() -> Screen::Config;
[[nodiscard]] auto buildPostFXParams() -> Rendering::PostFXParams;
} // namespace Options
+3
View File
@@ -34,6 +34,9 @@ namespace Defaults {
constexpr TextureFilter TEXTURE_FILTER = TextureFilter::NEAREST;
constexpr bool GPU_ACCELERATION = true; // Path SDL3 GPU per defecte; fallback a SDL_Renderer si falla.
constexpr const char* GPU_PREFERRED_DRIVER = ""; // Buit = auto. Aplica a l'arrencada.
constexpr bool SHADER_ENABLED = true; // Aplica PostFX; si false, sempre passthrough.
constexpr const char* POSTFX_DEFAULT_PRESET = "off"; // Preset inicial a config.yaml.
constexpr const char* POSTFX_FILE = "shaders/postfx.yaml"; // Relatiu a la carpeta de sistema.
} // namespace Video
namespace Audio {
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Texts {
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
constexpr const char* COPYRIGHT = "© 2026 JailDesigner";
constexpr const char* VERSION = "0.4.0"; // Single source of truth — Makefile el grep
constexpr const char* VERSION = "0.5.0"; // Single source of truth — Makefile el grep
} // namespace Texts
namespace GameCanvas {