Presets PostFX carregats de shaders/postfx.yaml; Screen aplica params (v0.5.0)
This commit is contained in:
1 parent
6c8ef9ff44
commit
26b4d9e610
7 files changed
+192
-2
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user