migrat, amb ajuda de claude, a sdl3gpu (postfx i crtpi) igual que el JDD
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Coffee Crisis Arcade Edition is a 2-player cooperative arcade shooter built with C++20 and SDL3. Players defend coffee against giant balloons. The game targets Windows, Linux, macOS (Intel/Apple Silicon), Raspberry Pi, and Anbernic handhelds.
|
||||
|
||||
## Build Commands
|
||||
|
||||
The project uses both CMake and a top-level Makefile. The Makefile is the primary build interface.
|
||||
|
||||
### CMake (generates compile_commands.json for IDE/linter support)
|
||||
```bash
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Debug # configure
|
||||
cmake --build build # build
|
||||
```
|
||||
|
||||
### Makefile (direct compilation, platform-specific targets)
|
||||
```bash
|
||||
make linux # build for Linux
|
||||
make linux_debug # debug build with -DDEBUG -DVERBOSE
|
||||
make linux_release # release tar.gz with resources.pack
|
||||
make windows # build for Windows (cross-compile or native)
|
||||
make windows_debug # Windows debug build
|
||||
make macos # build for macOS (arm64)
|
||||
make raspi # build for Raspberry Pi
|
||||
make anbernic # build for Anbernic (no shaders, arcade mode)
|
||||
make no_audio # build without audio system
|
||||
```
|
||||
|
||||
### Tools & Resources
|
||||
```bash
|
||||
make pack_tool # compile resource packer
|
||||
make resources.pack # pack data/ into resources.pack (required for release builds)
|
||||
make spirv # compile GLSL shaders to SPIR-V headers
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
```bash
|
||||
make format # run clang-format on all sources (or: cmake --build build --target format)
|
||||
make format-check # check formatting without modifying
|
||||
make tidy # run clang-tidy static analysis (cmake --build build --target tidy)
|
||||
make tidy-fix # run clang-tidy with auto-fix
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Singletons (core systems)
|
||||
- **Director** (`source/director.hpp`) — Application state machine, orchestrates scene transitions (Logo → Intro → Title → Game → Credits/HiScore → Title)
|
||||
- **Screen** (`source/screen.hpp`) — Window management, SDL3 GPU rendering pipeline, post-processing effects
|
||||
- **Resource** (`source/resource.hpp`) — Asset loading/caching with PRELOAD and LAZY_LOAD modes, reads from `resources.pack`
|
||||
- **Audio** (`source/audio.hpp`) — Music and SFX management
|
||||
- **Input** (`source/input.hpp`) — Keyboard and gamepad input handling
|
||||
|
||||
### Scenes (source/sections/)
|
||||
Each scene is a self-contained class with update/render lifecycle. Scene flow is managed by Director.
|
||||
|
||||
### Entity Managers
|
||||
- `BalloonManager` / `BulletManager` — Object pool-based entity management
|
||||
- `Player` — Two-player support (player 1: keyboard, player 2: gamepad)
|
||||
|
||||
### Rendering Pipeline
|
||||
- SDL3 GPU API (Vulkan/Metal/D3D12 backends)
|
||||
- SPIR-V shaders compiled offline from GLSL (`data/shaders/`) via `glslc`
|
||||
- Compiled shader headers embedded in `source/rendering/sdl3gpu/postfx_*_spv.h`
|
||||
- macOS uses Metal (no SPIR-V compilation needed)
|
||||
|
||||
### Configuration
|
||||
- Game parameters: `config/param_320x240.txt`, `config/param_320x256.txt`
|
||||
- Asset manifest: `config/assets.txt`
|
||||
- Balloon formations: `config/formations.txt`
|
||||
- Level definitions: `config/stages.txt`
|
||||
- Gamepad mappings: `config/gamecontrollerdb.txt`
|
||||
|
||||
### External Libraries (header-only/vendored in source/external/)
|
||||
- nlohmann/json, fkyaml (YAML), stb_image, stb_vorbis, jail_audio
|
||||
|
||||
## Code Style
|
||||
|
||||
Enforced via `.clang-format` (Google-based) and `.clang-tidy`:
|
||||
|
||||
- **Naming conventions**: Classes/structs `CamelCase`, methods/functions `camelBack`, variables/params `snake_case`, private/protected members `snake_case_` (trailing underscore), constants/constexpr `UPPER_CASE`, namespaces `CamelCase`, enum values `UPPER_CASE`
|
||||
- 4-space indentation, no column limit, braces attach to statement
|
||||
- clang-tidy treats all warnings as errors
|
||||
|
||||
## Conditional Compilation Defines
|
||||
|
||||
| Define | Purpose |
|
||||
|--------|---------|
|
||||
| `WINDOWS_BUILD` / `LINUX_BUILD` / `MACOS_BUILD` | Platform selection |
|
||||
| `DEBUG` / `VERBOSE` | Debug output |
|
||||
| `RELEASE_BUILD` | Release-specific code paths |
|
||||
| `RECORDING` | Demo recording mode |
|
||||
| `NO_SHADERS` | Disable shader pipeline (Anbernic) |
|
||||
| `NO_AUDIO` | Build without audio |
|
||||
| `ARCADE` | Arcade-specific mode |
|
||||
| `MACOS_BUNDLE` | macOS .app bundle paths |
|
||||
| `ANBERNIC` | Anbernic handheld build |
|
||||
|
||||
## Language
|
||||
|
||||
Code comments are in Spanish/Catalan. Game UI supports multiple languages via JSON files in `data/lang/`.
|
||||
+28
-17
@@ -123,32 +123,43 @@ message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
|
||||
if(NOT APPLE)
|
||||
find_program(GLSLC_EXE NAMES glslc)
|
||||
|
||||
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag")
|
||||
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_vert_spv.h")
|
||||
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_frag_spv.h")
|
||||
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag")
|
||||
set(SHADER_CRTPI_SRC "${CMAKE_SOURCE_DIR}/data/shaders/crtpi_frag.glsl")
|
||||
set(SHADER_UPSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/upscale.frag")
|
||||
set(SHADER_DOWNSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/downscale.frag")
|
||||
|
||||
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_vert_spv.h")
|
||||
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_frag_spv.h")
|
||||
set(SHADER_CRTPI_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/crtpi_frag_spv.h")
|
||||
set(SHADER_UPSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/upscale_frag_spv.h")
|
||||
set(SHADER_DOWNSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/downscale_frag_spv.h")
|
||||
|
||||
set(ALL_SHADER_SOURCES "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" "${SHADER_CRTPI_SRC}" "${SHADER_UPSCALE_SRC}" "${SHADER_DOWNSCALE_SRC}")
|
||||
set(ALL_SHADER_HEADERS "${SHADER_VERT_H}" "${SHADER_FRAG_H}" "${SHADER_CRTPI_H}" "${SHADER_UPSCALE_H}" "${SHADER_DOWNSCALE_H}")
|
||||
|
||||
if(GLSLC_EXE)
|
||||
add_custom_command(
|
||||
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}"
|
||||
OUTPUT ${ALL_SHADER_HEADERS}
|
||||
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh"
|
||||
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}"
|
||||
DEPENDS ${ALL_SHADER_SOURCES}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Compilando shaders SPIR-V..."
|
||||
)
|
||||
add_custom_target(shaders DEPENDS "${SHADER_VERT_H}" "${SHADER_FRAG_H}")
|
||||
add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
|
||||
message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
|
||||
else()
|
||||
if(NOT EXISTS "${SHADER_VERT_H}" OR NOT EXISTS "${SHADER_FRAG_H}")
|
||||
message(FATAL_ERROR
|
||||
"glslc no encontrado y headers SPIR-V no existen.\n"
|
||||
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
||||
" choco install vulkan-sdk (Windows)\n"
|
||||
" O genera los headers manualmente: tools/shaders/compile_spirv.sh"
|
||||
)
|
||||
else()
|
||||
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
||||
endif()
|
||||
foreach(_h IN LISTS ALL_SHADER_HEADERS)
|
||||
if(NOT EXISTS "${_h}")
|
||||
message(FATAL_ERROR
|
||||
"glslc no encontrado y header SPIR-V no existe: ${_h}\n"
|
||||
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
||||
" choco install vulkan-sdk (Windows)\n"
|
||||
" O genera los headers manualmente: tools/shaders/compile_spirv.sh"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
DATA|${SYSTEM_FOLDER}/config.yaml|optional,absolute
|
||||
DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
||||
DATA|${SYSTEM_FOLDER}/postfx.yaml|optional,absolute
|
||||
DATA|${SYSTEM_FOLDER}/crtpi.yaml|optional,absolute
|
||||
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
||||
|
||||
# Archivos de configuración del juego
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#version 450
|
||||
|
||||
// Vulkan GLSL fragment shader — CRT-Pi PostFX
|
||||
// Algoritmo de scanlines continuas con pesos gaussianos, bloom y máscara de fósforo.
|
||||
// Basado en el shader CRT-Pi original (GLSL 3.3), portado a GLSL 4.50 con parámetros uniformes.
|
||||
//
|
||||
// Compile: glslc -fshader-stage=frag --target-env=vulkan1.0 crtpi_frag.glsl -o crtpi_frag.spv
|
||||
// xxd -i crtpi_frag.spv > ../../source/core/rendering/sdl3gpu/crtpi_frag_spv.h
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D Texture;
|
||||
|
||||
layout(set = 3, binding = 0) uniform CrtPiBlock {
|
||||
// vec4 #0
|
||||
float scanline_weight; // Ajuste gaussiano de scanlines (default 6.0)
|
||||
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
|
||||
float bloom_factor; // Factor de brillo en zonas iluminadas (default 3.5)
|
||||
float input_gamma; // Gamma de entrada — linealización (default 2.4)
|
||||
// vec4 #1
|
||||
float output_gamma; // Gamma de salida — codificación (default 2.2)
|
||||
float mask_brightness; // Brillo sub-píxeles de la máscara (default 0.80)
|
||||
float curvature_x; // Distorsión barrel eje X (default 0.05)
|
||||
float curvature_y; // Distorsión barrel eje Y (default 0.10)
|
||||
// vec4 #2
|
||||
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
|
||||
int enable_scanlines; // 0 = off, 1 = on
|
||||
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico de scanlines)
|
||||
int enable_gamma; // 0 = off, 1 = on
|
||||
// vec4 #3
|
||||
int enable_curvature; // 0 = off, 1 = on
|
||||
int enable_sharper; // 0 = off, 1 = on
|
||||
float texture_width; // Ancho del canvas lógico en píxeles
|
||||
float texture_height; // Alto del canvas lógico en píxeles
|
||||
} u;
|
||||
|
||||
// Distorsión barrel CRT
|
||||
vec2 distort(vec2 coord, vec2 screen_scale) {
|
||||
vec2 curvature = vec2(u.curvature_x, u.curvature_y);
|
||||
vec2 barrel_scale = 1.0 - (0.23 * curvature);
|
||||
coord *= screen_scale;
|
||||
coord -= vec2(0.5);
|
||||
float rsq = coord.x * coord.x + coord.y * coord.y;
|
||||
coord += coord * (curvature * rsq);
|
||||
coord *= barrel_scale;
|
||||
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) {
|
||||
return vec2(-1.0); // fuera de pantalla
|
||||
}
|
||||
coord += vec2(0.5);
|
||||
coord /= screen_scale;
|
||||
return coord;
|
||||
}
|
||||
|
||||
float calcScanLineWeight(float dist) {
|
||||
return max(1.0 - dist * dist * u.scanline_weight, u.scanline_gap_brightness);
|
||||
}
|
||||
|
||||
float calcScanLine(float dy, float filter_width) {
|
||||
float weight = calcScanLineWeight(dy);
|
||||
if (u.enable_multisample != 0) {
|
||||
weight += calcScanLineWeight(dy - filter_width);
|
||||
weight += calcScanLineWeight(dy + filter_width);
|
||||
weight *= 0.3333333;
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 tex_size = vec2(u.texture_width, u.texture_height);
|
||||
|
||||
// filterWidth: equivalente al original (768.0 / TextureSize.y) / 3.0
|
||||
float filter_width = (768.0 / u.texture_height) / 3.0;
|
||||
|
||||
vec2 texcoord = v_uv;
|
||||
|
||||
// Curvatura barrel opcional
|
||||
if (u.enable_curvature != 0) {
|
||||
texcoord = distort(texcoord, vec2(1.0, 1.0));
|
||||
if (texcoord.x < 0.0) {
|
||||
out_color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vec2 texcoord_in_pixels = texcoord * tex_size;
|
||||
vec2 tc;
|
||||
float scan_line_weight;
|
||||
|
||||
if (u.enable_sharper != 0) {
|
||||
// Modo SHARPER: filtrado bicúbico-like con subpixel sharpen
|
||||
vec2 temp_coord = floor(texcoord_in_pixels) + 0.5;
|
||||
tc = temp_coord / tex_size;
|
||||
vec2 deltas = texcoord_in_pixels - temp_coord;
|
||||
scan_line_weight = calcScanLine(deltas.y, filter_width);
|
||||
vec2 signs = sign(deltas);
|
||||
deltas.x *= 2.0;
|
||||
deltas = deltas * deltas;
|
||||
deltas.y = deltas.y * deltas.y;
|
||||
deltas.x *= 0.5;
|
||||
deltas.y *= 8.0;
|
||||
deltas /= tex_size;
|
||||
deltas *= signs;
|
||||
tc = tc + deltas;
|
||||
} else {
|
||||
// Modo estándar
|
||||
float temp_y = floor(texcoord_in_pixels.y) + 0.5;
|
||||
float y_coord = temp_y / tex_size.y;
|
||||
float dy = texcoord_in_pixels.y - temp_y;
|
||||
scan_line_weight = calcScanLine(dy, filter_width);
|
||||
float sign_y = sign(dy);
|
||||
dy = dy * dy;
|
||||
dy = dy * dy;
|
||||
dy *= 8.0;
|
||||
dy /= tex_size.y;
|
||||
dy *= sign_y;
|
||||
tc = vec2(texcoord.x, y_coord + dy);
|
||||
}
|
||||
|
||||
vec3 colour = texture(Texture, tc).rgb;
|
||||
|
||||
if (u.enable_scanlines != 0) {
|
||||
if (u.enable_gamma != 0) {
|
||||
colour = pow(colour, vec3(u.input_gamma));
|
||||
}
|
||||
colour *= scan_line_weight * u.bloom_factor;
|
||||
if (u.enable_gamma != 0) {
|
||||
colour = pow(colour, vec3(1.0 / u.output_gamma));
|
||||
}
|
||||
}
|
||||
|
||||
// Máscara de fósforo
|
||||
if (u.mask_type == 1) {
|
||||
float which_mask = fract(gl_FragCoord.x * 0.5);
|
||||
vec3 mask = (which_mask < 0.5)
|
||||
? vec3(u.mask_brightness, 1.0, u.mask_brightness)
|
||||
: vec3(1.0, u.mask_brightness, 1.0);
|
||||
colour *= mask;
|
||||
} else if (u.mask_type == 2) {
|
||||
float which_mask = fract(gl_FragCoord.x * 0.3333333);
|
||||
vec3 mask = vec3(u.mask_brightness);
|
||||
if (which_mask < 0.3333333)
|
||||
mask.x = 1.0;
|
||||
else if (which_mask < 0.6666666)
|
||||
mask.y = 1.0;
|
||||
else
|
||||
mask.z = 1.0;
|
||||
colour *= mask;
|
||||
}
|
||||
|
||||
out_color = vec4(colour, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#version 450
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D source;
|
||||
|
||||
layout(set = 3, binding = 0) uniform DownscaleUniforms {
|
||||
int algorithm; // 0 = Lanczos2 (ventana 2, ±2 taps), 1 = Lanczos3 (ventana 3, ±3 taps)
|
||||
float pad0;
|
||||
float pad1;
|
||||
float pad2;
|
||||
} u;
|
||||
|
||||
// Kernel Lanczos normalizado: sinc(t) * sinc(t/a) para |t| < a, 0 fuera.
|
||||
float lanczos(float t, float a) {
|
||||
t = abs(t);
|
||||
if (t < 0.0001) { return 1.0; }
|
||||
if (t >= a) { return 0.0; }
|
||||
const float PI = 3.14159265358979;
|
||||
float pt = PI * t;
|
||||
return (a * sin(pt) * sin(pt / a)) / (pt * pt);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 src_size = vec2(textureSize(source, 0));
|
||||
// Posición en coordenadas de texel (centros de texel en N+0.5)
|
||||
vec2 p = v_uv * src_size;
|
||||
vec2 p_floor = floor(p);
|
||||
|
||||
float a = (u.algorithm == 0) ? 2.0 : 3.0;
|
||||
int win = int(a);
|
||||
|
||||
vec4 color = vec4(0.0);
|
||||
float weight_sum = 0.0;
|
||||
|
||||
for (int j = -win; j <= win; j++) {
|
||||
for (int i = -win; i <= win; i++) {
|
||||
// Centro del texel (i,j) relativo a p_floor
|
||||
vec2 tap_center = p_floor + vec2(float(i), float(j)) + 0.5;
|
||||
vec2 offset = tap_center - p;
|
||||
float w = lanczos(offset.x, a) * lanczos(offset.y, a);
|
||||
color += texture(source, tap_center / src_size) * w;
|
||||
weight_sum += w;
|
||||
}
|
||||
}
|
||||
|
||||
out_color = (weight_sum > 0.0) ? (color / weight_sum) : vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
// Vulkan GLSL fragment shader — Nearest-neighbour upscale pass
|
||||
// Used as the first render pass when supersampling is active.
|
||||
// Compile: glslc upscale.frag -o upscale.frag.spv
|
||||
// xxd -i upscale.frag.spv > ../../source/core/rendering/sdl3gpu/upscale_frag_spv.h
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D scene;
|
||||
|
||||
void main() {
|
||||
out_color = texture(scene, v_uv);
|
||||
}
|
||||
@@ -57,13 +57,13 @@ class AnimatedSprite : public MovingSprite {
|
||||
void update(float delta_time) override; // Actualiza la animación (time-based)
|
||||
|
||||
// --- Control de animaciones ---
|
||||
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
|
||||
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
|
||||
void resetAnimation(); // Reinicia la animación actual
|
||||
void setAnimationSpeed(float value); // Establece la velocidad de la animación
|
||||
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
|
||||
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
|
||||
void resetAnimation(); // Reinicia la animación actual
|
||||
void setAnimationSpeed(float value); // Establece la velocidad de la animación
|
||||
[[nodiscard]] auto getAnimationSpeed() const -> float { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
|
||||
void animtionPause() { animations_[current_animation_].paused = true; } // Detiene la animación
|
||||
void animationResume() { animations_[current_animation_].paused = false; } // Reanuda la animación
|
||||
void animtionPause() { animations_[current_animation_].paused = true; } // Detiene la animación
|
||||
void animationResume() { animations_[current_animation_].paused = false; } // Reanuda la animación
|
||||
[[nodiscard]] auto getCurrentAnimationFrame() const -> size_t { return animations_[current_animation_].current_frame; } // Obtiene el numero de frame de la animación actual
|
||||
|
||||
// --- Consultas ---
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ class Asset {
|
||||
void add(const std::string& file_path, Type type, bool required = true, bool absolute = false);
|
||||
void loadFromFile(const std::string& config_file_path, const std::string& prefix = "", const std::string& system_folder = ""); // Con soporte para variables
|
||||
[[nodiscard]] auto getPath(const std::string& filename) const -> std::string;
|
||||
[[nodiscard]] auto loadData(const std::string& filename) const -> std::vector<uint8_t>; // Carga datos del archivo
|
||||
[[nodiscard]] auto loadData(const std::string& filename) const -> std::vector<uint8_t>; // Carga datos del archivo
|
||||
[[nodiscard]] auto check() const -> bool;
|
||||
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
||||
[[nodiscard]] auto exists(const std::string& filename) const -> bool; // Nueva función para verificar existencia
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "balloon_formations.hpp"
|
||||
|
||||
#include <algorithm> // Para max, min, copy
|
||||
#include <utility> // Para std::cmp_less
|
||||
#include <array> // Para array
|
||||
#include <cctype> // Para isdigit
|
||||
#include <cstddef> // Para size_t
|
||||
@@ -11,6 +10,7 @@
|
||||
#include <map> // Para map, operator==, _Rb_tree_iterator
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <string> // Para string, char_traits, allocator, operator==, stoi, getline, operator<=>, basic_string
|
||||
#include <utility> // Para std::cmp_less
|
||||
|
||||
#include "asset.hpp" // Para Asset
|
||||
#include "balloon.hpp" // Para Balloon
|
||||
|
||||
+5
-2
@@ -186,8 +186,11 @@ namespace Defaults::Video {
|
||||
constexpr bool FULLSCREEN = false;
|
||||
constexpr bool VSYNC = true;
|
||||
constexpr bool INTEGER_SCALE = true;
|
||||
constexpr bool POSTFX = false;
|
||||
constexpr int SUPERSAMPLING = 1;
|
||||
constexpr bool GPU_ACCELERATION = true;
|
||||
constexpr bool SHADER_ENABLED = false;
|
||||
constexpr bool SUPERSAMPLING = false;
|
||||
constexpr bool LINEAR_UPSCALE = false;
|
||||
constexpr int DOWNSCALE_ALGO = 1;
|
||||
} // namespace Defaults::Video
|
||||
|
||||
namespace Defaults::Music {
|
||||
|
||||
+7
-5
@@ -103,13 +103,15 @@ void Director::init() {
|
||||
Input::init(Asset::get()->getPath("gamecontrollerdb.txt"), Asset::get()->getPath("controllers.json")); // Carga configuración de controles
|
||||
|
||||
Logger::section("INIT CONFIG");
|
||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
||||
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
||||
Options::loadFromFile(); // Carga el archivo de configuración
|
||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||
loadParams(); // Carga los parámetros del programa
|
||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
||||
Options::loadFromFile(); // Carga el archivo de configuración
|
||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
||||
loadParams(); // Carga los parámetros del programa
|
||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||
|
||||
// Inicialización de subsistemas principales
|
||||
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
||||
|
||||
@@ -33,8 +33,8 @@ class EnterName {
|
||||
private:
|
||||
// --- Variables de estado ---
|
||||
std::string character_list_{"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{"}; // Lista de caracteres permitidos
|
||||
std::string name_; // Nombre en proceso
|
||||
size_t selected_index_ = 0; // Índice del carácter seleccionado en "character_list_"
|
||||
std::string name_; // Nombre en proceso
|
||||
size_t selected_index_ = 0; // Índice del carácter seleccionado en "character_list_"
|
||||
|
||||
[[nodiscard]] auto sanitizeName(const std::string& name) const -> std::string; // Valida y limpia el nombre
|
||||
static auto getRandomName() -> std::string; // Devuelve un nombre al azar
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ class Fade {
|
||||
int num_squares_width_; // Cuadrados en horizontal
|
||||
int num_squares_height_; // Cuadrados en vertical
|
||||
int square_transition_duration_; // Duración de transición de cada cuadrado en ms
|
||||
int fading_duration_{0}; // Duración del estado FADING en milisegundos
|
||||
int fading_duration_{0}; // Duración del estado FADING en milisegundos
|
||||
Uint32 fading_start_time_ = 0; // Tiempo de inicio del estado FADING
|
||||
int post_duration_ = 0; // Duración posterior en milisegundos
|
||||
Uint32 post_start_time_ = 0; // Tiempo de inicio del estado POST
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace GlobalEvents {
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
Screen::initPostFX();
|
||||
Screen::initShaders();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
+30
-15
@@ -62,25 +62,36 @@ namespace GlobalInputs {
|
||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.vsync)});
|
||||
}
|
||||
|
||||
// Activa o desactiva los efectos PostFX
|
||||
void togglePostFX() {
|
||||
Screen::togglePostFX();
|
||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.postfx)});
|
||||
// Activa o desactiva los shaders
|
||||
void toggleShaders() {
|
||||
Screen::toggleShaders();
|
||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shader.enabled)});
|
||||
}
|
||||
|
||||
// Avanza al siguiente preset PostFX
|
||||
void nextPostFXPreset() {
|
||||
Screen::nextPostFXPreset();
|
||||
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset)).name;
|
||||
Notifier::get()->show({"PostFX: " + name});
|
||||
// Cambia entre PostFX y CrtPi
|
||||
void nextShader() {
|
||||
Screen::nextShader();
|
||||
const std::string SHADER_NAME = (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) ? "CrtPi" : "PostFX";
|
||||
Notifier::get()->show({"Shader: " + SHADER_NAME});
|
||||
}
|
||||
|
||||
// Activa o desactiva el supersampling 3x
|
||||
// Avanza al siguiente preset PostFX o CrtPi según shader activo
|
||||
void nextPreset() {
|
||||
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||
Screen::nextCrtPiPreset();
|
||||
const std::string name = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
|
||||
Notifier::get()->show({"CrtPi: " + name});
|
||||
} else {
|
||||
Screen::nextPostFXPreset();
|
||||
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name;
|
||||
Notifier::get()->show({"PostFX: " + name});
|
||||
}
|
||||
}
|
||||
|
||||
// Activa o desactiva el supersampling
|
||||
void toggleSupersampling() {
|
||||
Screen::toggleSupersampling();
|
||||
const int SS = Options::video.supersampling;
|
||||
const std::string SS_LABEL = (SS <= 1) ? "OFF" : (std::to_string(SS) + "\xC3\x97");
|
||||
Notifier::get()->show({"SS: " + SS_LABEL});
|
||||
Notifier::get()->show({"SS: " + std::string(Options::video.supersampling.enabled ? "ON" : "OFF")});
|
||||
}
|
||||
|
||||
// Cambia al siguiente idioma
|
||||
@@ -202,11 +213,15 @@ namespace GlobalInputs {
|
||||
}
|
||||
|
||||
if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_POSTFX, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||
togglePostFX();
|
||||
toggleShaders();
|
||||
return true;
|
||||
}
|
||||
if (Input::get()->checkAction(Input::Action::NEXT_SHADER, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||
nextShader();
|
||||
return true;
|
||||
}
|
||||
if (Input::get()->checkAction(Input::Action::NEXT_POSTFX_PRESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||
nextPostFXPreset();
|
||||
nextPreset();
|
||||
return true;
|
||||
}
|
||||
if (Input::get()->checkAction(Input::Action::TOGGLE_SUPERSAMPLING, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||
|
||||
@@ -82,6 +82,7 @@ class Input {
|
||||
{Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
|
||||
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
||||
{Action::TOGGLE_VIDEO_POSTFX, KeyState(SDL_SCANCODE_F4)},
|
||||
{Action::NEXT_SHADER, KeyState(SDL_SCANCODE_8)},
|
||||
{Action::NEXT_POSTFX_PRESET, KeyState(SDL_SCANCODE_9)},
|
||||
{Action::TOGGLE_SUPERSAMPLING, KeyState(SDL_SCANCODE_0)},
|
||||
{Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
|
||||
|
||||
@@ -22,6 +22,7 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
|
||||
{InputAction::WINDOW_INC_SIZE, "WINDOW_INC_SIZE"},
|
||||
{InputAction::WINDOW_DEC_SIZE, "WINDOW_DEC_SIZE"},
|
||||
{InputAction::TOGGLE_VIDEO_POSTFX, "TOGGLE_VIDEO_POSTFX"},
|
||||
{InputAction::NEXT_SHADER, "NEXT_SHADER"},
|
||||
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"},
|
||||
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
||||
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, "TOGGLE_VIDEO_INTEGER_SCALE"},
|
||||
@@ -54,6 +55,7 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
||||
{"WINDOW_INC_SIZE", InputAction::WINDOW_INC_SIZE},
|
||||
{"WINDOW_DEC_SIZE", InputAction::WINDOW_DEC_SIZE},
|
||||
{"TOGGLE_VIDEO_POSTFX", InputAction::TOGGLE_VIDEO_POSTFX},
|
||||
{"NEXT_SHADER", InputAction::NEXT_SHADER},
|
||||
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET},
|
||||
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
||||
{"TOGGLE_VIDEO_INTEGER_SCALE", InputAction::TOGGLE_VIDEO_INTEGER_SCALE},
|
||||
|
||||
@@ -32,6 +32,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
|
||||
WINDOW_INC_SIZE,
|
||||
WINDOW_DEC_SIZE,
|
||||
TOGGLE_VIDEO_POSTFX,
|
||||
NEXT_SHADER,
|
||||
NEXT_POSTFX_PRESET,
|
||||
TOGGLE_SUPERSAMPLING,
|
||||
TOGGLE_VIDEO_INTEGER_SCALE,
|
||||
|
||||
+305
-73
@@ -8,7 +8,7 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "difficulty.hpp" // Para Code, init
|
||||
#include "difficulty.hpp" // Para Code, init
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para getText, Code
|
||||
@@ -24,16 +24,17 @@ namespace Options {
|
||||
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
||||
Keyboard keyboard; // Opciones para el teclado
|
||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
std::vector<PostFXPreset> postfx_presets = { // Lista de presets de PostFX
|
||||
{"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F},
|
||||
{"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F},
|
||||
{"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F},
|
||||
{"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F},
|
||||
{"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F},
|
||||
{"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F},
|
||||
std::vector<PostFXPreset> postfx_presets = {
|
||||
{"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F},
|
||||
{"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F},
|
||||
{"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F},
|
||||
{"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F},
|
||||
{"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F},
|
||||
{"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F},
|
||||
};
|
||||
int current_postfx_preset = 0; // Índice del preset PostFX activo
|
||||
std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
||||
std::string postfx_file_path;
|
||||
std::vector<CrtPiPreset> crtpi_presets;
|
||||
std::string crtpi_file_path;
|
||||
|
||||
// Establece el fichero de configuración
|
||||
void setConfigFile(const std::string& file_path) { settings.config_file = file_path; }
|
||||
@@ -44,6 +45,9 @@ namespace Options {
|
||||
// Establece la ruta del fichero de PostFX
|
||||
void setPostFXFile(const std::string& path) { postfx_file_path = path; }
|
||||
|
||||
// Establece la ruta del fichero de CrtPi
|
||||
void setCrtPiFile(const std::string& path) { crtpi_file_path = path; }
|
||||
|
||||
// Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión
|
||||
static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) {
|
||||
if (node.contains(key)) {
|
||||
@@ -89,11 +93,21 @@ namespace Options {
|
||||
}
|
||||
|
||||
if (!postfx_presets.empty()) {
|
||||
current_postfx_preset = std::clamp(
|
||||
current_postfx_preset, 0,
|
||||
// Resolver nombre → índice
|
||||
if (!video.shader.current_postfx_preset_name.empty()) {
|
||||
for (int i = 0; i < static_cast<int>(postfx_presets.size()); ++i) {
|
||||
if (postfx_presets[static_cast<size_t>(i)].name == video.shader.current_postfx_preset_name) {
|
||||
video.shader.current_postfx_preset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
video.shader.current_postfx_preset = std::clamp(
|
||||
video.shader.current_postfx_preset,
|
||||
0,
|
||||
static_cast<int>(postfx_presets.size()) - 1);
|
||||
} else {
|
||||
current_postfx_preset = 0;
|
||||
video.shader.current_postfx_preset = 0;
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "PostFX file loaded: %zu preset(s)", postfx_presets.size());
|
||||
@@ -190,17 +204,141 @@ namespace Options {
|
||||
|
||||
// Cargar los presets recién escritos
|
||||
postfx_presets.clear();
|
||||
postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F});
|
||||
postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F});
|
||||
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;
|
||||
postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F});
|
||||
postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F});
|
||||
postfx_presets.push_back({"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F});
|
||||
video.shader.current_postfx_preset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper: extrae un campo bool de un nodo YAML si existe, ignorando errores
|
||||
static void parseBoolField(const fkyaml::node& node, const std::string& key, bool& target) {
|
||||
if (node.contains(key)) {
|
||||
try {
|
||||
target = node[key].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: extrae un campo int de un nodo YAML si existe, ignorando errores
|
||||
static void parseIntField(const fkyaml::node& node, const std::string& key, int& target) {
|
||||
if (node.contains(key)) {
|
||||
try {
|
||||
target = node[key].get_value<int>();
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Rellena los presets CrtPi por defecto
|
||||
static void populateDefaultCrtPiPresets() {
|
||||
crtpi_presets.clear();
|
||||
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});
|
||||
}
|
||||
|
||||
// Escribe los presets CrtPi por defecto al fichero
|
||||
static auto saveCrtPiDefaults() -> bool {
|
||||
if (crtpi_file_path.empty()) { return false; }
|
||||
std::ofstream file(crtpi_file_path);
|
||||
if (!file.is_open()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened for writing", crtpi_file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
file << "# Coffee Crisis Arcade Edition - CrtPi Shader Presets\n";
|
||||
file << "# scanline_weight: gaussian adjustment (higher = narrower scanlines, default 6.0)\n";
|
||||
file << "# scanline_gap_brightness: min brightness between scanlines (0.0-1.0, default 0.12)\n";
|
||||
file << "# bloom_factor: brightness for bright areas (default 3.5)\n";
|
||||
file << "# input_gamma: input gamma - linearization (default 2.4)\n";
|
||||
file << "# output_gamma: output gamma - encoding (default 2.2)\n";
|
||||
file << "# mask_brightness: sub-pixel brightness (default 0.80)\n";
|
||||
file << "# curvature_x/y: barrel CRT distortion (0.0 = flat)\n";
|
||||
file << "# mask_type: 0=none, 1=green/magenta, 2=RGB phosphor\n";
|
||||
file << "# enable_scanlines/multisample/gamma/curvature/sharper: true/false\n";
|
||||
file << "\npresets:\n";
|
||||
file << " - 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";
|
||||
file << " - 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";
|
||||
file << " - 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";
|
||||
file << " - 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";
|
||||
file.close();
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file created with defaults: %s", crtpi_file_path.c_str());
|
||||
populateDefaultCrtPiPresets();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Carga los presets de CrtPi desde el fichero
|
||||
auto loadCrtPiFromFile() -> bool {
|
||||
crtpi_presets.clear();
|
||||
|
||||
std::ifstream file(crtpi_file_path);
|
||||
if (!file.good()) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file not found, creating default: %s", crtpi_file_path.c_str());
|
||||
return saveCrtPiDefaults();
|
||||
}
|
||||
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(content);
|
||||
|
||||
if (yaml.contains("presets")) {
|
||||
const auto& presets = yaml["presets"];
|
||||
for (const auto& p : presets) {
|
||||
CrtPiPreset preset;
|
||||
if (p.contains("name")) {
|
||||
preset.name = p["name"].get_value<std::string>();
|
||||
}
|
||||
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);
|
||||
parseIntField(p, "mask_type", preset.mask_type);
|
||||
parseBoolField(p, "enable_scanlines", preset.enable_scanlines);
|
||||
parseBoolField(p, "enable_multisample", preset.enable_multisample);
|
||||
parseBoolField(p, "enable_gamma", preset.enable_gamma);
|
||||
parseBoolField(p, "enable_curvature", preset.enable_curvature);
|
||||
parseBoolField(p, "enable_sharper", preset.enable_sharper);
|
||||
crtpi_presets.push_back(preset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!crtpi_presets.empty()) {
|
||||
// Resolver nombre → índice
|
||||
if (!video.shader.current_crtpi_preset_name.empty()) {
|
||||
for (int i = 0; i < static_cast<int>(crtpi_presets.size()); ++i) {
|
||||
if (crtpi_presets[static_cast<size_t>(i)].name == video.shader.current_crtpi_preset_name) {
|
||||
video.shader.current_crtpi_preset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
video.shader.current_crtpi_preset = std::clamp(
|
||||
video.shader.current_crtpi_preset,
|
||||
0,
|
||||
static_cast<int>(crtpi_presets.size()) - 1);
|
||||
} else {
|
||||
video.shader.current_crtpi_preset = 0;
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file loaded: %zu preset(s)", crtpi_presets.size());
|
||||
return true;
|
||||
|
||||
} catch (const fkyaml::exception& e) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing CrtPi YAML: %s. Recreating defaults.", e.what());
|
||||
return saveCrtPiDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa las opciones del programa
|
||||
void init() {
|
||||
// Dificultades
|
||||
@@ -234,55 +372,109 @@ namespace Options {
|
||||
const auto& vid = yaml["video"];
|
||||
|
||||
if (vid.contains("fullscreen")) {
|
||||
try { video.fullscreen = vid["fullscreen"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
video.fullscreen = vid["fullscreen"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vid.contains("scale_mode")) {
|
||||
try { video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>()); } catch (...) {}
|
||||
try {
|
||||
video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>());
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vid.contains("vsync")) {
|
||||
try { video.vsync = vid["vsync"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
video.vsync = vid["vsync"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vid.contains("integer_scale")) {
|
||||
try { video.integer_scale = vid["integer_scale"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
video.integer_scale = vid["integer_scale"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vid.contains("postfx")) {
|
||||
try { video.postfx = vid["postfx"].get_value<bool>(); } catch (...) {}
|
||||
}
|
||||
// Nuevo formato: supersampling (bool) + supersampling_amount (int)
|
||||
// Backward compat: si solo existe supersampling como int, también funciona
|
||||
{
|
||||
bool ss_enabled = false;
|
||||
int ss_amount = 3;
|
||||
if (vid.contains("supersampling")) {
|
||||
|
||||
// --- GPU ---
|
||||
if (vid.contains("gpu")) {
|
||||
const auto& gpu_node = vid["gpu"];
|
||||
if (gpu_node.contains("acceleration")) {
|
||||
try {
|
||||
const auto& node = vid["supersampling"];
|
||||
if (node.is_boolean()) {
|
||||
ss_enabled = node.get_value<bool>();
|
||||
video.gpu.acceleration = gpu_node["acceleration"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (gpu_node.contains("preferred_driver")) {
|
||||
try {
|
||||
video.gpu.preferred_driver = gpu_node["preferred_driver"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Shader config ---
|
||||
if (vid.contains("shader")) {
|
||||
const auto& sh = vid["shader"];
|
||||
if (sh.contains("enabled")) {
|
||||
try {
|
||||
video.shader.enabled = sh["enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (sh.contains("current_shader")) {
|
||||
try {
|
||||
auto s = sh["current_shader"].get_value<std::string>();
|
||||
video.shader.current_shader = (s == "crtpi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
|
||||
} catch (...) {}
|
||||
}
|
||||
if (sh.contains("postfx_preset")) {
|
||||
try {
|
||||
video.shader.current_postfx_preset_name = sh["postfx_preset"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (sh.contains("crtpi_preset")) {
|
||||
try {
|
||||
video.shader.current_crtpi_preset_name = sh["crtpi_preset"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
} else if (vid.contains("postfx")) {
|
||||
// Backward compat: formato antiguo plano
|
||||
try {
|
||||
video.shader.enabled = vid["postfx"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
if (vid.contains("postfx_preset")) {
|
||||
try {
|
||||
int preset = vid["postfx_preset"].get_value<int>();
|
||||
if (preset >= 0) { video.shader.current_postfx_preset = preset; }
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Supersampling ---
|
||||
if (vid.contains("supersampling")) {
|
||||
const auto& ss_node = vid["supersampling"];
|
||||
if (ss_node.is_mapping()) {
|
||||
// Nuevo formato anidado
|
||||
if (ss_node.contains("enabled")) {
|
||||
try {
|
||||
video.supersampling.enabled = ss_node["enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (ss_node.contains("linear_upscale")) {
|
||||
try {
|
||||
video.supersampling.linear_upscale = ss_node["linear_upscale"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (ss_node.contains("downscale_algo")) {
|
||||
try {
|
||||
video.supersampling.downscale_algo = ss_node["downscale_algo"].get_value<int>();
|
||||
} catch (...) {}
|
||||
}
|
||||
} else {
|
||||
// Backward compat: bool o int
|
||||
try {
|
||||
if (ss_node.is_boolean()) {
|
||||
video.supersampling.enabled = ss_node.get_value<bool>();
|
||||
} else {
|
||||
// Formato antiguo: int directamente
|
||||
int factor = node.get_value<int>();
|
||||
ss_enabled = factor >= 2;
|
||||
ss_amount = (factor >= 2) ? factor : 3;
|
||||
int factor = ss_node.get_value<int>();
|
||||
video.supersampling.enabled = factor >= 2;
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vid.contains("supersampling_amount")) {
|
||||
try {
|
||||
int amount = vid["supersampling_amount"].get_value<int>();
|
||||
if (amount >= 2) { ss_amount = amount; }
|
||||
} catch (...) {}
|
||||
}
|
||||
video.supersampling = ss_enabled ? ss_amount : 1;
|
||||
}
|
||||
if (vid.contains("postfx_preset")) {
|
||||
try {
|
||||
int preset = vid["postfx_preset"].get_value<int>();
|
||||
// No validamos contra postfx_presets.size() aquí porque postfx.yaml
|
||||
// aún no se ha cargado. El clamp se hace en loadPostFXFromFile().
|
||||
if (preset >= 0) {
|
||||
current_postfx_preset = preset;
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,27 +483,39 @@ namespace Options {
|
||||
const auto& aud = yaml["audio"];
|
||||
|
||||
if (aud.contains("enabled")) {
|
||||
try { audio.enabled = aud["enabled"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
audio.enabled = aud["enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (aud.contains("volume")) {
|
||||
try { audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
try {
|
||||
audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100);
|
||||
} catch (...) {}
|
||||
}
|
||||
if (aud.contains("music")) {
|
||||
const auto& mus = aud["music"];
|
||||
if (mus.contains("enabled")) {
|
||||
try { audio.music.enabled = mus["enabled"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
audio.music.enabled = mus["enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (mus.contains("volume")) {
|
||||
try { audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
try {
|
||||
audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
if (aud.contains("sound")) {
|
||||
const auto& snd = aud["sound"];
|
||||
if (snd.contains("enabled")) {
|
||||
try { audio.sound.enabled = snd["enabled"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
audio.sound.enabled = snd["enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (snd.contains("volume")) {
|
||||
try { audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
||||
try {
|
||||
audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,13 +542,19 @@ namespace Options {
|
||||
} catch (...) {}
|
||||
}
|
||||
if (game.contains("autofire")) {
|
||||
try { settings.autofire = game["autofire"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
settings.autofire = game["autofire"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (game.contains("shutdown_enabled")) {
|
||||
try { settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>(); } catch (...) {}
|
||||
try {
|
||||
settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (game.contains("params_file")) {
|
||||
try { settings.params_file = game["params_file"].get_value<std::string>(); } catch (...) {}
|
||||
try {
|
||||
settings.params_file = game["params_file"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,10 +566,14 @@ namespace Options {
|
||||
for (const auto& ctrl : controllers) {
|
||||
if (i >= GamepadManager::size()) { break; }
|
||||
if (ctrl.contains("name")) {
|
||||
try { gamepad_manager[i].name = ctrl["name"].get_value<std::string>(); } catch (...) {}
|
||||
try {
|
||||
gamepad_manager[i].name = ctrl["name"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (ctrl.contains("path")) {
|
||||
try { gamepad_manager[i].path = ctrl["path"].get_value<std::string>(); } catch (...) {}
|
||||
try {
|
||||
gamepad_manager[i].path = ctrl["path"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (ctrl.contains("player")) {
|
||||
try {
|
||||
@@ -379,7 +593,9 @@ namespace Options {
|
||||
if (!yaml.contains("keyboard")) { return; }
|
||||
const auto& kb = yaml["keyboard"];
|
||||
if (kb.contains("player")) {
|
||||
try { keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>()); } catch (...) {}
|
||||
try {
|
||||
keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>());
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,10 +668,26 @@ namespace Options {
|
||||
file << " scale_mode: " << static_cast<int>(video.scale_mode) << " # " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": linear\n";
|
||||
file << " vsync: " << boolToString(video.vsync) << "\n";
|
||||
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
||||
file << " postfx: " << boolToString(video.postfx) << "\n";
|
||||
file << " postfx_preset: " << current_postfx_preset << "\n";
|
||||
file << " supersampling: " << boolToString(video.supersampling > 1) << "\n";
|
||||
file << " supersampling_amount: " << std::max(2, video.supersampling) << "\n";
|
||||
file << " gpu:\n";
|
||||
file << " acceleration: " << boolToString(video.gpu.acceleration) << "\n";
|
||||
file << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\n";
|
||||
file << " shader:\n";
|
||||
file << " enabled: " << boolToString(video.shader.enabled) << "\n";
|
||||
file << " current_shader: " << (video.shader.current_shader == Rendering::ShaderType::CRTPI ? "crtpi" : "postfx") << "\n";
|
||||
{
|
||||
std::string postfx_name = (!postfx_presets.empty() && video.shader.current_postfx_preset < static_cast<int>(postfx_presets.size()))
|
||||
? postfx_presets[static_cast<size_t>(video.shader.current_postfx_preset)].name
|
||||
: "";
|
||||
std::string crtpi_name = (!crtpi_presets.empty() && video.shader.current_crtpi_preset < static_cast<int>(crtpi_presets.size()))
|
||||
? crtpi_presets[static_cast<size_t>(video.shader.current_crtpi_preset)].name
|
||||
: "";
|
||||
file << " postfx_preset: \"" << postfx_name << "\"\n";
|
||||
file << " crtpi_preset: \"" << crtpi_name << "\"\n";
|
||||
}
|
||||
file << " supersampling:\n";
|
||||
file << " enabled: " << boolToString(video.supersampling.enabled) << "\n";
|
||||
file << " linear_upscale: " << boolToString(video.supersampling.linear_upscale) << "\n";
|
||||
file << " downscale_algo: " << video.supersampling.downscale_algo << "\n";
|
||||
file << "\n";
|
||||
|
||||
// AUDIO
|
||||
|
||||
+61
-18
@@ -14,11 +14,12 @@
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "defaults.hpp"
|
||||
#include "difficulty.hpp" // for Code
|
||||
#include "input.hpp" // for Input
|
||||
#include "lang.hpp" // for Code
|
||||
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
||||
#include "player.hpp" // for Player
|
||||
#include "difficulty.hpp" // for Code
|
||||
#include "input.hpp" // for Input
|
||||
#include "lang.hpp" // for Code
|
||||
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
||||
#include "player.hpp" // for Player
|
||||
#include "rendering/shader_backend.hpp" // for Rendering::ShaderType
|
||||
|
||||
// --- Namespace Options: gestión de configuración y opciones del juego ---
|
||||
namespace Options {
|
||||
@@ -36,20 +37,59 @@ namespace Options {
|
||||
float flicker{0.0F};
|
||||
};
|
||||
|
||||
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};
|
||||
};
|
||||
|
||||
struct Window {
|
||||
std::string caption = Defaults::Window::CAPTION; // Texto que aparece en la barra de título de la ventana
|
||||
int zoom = Defaults::Window::ZOOM; // Valor por el que se multiplica el tamaño de la ventana
|
||||
int max_zoom = Defaults::Window::MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla
|
||||
std::string caption = Defaults::Window::CAPTION;
|
||||
int zoom = Defaults::Window::ZOOM;
|
||||
int max_zoom = Defaults::Window::MAX_ZOOM;
|
||||
};
|
||||
|
||||
struct GPU {
|
||||
bool acceleration{Defaults::Video::GPU_ACCELERATION};
|
||||
std::string preferred_driver;
|
||||
};
|
||||
|
||||
struct Supersampling {
|
||||
bool enabled{Defaults::Video::SUPERSAMPLING};
|
||||
bool linear_upscale{Defaults::Video::LINEAR_UPSCALE};
|
||||
int downscale_algo{Defaults::Video::DOWNSCALE_ALGO};
|
||||
};
|
||||
|
||||
struct ShaderConfig {
|
||||
bool enabled{Defaults::Video::SHADER_ENABLED};
|
||||
Rendering::ShaderType current_shader{Rendering::ShaderType::POSTFX};
|
||||
std::string current_postfx_preset_name;
|
||||
std::string current_crtpi_preset_name;
|
||||
int current_postfx_preset{0};
|
||||
int current_crtpi_preset{0};
|
||||
};
|
||||
|
||||
struct Video {
|
||||
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE; // Filtro usado para el escalado de la imagen
|
||||
bool fullscreen = Defaults::Video::FULLSCREEN; // Indica si se usa pantalla completa
|
||||
bool vsync = Defaults::Video::VSYNC; // Indica si se usa vsync
|
||||
bool integer_scale = Defaults::Video::INTEGER_SCALE; // Indica si se usa escalado entero
|
||||
bool postfx = Defaults::Video::POSTFX; // Indica si se usan efectos PostFX
|
||||
int supersampling = Defaults::Video::SUPERSAMPLING; // Factor de supersampling: 1=off, 2=2×, 3=3×
|
||||
std::string info; // Información sobre el modo de vídeo
|
||||
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE;
|
||||
bool fullscreen = Defaults::Video::FULLSCREEN;
|
||||
bool vsync = Defaults::Video::VSYNC;
|
||||
bool integer_scale = Defaults::Video::INTEGER_SCALE;
|
||||
std::string info;
|
||||
GPU gpu{};
|
||||
Supersampling supersampling{};
|
||||
ShaderConfig shader{};
|
||||
};
|
||||
|
||||
struct Music {
|
||||
@@ -63,8 +103,8 @@ namespace Options {
|
||||
};
|
||||
|
||||
struct Audio {
|
||||
Music music; // Opciones para la música
|
||||
Sound sound; // Opciones para los efectos de sonido
|
||||
Music music; // Opciones para la música
|
||||
Sound sound; // Opciones para los efectos de sonido
|
||||
bool enabled = Defaults::Audio::ENABLED; // Indica si el audio está activo o no
|
||||
int volume = Defaults::Audio::VOLUME; // Volumen general del audio
|
||||
};
|
||||
@@ -292,16 +332,19 @@ namespace Options {
|
||||
extern Keyboard keyboard; // Opciones para el teclado
|
||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
extern std::vector<PostFXPreset> postfx_presets; // Lista de presets de PostFX
|
||||
extern int current_postfx_preset; // Índice del preset PostFX activo
|
||||
extern std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
||||
extern std::vector<CrtPiPreset> crtpi_presets; // Lista de presets de CrtPi
|
||||
extern std::string crtpi_file_path; // Ruta al fichero de presets CrtPi
|
||||
|
||||
// --- Funciones ---
|
||||
void init(); // Inicializa las opciones del programa
|
||||
void setConfigFile(const std::string& file_path); // Establece el fichero de configuración
|
||||
void setControllersFile(const std::string& file_path); // Establece el fichero de configuración de mandos
|
||||
void setPostFXFile(const std::string& path); // Establece el fichero de presets PostFX
|
||||
void setCrtPiFile(const std::string& path); // Establece el fichero de presets CrtPi
|
||||
auto loadPostFXFromFile() -> bool; // Carga los presets PostFX desde fichero
|
||||
auto savePostFXToFile() -> bool; // Guarda los presets PostFX por defecto al fichero
|
||||
auto loadCrtPiFromFile() -> bool; // Carga los presets CrtPi desde fichero
|
||||
auto loadFromFile() -> bool; // Carga el fichero de configuración
|
||||
auto saveToFile() -> bool; // Guarda el fichero de configuración
|
||||
void setKeyboardToPlayer(Player::Id player_id); // Asigna el teclado al jugador
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3719
-2815
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,24 +3,56 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_gpu.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rendering/shader_backend.hpp"
|
||||
|
||||
// PostFX uniforms pushed to fragment stage each frame.
|
||||
// Must match the MSL struct and GLSL uniform block layout.
|
||||
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||||
struct PostFXUniforms {
|
||||
float vignette_strength; // 0 = none, ~0.8 = subtle
|
||||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
||||
float scanline_strength; // 0 = off, 1 = full
|
||||
float screen_height; // logical height in pixels (used by bleeding effect)
|
||||
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
||||
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
||||
float curvature; // 0 = flat, 1 = max barrel distortion
|
||||
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
||||
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
||||
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||||
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
|
||||
float vignette_strength;
|
||||
float chroma_strength;
|
||||
float scanline_strength;
|
||||
float screen_height;
|
||||
float mask_strength;
|
||||
float gamma_strength;
|
||||
float curvature;
|
||||
float bleeding;
|
||||
float pixel_scale;
|
||||
float time;
|
||||
float oversample;
|
||||
float flicker;
|
||||
};
|
||||
|
||||
// CrtPi uniforms pushed to fragment stage each frame.
|
||||
// 16 fields = 64 bytes — 4 × 16-byte alignment.
|
||||
struct CrtPiUniforms {
|
||||
float scanline_weight;
|
||||
float scanline_gap_brightness;
|
||||
float bloom_factor;
|
||||
float input_gamma;
|
||||
float output_gamma;
|
||||
float mask_brightness;
|
||||
float curvature_x;
|
||||
float curvature_y;
|
||||
int mask_type;
|
||||
int enable_scanlines;
|
||||
int enable_multisample;
|
||||
int enable_gamma;
|
||||
int enable_curvature;
|
||||
int enable_sharper;
|
||||
float texture_width;
|
||||
float texture_height;
|
||||
};
|
||||
|
||||
// Downscale uniforms for Lanczos downscale fragment stage.
|
||||
// 1 int + 3 floats = 16 bytes.
|
||||
struct DownscaleUniforms {
|
||||
int algorithm;
|
||||
float pad0;
|
||||
float pad1;
|
||||
float pad2;
|
||||
};
|
||||
|
||||
namespace Rendering {
|
||||
@@ -28,9 +60,8 @@ namespace Rendering {
|
||||
/**
|
||||
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
||||
*
|
||||
* Backend de shaders PostFX para macOS (Metal) y Win/Linux (Vulkan/SPIR-V).
|
||||
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
||||
* → PostFX render pass → swapchain → present
|
||||
* → [Upscale →] PostFX/CrtPi render pass → [Lanczos downscale →] swapchain → present
|
||||
*/
|
||||
class SDL3GPUShader : public ShaderBackend {
|
||||
public:
|
||||
@@ -44,25 +75,28 @@ namespace Rendering {
|
||||
|
||||
void render() override;
|
||||
void setTextureSize(float width, float height) override {}
|
||||
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
|
||||
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
|
||||
void cleanup() final;
|
||||
void destroy();
|
||||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||||
[[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; }
|
||||
|
||||
// Sube píxeles ARGB8888 desde CPU; llamado antes de render()
|
||||
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
|
||||
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
||||
|
||||
// Actualiza los parámetros de intensidad de los efectos PostFX
|
||||
void setPostFXParams(const PostFXParams& p) override;
|
||||
|
||||
// Activa/desactiva VSync en el swapchain
|
||||
void setVSync(bool vsync) override;
|
||||
|
||||
// Activa/desactiva escalado entero (integer scale)
|
||||
void setScaleMode(bool integer_scale) override;
|
||||
|
||||
// Establece factor de supersampling (1 = off, 3 = 3×SS)
|
||||
void setOversample(int factor) override;
|
||||
|
||||
void setLinearUpscale(bool linear) override;
|
||||
[[nodiscard]] auto isLinearUpscale() const -> bool override { return linear_upscale_; }
|
||||
void setDownscaleAlgo(int algo) override;
|
||||
[[nodiscard]] auto getDownscaleAlgo() const -> int override { return downscale_algo_; }
|
||||
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
|
||||
|
||||
void setActiveShader(ShaderType type) override;
|
||||
void setCrtPiParams(const CrtPiParams& p) override;
|
||||
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
|
||||
|
||||
private:
|
||||
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||
const char* msl_source,
|
||||
@@ -80,27 +114,41 @@ namespace Rendering {
|
||||
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||||
|
||||
auto createPipeline() -> bool;
|
||||
auto reinitTexturesAndBuffer() -> bool; // Recrea textura y buffer con oversample actual
|
||||
auto createCrtPiPipeline() -> bool;
|
||||
auto reinitTexturesAndBuffer() -> bool;
|
||||
auto recreateScaledTexture(int factor) -> bool;
|
||||
static auto calcSsFactor(float zoom) -> int;
|
||||
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
|
||||
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_GPUDevice* device_ = nullptr;
|
||||
SDL_GPUGraphicsPipeline* pipeline_ = nullptr;
|
||||
SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr;
|
||||
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr;
|
||||
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr;
|
||||
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr;
|
||||
SDL_GPUTexture* scene_texture_ = nullptr;
|
||||
SDL_GPUTexture* scaled_texture_ = nullptr;
|
||||
SDL_GPUTexture* postfx_texture_ = nullptr;
|
||||
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||||
SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling
|
||||
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling
|
||||
SDL_GPUSampler* sampler_ = nullptr;
|
||||
SDL_GPUSampler* linear_sampler_ = nullptr;
|
||||
|
||||
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F};
|
||||
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
|
||||
ShaderType active_shader_ = ShaderType::POSTFX;
|
||||
|
||||
int game_width_ = 0; // Dimensiones originales del canvas (sin SS)
|
||||
int game_width_ = 0;
|
||||
int game_height_ = 0;
|
||||
int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_)
|
||||
int tex_height_ = 0;
|
||||
int oversample_ = 1; // Factor SS actual (1 o 3)
|
||||
float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU
|
||||
int ss_factor_ = 0;
|
||||
int oversample_ = 1;
|
||||
int downscale_algo_ = 1;
|
||||
std::string driver_name_;
|
||||
std::string preferred_driver_;
|
||||
bool is_initialized_ = false;
|
||||
bool vsync_ = true;
|
||||
bool integer_scale_ = false;
|
||||
bool linear_upscale_ = false;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -0,0 +1,633 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
static const uint8_t kupscale_frag_spv[] = {
|
||||
0x03,
|
||||
0x02,
|
||||
0x23,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x14,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x53,
|
||||
0x4c,
|
||||
0x2e,
|
||||
0x73,
|
||||
0x74,
|
||||
0x64,
|
||||
0x2e,
|
||||
0x34,
|
||||
0x35,
|
||||
0x30,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x6d,
|
||||
0x61,
|
||||
0x69,
|
||||
0x6e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x10,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xc2,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x5f,
|
||||
0x47,
|
||||
0x4f,
|
||||
0x4f,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x45,
|
||||
0x5f,
|
||||
0x63,
|
||||
0x70,
|
||||
0x70,
|
||||
0x5f,
|
||||
0x73,
|
||||
0x74,
|
||||
0x79,
|
||||
0x6c,
|
||||
0x65,
|
||||
0x5f,
|
||||
0x6c,
|
||||
0x69,
|
||||
0x6e,
|
||||
0x65,
|
||||
0x5f,
|
||||
0x64,
|
||||
0x69,
|
||||
0x72,
|
||||
0x65,
|
||||
0x63,
|
||||
0x74,
|
||||
0x69,
|
||||
0x76,
|
||||
0x65,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x08,
|
||||
0x00,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x5f,
|
||||
0x47,
|
||||
0x4f,
|
||||
0x4f,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x45,
|
||||
0x5f,
|
||||
0x69,
|
||||
0x6e,
|
||||
0x63,
|
||||
0x6c,
|
||||
0x75,
|
||||
0x64,
|
||||
0x65,
|
||||
0x5f,
|
||||
0x64,
|
||||
0x69,
|
||||
0x72,
|
||||
0x65,
|
||||
0x63,
|
||||
0x74,
|
||||
0x69,
|
||||
0x76,
|
||||
0x65,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x6d,
|
||||
0x61,
|
||||
0x69,
|
||||
0x6e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x6f,
|
||||
0x75,
|
||||
0x74,
|
||||
0x5f,
|
||||
0x63,
|
||||
0x6f,
|
||||
0x6c,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x73,
|
||||
0x63,
|
||||
0x65,
|
||||
0x6e,
|
||||
0x65,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x76,
|
||||
0x5f,
|
||||
0x75,
|
||||
0x76,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x21,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x22,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x21,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x16,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x08,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x08,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x19,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1b,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x10,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x10,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x36,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xf8,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3d,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3d,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x12,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x57,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x12,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3e,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xfd,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x38,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00};
|
||||
static const size_t kupscale_frag_spv_size = 628;
|
||||
@@ -3,9 +3,14 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
/** @brief Identificador del shader de post-procesado activo */
|
||||
enum class ShaderType { POSTFX,
|
||||
CRTPI };
|
||||
|
||||
/**
|
||||
* @brief Parámetros de intensidad de los efectos PostFX
|
||||
*/
|
||||
@@ -20,57 +25,64 @@ namespace Rendering {
|
||||
float flicker = 0.0F;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
|
||||
*/
|
||||
struct CrtPiParams {
|
||||
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};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Interfaz abstracta para backends de renderizado con shaders
|
||||
*
|
||||
* Esta interfaz define el contrato que todos los backends de shaders
|
||||
* deben cumplir (Metal, Vulkan, etc.)
|
||||
*/
|
||||
class ShaderBackend {
|
||||
public:
|
||||
virtual ~ShaderBackend() = default;
|
||||
|
||||
/**
|
||||
* @brief Inicializa el backend de shaders
|
||||
* @param window Ventana SDL
|
||||
* @param texture Textura de backbuffer a la que aplicar shaders
|
||||
* @param vertex_source Código fuente del vertex shader
|
||||
* @param fragment_source Código fuente del fragment shader
|
||||
* @return true si la inicialización fue exitosa
|
||||
*/
|
||||
virtual auto init(SDL_Window* window,
|
||||
SDL_Texture* texture,
|
||||
const std::string& vertex_source,
|
||||
const std::string& fragment_source) -> bool = 0;
|
||||
|
||||
/**
|
||||
* @brief Renderiza la textura con los shaders aplicados
|
||||
*/
|
||||
virtual void render() = 0;
|
||||
|
||||
/**
|
||||
* @brief Establece el tamaño de la textura como parámetro del shader
|
||||
* @param width Ancho de la textura
|
||||
* @param height Alto de la textura
|
||||
*/
|
||||
virtual void setTextureSize(float width, float height) = 0;
|
||||
|
||||
/**
|
||||
* @brief Limpia y libera recursos del backend
|
||||
*/
|
||||
virtual void cleanup() = 0;
|
||||
|
||||
/**
|
||||
* @brief Verifica si el backend está usando aceleración por hardware
|
||||
* @return true si usa aceleración por hardware
|
||||
*/
|
||||
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
|
||||
|
||||
virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}
|
||||
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
||||
virtual void setVSync(bool /*vsync*/) {}
|
||||
virtual void setScaleMode(bool /*integer_scale*/) {}
|
||||
virtual void setOversample(int /*factor*/) {}
|
||||
|
||||
virtual void setLinearUpscale(bool /*linear*/) {}
|
||||
[[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }
|
||||
|
||||
virtual void setDownscaleAlgo(int /*algo*/) {}
|
||||
[[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }
|
||||
|
||||
[[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }
|
||||
|
||||
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
|
||||
|
||||
[[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }
|
||||
virtual void setPreferredDriver(const std::string& /*driver*/) {}
|
||||
|
||||
virtual void setActiveShader(ShaderType /*type*/) {}
|
||||
virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}
|
||||
[[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
+115
-48
@@ -8,16 +8,16 @@
|
||||
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "asset.hpp" // Para Asset
|
||||
#include "mouse.hpp" // Para updateCursorVisibility
|
||||
#include "options.hpp" // Para Video, video, Window, window
|
||||
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||
#include "asset.hpp" // Para Asset
|
||||
#include "mouse.hpp" // Para updateCursorVisibility
|
||||
#include "options.hpp" // Para Video, video, Window, window
|
||||
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||
#include "rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||
#include "text.hpp" // Para Text
|
||||
#include "texture.hpp" // Para Texture
|
||||
#include "ui/logger.hpp" // Para info
|
||||
#include "ui/notifier.hpp" // Para Notifier
|
||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
||||
#include "text.hpp" // Para Text
|
||||
#include "texture.hpp" // Para Texture
|
||||
#include "ui/logger.hpp" // Para info
|
||||
#include "ui/notifier.hpp" // Para Notifier
|
||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
||||
|
||||
// Singleton
|
||||
Screen* Screen::instance = nullptr;
|
||||
@@ -25,7 +25,7 @@ Screen* Screen::instance = nullptr;
|
||||
// Inicializa la instancia única del singleton
|
||||
void Screen::init() {
|
||||
Screen::instance = new Screen();
|
||||
Screen::initPostFX(); // Llamar aquí para que Screen::get() ya devuelva la instancia
|
||||
Screen::initShaders(); // Llamar aquí para que Screen::get() ya devuelva la instancia
|
||||
}
|
||||
|
||||
// Libera la instancia
|
||||
@@ -68,7 +68,6 @@ Screen::Screen()
|
||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderPresent(renderer_);
|
||||
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -112,13 +111,11 @@ void Screen::renderPresent() {
|
||||
SDL_Surface* surface = SDL_RenderReadPixels(renderer_, nullptr);
|
||||
if (surface != nullptr) {
|
||||
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
||||
std::memcpy(pixel_buffer_.data(), surface->pixels,
|
||||
pixel_buffer_.size() * sizeof(Uint32));
|
||||
std::memcpy(pixel_buffer_.data(), surface->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||
} else {
|
||||
SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (converted != nullptr) {
|
||||
std::memcpy(pixel_buffer_.data(), converted->pixels,
|
||||
pixel_buffer_.size() * sizeof(Uint32));
|
||||
std::memcpy(pixel_buffer_.data(), converted->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||
SDL_DestroySurface(converted);
|
||||
}
|
||||
}
|
||||
@@ -256,26 +253,35 @@ void Screen::renderInfo() const {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Inicializa PostFX (SDL3GPU)
|
||||
void Screen::initPostFX() {
|
||||
// Inicializa shaders (SDL3GPU)
|
||||
void Screen::initShaders() {
|
||||
#ifndef NO_SHADERS
|
||||
auto* self = Screen::get();
|
||||
if (self == nullptr) {
|
||||
SDL_Log("Screen::initPostFX: instance is null, skipping");
|
||||
SDL_Log("Screen::initShaders: instance is null, skipping");
|
||||
return;
|
||||
}
|
||||
if (!self->shader_backend_) {
|
||||
self->shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
||||
const std::string FALLBACK_DRIVER = "none";
|
||||
self->shader_backend_->setPreferredDriver(
|
||||
Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER);
|
||||
}
|
||||
if (!self->shader_backend_->isHardwareAccelerated()) {
|
||||
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
||||
SDL_Log("Screen::initPostFX: SDL3GPUShader::init() = %s", ok ? "OK" : "FAILED");
|
||||
SDL_Log("Screen::initShaders: SDL3GPUShader::init() = %s", ok ? "OK" : "FAILED");
|
||||
}
|
||||
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
|
||||
self->shader_backend_->setDownscaleAlgo(Options::video.supersampling.downscale_algo);
|
||||
self->shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||
self->shader_backend_->setActiveShader(Options::video.shader.current_shader);
|
||||
}
|
||||
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||
self->applyCurrentCrtPiPreset();
|
||||
} else {
|
||||
self->applyCurrentPostFXPreset();
|
||||
}
|
||||
SDL_Log("Screen::initPostFX: presets=%d current=%d postfx=%s",
|
||||
static_cast<int>(Options::postfx_presets.size()),
|
||||
Options::current_postfx_preset,
|
||||
Options::video.postfx ? "ON" : "OFF");
|
||||
self->applyCurrentPostFXPreset();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -438,11 +444,34 @@ void Screen::getDisplayInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
// Alterna entre activar y desactivar los efectos PostFX
|
||||
void Screen::togglePostFX() {
|
||||
Options::video.postfx = !Options::video.postfx;
|
||||
// Alterna activar/desactivar shaders
|
||||
void Screen::toggleShaders() {
|
||||
Options::video.shader.enabled = !Options::video.shader.enabled;
|
||||
auto* self = Screen::get();
|
||||
if (self != nullptr) {
|
||||
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||
self->applyCurrentCrtPiPreset();
|
||||
} else {
|
||||
self->applyCurrentPostFXPreset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cambia entre PostFX y CrtPi
|
||||
void Screen::nextShader() {
|
||||
auto* self = Screen::get();
|
||||
if (self == nullptr || !self->shader_backend_ || !self->shader_backend_->isHardwareAccelerated()) { return; }
|
||||
|
||||
const auto NEXT = (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX)
|
||||
? Rendering::ShaderType::CRTPI
|
||||
: Rendering::ShaderType::POSTFX;
|
||||
|
||||
Options::video.shader.current_shader = NEXT;
|
||||
self->shader_backend_->setActiveShader(NEXT);
|
||||
|
||||
if (NEXT == Rendering::ShaderType::CRTPI) {
|
||||
self->applyCurrentCrtPiPreset();
|
||||
} else {
|
||||
self->applyCurrentPostFXPreset();
|
||||
}
|
||||
}
|
||||
@@ -450,49 +479,87 @@ void Screen::togglePostFX() {
|
||||
// Avanza al siguiente preset PostFX
|
||||
void Screen::nextPostFXPreset() {
|
||||
if (Options::postfx_presets.empty()) { return; }
|
||||
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
|
||||
Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
|
||||
auto* self = Screen::get();
|
||||
if (self != nullptr) {
|
||||
self->applyCurrentPostFXPreset();
|
||||
}
|
||||
}
|
||||
|
||||
// Alterna entre activar y desactivar el supersampling 3x
|
||||
// Avanza al siguiente preset CrtPi
|
||||
void Screen::nextCrtPiPreset() {
|
||||
if (Options::crtpi_presets.empty()) { return; }
|
||||
Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
|
||||
auto* self = Screen::get();
|
||||
if (self != nullptr) {
|
||||
self->applyCurrentCrtPiPreset();
|
||||
}
|
||||
}
|
||||
|
||||
// Alterna supersampling
|
||||
void Screen::toggleSupersampling() {
|
||||
Options::video.supersampling = (Options::video.supersampling % 3) + 1;
|
||||
Options::video.supersampling.enabled = !Options::video.supersampling.enabled;
|
||||
auto* self = Screen::get();
|
||||
if (self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||
self->applyCurrentPostFXPreset();
|
||||
self->shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||
self->applyCurrentCrtPiPreset();
|
||||
} else {
|
||||
self->applyCurrentPostFXPreset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aplica el preset PostFX activo al backend
|
||||
void Screen::applyCurrentPostFXPreset() {
|
||||
if (!shader_backend_) { return; }
|
||||
// setOversample PRIMERO: puede recrear texturas antes de que setPostFXParams
|
||||
// decida si hornear scanlines en CPU o aplicarlas en GPU.
|
||||
shader_backend_->setOversample(Options::video.supersampling);
|
||||
shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||
Rendering::PostFXParams p{};
|
||||
if (Options::video.postfx && !Options::postfx_presets.empty()) {
|
||||
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset));
|
||||
p.vignette = preset.vignette;
|
||||
if (Options::video.shader.enabled && !Options::postfx_presets.empty()) {
|
||||
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset));
|
||||
p.vignette = preset.vignette;
|
||||
p.scanlines = preset.scanlines;
|
||||
p.chroma = preset.chroma;
|
||||
p.mask = preset.mask;
|
||||
p.gamma = preset.gamma;
|
||||
p.chroma = preset.chroma;
|
||||
p.mask = preset.mask;
|
||||
p.gamma = preset.gamma;
|
||||
p.curvature = preset.curvature;
|
||||
p.bleeding = preset.bleeding;
|
||||
p.flicker = preset.flicker;
|
||||
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f ss=%d×",
|
||||
preset.name.c_str(), p.scanlines, p.vignette, p.chroma, Options::video.supersampling);
|
||||
} else {
|
||||
SDL_Log("Screen::applyCurrentPostFXPreset: PostFX=%s presets=%d → passthrough",
|
||||
Options::video.postfx ? "ON" : "OFF",
|
||||
static_cast<int>(Options::postfx_presets.size()));
|
||||
p.bleeding = preset.bleeding;
|
||||
p.flicker = preset.flicker;
|
||||
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f",
|
||||
preset.name.c_str(),
|
||||
p.scanlines,
|
||||
p.vignette,
|
||||
p.chroma);
|
||||
}
|
||||
shader_backend_->setPostFXParams(p);
|
||||
}
|
||||
|
||||
// Aplica el preset CrtPi activo al backend
|
||||
void Screen::applyCurrentCrtPiPreset() {
|
||||
if (!shader_backend_) { return; }
|
||||
if (Options::video.shader.enabled && !Options::crtpi_presets.empty()) {
|
||||
const auto& preset = Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset));
|
||||
Rendering::CrtPiParams p{
|
||||
.scanline_weight = preset.scanline_weight,
|
||||
.scanline_gap_brightness = preset.scanline_gap_brightness,
|
||||
.bloom_factor = preset.bloom_factor,
|
||||
.input_gamma = preset.input_gamma,
|
||||
.output_gamma = preset.output_gamma,
|
||||
.mask_brightness = preset.mask_brightness,
|
||||
.curvature_x = preset.curvature_x,
|
||||
.curvature_y = preset.curvature_y,
|
||||
.mask_type = preset.mask_type,
|
||||
.enable_scanlines = preset.enable_scanlines,
|
||||
.enable_multisample = preset.enable_multisample,
|
||||
.enable_gamma = preset.enable_gamma,
|
||||
.enable_curvature = preset.enable_curvature,
|
||||
.enable_sharper = preset.enable_sharper,
|
||||
};
|
||||
shader_backend_->setCrtPiParams(p);
|
||||
SDL_Log("Screen::applyCurrentCrtPiPreset: preset='%s'", preset.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Alterna entre activar y desactivar el escalado entero
|
||||
void Screen::toggleIntegerScale() {
|
||||
Options::video.integer_scale = !Options::video.integer_scale;
|
||||
|
||||
+34
-34
@@ -6,18 +6,15 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "color.hpp" // Para Color
|
||||
#include "options.hpp" // Para VideoOptions, video
|
||||
#include "color.hpp" // Para Color
|
||||
#include "options.hpp" // Para VideoOptions, video
|
||||
#include "rendering/shader_backend.hpp" // Para Rendering::ShaderType
|
||||
|
||||
// Forward declarations
|
||||
class Notifier;
|
||||
class ServiceMenu;
|
||||
class Text;
|
||||
|
||||
namespace Rendering {
|
||||
class ShaderBackend;
|
||||
}
|
||||
|
||||
// --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
|
||||
class Screen {
|
||||
public:
|
||||
@@ -41,18 +38,20 @@ class Screen {
|
||||
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
|
||||
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
|
||||
void applySettings(); // Aplica los valores de las opciones
|
||||
static void initPostFX(); // Inicializa PostFX (SDL3GPU)
|
||||
static void initShaders(); // Inicializa shaders (SDL3GPU)
|
||||
|
||||
// --- Efectos visuales ---
|
||||
void shake(int desp = 2, float delay_s = 0.05F, float duration_s = 0.133F) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay_s, duration_s); } // Agita la pantalla (tiempo en segundos)
|
||||
void flash(Color color, float duration_s = 0.167F, float delay_s = 0.0F) { flash_effect_ = FlashEffect(true, duration_s, delay_s, color); } // Pone la pantalla de color (tiempo en segundos)
|
||||
static void togglePostFX(); // Alterna entre activar y desactivar los efectos PostFX
|
||||
static void nextPostFXPreset(); // Avanza al siguiente preset PostFX
|
||||
static void toggleSupersampling(); // Alterna entre activar y desactivar el supersampling 3x
|
||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||
void setVSync(bool enabled); // Establece el estado del V-Sync
|
||||
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
||||
void shake(int desp = 2, float delay_s = 0.05F, float duration_s = 0.133F) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay_s, duration_s); }
|
||||
void flash(Color color, float duration_s = 0.167F, float delay_s = 0.0F) { flash_effect_ = FlashEffect(true, duration_s, delay_s, color); }
|
||||
static void toggleShaders(); // Alterna activar/desactivar shaders
|
||||
static void nextShader(); // Cambia entre PostFX y CrtPi
|
||||
static void nextPostFXPreset(); // Avanza al siguiente preset PostFX
|
||||
static void nextCrtPiPreset(); // Avanza al siguiente preset CrtPi
|
||||
static void toggleSupersampling(); // Alterna supersampling
|
||||
void toggleIntegerScale();
|
||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||
void setVSync(bool enabled); // Establece el estado del V-Sync
|
||||
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
||||
|
||||
// --- Getters ---
|
||||
auto getRenderer() -> SDL_Renderer* { return renderer_; } // Obtiene el renderizador
|
||||
@@ -220,30 +219,31 @@ class Screen {
|
||||
std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend de shaders (SDL3GPU)
|
||||
|
||||
// --- Variables de estado ---
|
||||
SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego
|
||||
SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego
|
||||
SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego
|
||||
SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego
|
||||
std::vector<Uint32> pixel_buffer_; // Buffer de píxeles para SDL_RenderReadPixels
|
||||
FPS fps_; // Gestión de frames por segundo
|
||||
FlashEffect flash_effect_; // Efecto de flash en pantalla
|
||||
ShakeEffect shake_effect_; // Efecto de agitar la pantalla
|
||||
bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
|
||||
DisplayMonitor display_monitor_; // Información del monitor actual
|
||||
FPS fps_; // Gestión de frames por segundo
|
||||
FlashEffect flash_effect_; // Efecto de flash en pantalla
|
||||
ShakeEffect shake_effect_; // Efecto de agitar la pantalla
|
||||
bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
|
||||
DisplayMonitor display_monitor_; // Información del monitor actual
|
||||
#ifdef _DEBUG
|
||||
Debug debug_info_; // Información de debug
|
||||
#endif
|
||||
|
||||
// --- Métodos internos ---
|
||||
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
|
||||
void renderFlash(); // Dibuja el efecto de flash en la pantalla
|
||||
void renderShake(); // Aplica el efecto de agitar la pantalla
|
||||
void renderInfo() const; // Muestra información por pantalla
|
||||
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
||||
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
|
||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||
void renderOverlays(); // Renderiza todos los overlays y efectos
|
||||
void renderAttenuate(); // Atenúa la pantalla
|
||||
void createText(); // Crea el objeto de texto
|
||||
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
|
||||
void renderFlash(); // Dibuja el efecto de flash en la pantalla
|
||||
void renderShake(); // Aplica el efecto de agitar la pantalla
|
||||
void renderInfo() const; // Muestra información por pantalla
|
||||
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
||||
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
|
||||
void applyCurrentCrtPiPreset(); // Aplica el preset CrtPi activo al backend
|
||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||
void renderOverlays(); // Renderiza todos los overlays y efectos
|
||||
void renderAttenuate(); // Atenúa la pantalla
|
||||
void createText(); // Crea el objeto de texto
|
||||
|
||||
// --- Constructores y destructor privados (singleton) ---
|
||||
Screen(); // Constructor privado
|
||||
|
||||
@@ -93,15 +93,15 @@ class Game {
|
||||
|
||||
// --- Estructuras ---
|
||||
struct Helper {
|
||||
bool need_coffee{false}; // Indica si se necesitan cafes
|
||||
bool need_coffee_machine{false}; // Indica si se necesita PowerUp
|
||||
bool need_power_ball{false}; // Indica si se necesita una PowerBall
|
||||
float counter{HELP_COUNTER_S * 1000}; // Contador para no dar ayudas consecutivas
|
||||
int item_disk_odds{ITEM_POINTS_1_DISK_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_gavina_odds{ITEM_POINTS_2_GAVINA_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_pacmar_odds{ITEM_POINTS_3_PACMAR_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_clock_odds{ITEM_CLOCK_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_coffee_odds{ITEM_COFFEE_ODDS}; // Probabilidad de aparición del objeto
|
||||
bool need_coffee{false}; // Indica si se necesitan cafes
|
||||
bool need_coffee_machine{false}; // Indica si se necesita PowerUp
|
||||
bool need_power_ball{false}; // Indica si se necesita una PowerBall
|
||||
float counter{HELP_COUNTER_S * 1000}; // Contador para no dar ayudas consecutivas
|
||||
int item_disk_odds{ITEM_POINTS_1_DISK_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_gavina_odds{ITEM_POINTS_2_GAVINA_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_pacmar_odds{ITEM_POINTS_3_PACMAR_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_clock_odds{ITEM_CLOCK_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_coffee_odds{ITEM_COFFEE_ODDS}; // Probabilidad de aparición del objeto
|
||||
int item_coffee_machine_odds{ITEM_COFFEE_MACHINE_ODDS}; // Probabilidad de aparición del objeto
|
||||
};
|
||||
|
||||
|
||||
@@ -71,15 +71,15 @@ class Instructions {
|
||||
std::unique_ptr<Fade> fade_; // Objeto para renderizar fades
|
||||
|
||||
// --- Variables ---
|
||||
float elapsed_time_ = 0.0F; // Tiempo transcurrido (segundos)
|
||||
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
|
||||
float elapsed_time_ = 0.0F; // Tiempo transcurrido (segundos)
|
||||
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
|
||||
SDL_FPoint sprite_pos_ = {.x = 0, .y = 0}; // Posición del primer sprite en la lista
|
||||
float item_space_ = 2.0; // Espacio entre los items en pantalla
|
||||
std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla
|
||||
bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla
|
||||
float start_delay_timer_ = 0.0F; // Timer para retraso antes de mover líneas (segundos)
|
||||
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
|
||||
float item_space_ = 2.0; // Espacio entre los items en pantalla
|
||||
std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla
|
||||
bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla
|
||||
float start_delay_timer_ = 0.0F; // Timer para retraso antes de mover líneas (segundos)
|
||||
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
|
||||
|
||||
// --- Métodos internos ---
|
||||
void update(float delta_time); // Actualiza las variables
|
||||
|
||||
+2
-2
@@ -2,12 +2,12 @@
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_FRect, Uint8, SDL_GetRenderTarget, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_TextureAccess, SDL_GetTextureAlphaMod
|
||||
|
||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, istream, ifstream, istringstream
|
||||
#include <utility> // Para std::cmp_less_equal
|
||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, istream, ifstream, istringstream
|
||||
#include <iostream> // Para cerr
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <stdexcept> // Para runtime_error
|
||||
#include <string_view> // Para string_view
|
||||
#include <utility> // Para std::cmp_less_equal
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "color.hpp" // Para Color
|
||||
|
||||
@@ -366,19 +366,20 @@ void ServiceMenu::initializeOptions() {
|
||||
options_.push_back(std::make_unique<BoolOption>(
|
||||
Lang::getText("[SERVICE_MENU] POSTFX"),
|
||||
SettingsGroup::VIDEO,
|
||||
&Options::video.postfx));
|
||||
&Options::video.shader.enabled));
|
||||
|
||||
options_.push_back(std::make_unique<IntOption>(
|
||||
Lang::getText("[SERVICE_MENU] POSTFX_PRESET"),
|
||||
SettingsGroup::VIDEO,
|
||||
&Options::current_postfx_preset,
|
||||
0, static_cast<int>(Options::postfx_presets.size()) - 1, 1));
|
||||
&Options::video.shader.current_postfx_preset,
|
||||
0,
|
||||
static_cast<int>(Options::postfx_presets.size()) - 1,
|
||||
1));
|
||||
|
||||
options_.push_back(std::make_unique<IntOption>(
|
||||
options_.push_back(std::make_unique<BoolOption>(
|
||||
Lang::getText("[SERVICE_MENU] SUPERSAMPLING"),
|
||||
SettingsGroup::VIDEO,
|
||||
&Options::video.supersampling,
|
||||
1, 3, 1));
|
||||
&Options::video.supersampling.enabled));
|
||||
|
||||
options_.push_back(std::make_unique<BoolOption>(
|
||||
Lang::getText("[SERVICE_MENU] VSYNC"),
|
||||
|
||||
@@ -20,6 +20,9 @@ echo "Compiling SPIR-V shaders..."
|
||||
|
||||
glslc "${SHADERS_DIR}/postfx.vert" -o /tmp/postfx.vert.spv
|
||||
glslc "${SHADERS_DIR}/postfx.frag" -o /tmp/postfx.frag.spv
|
||||
glslc -fshader-stage=fragment "${SHADERS_DIR}/crtpi_frag.glsl" -o /tmp/crtpi_frag.spv
|
||||
glslc "${SHADERS_DIR}/upscale.frag" -o /tmp/upscale.frag.spv
|
||||
glslc "${SHADERS_DIR}/downscale.frag" -o /tmp/downscale.frag.spv
|
||||
|
||||
echo "Generating C++ headers..."
|
||||
|
||||
@@ -33,12 +36,30 @@ xxd -i /tmp/postfx.frag.spv | \
|
||||
sed 's/unsigned int .*postfx_frag_spv_len/static const size_t kpostfx_frag_spv_size/' \
|
||||
> "${HEADERS_DIR}/postfx_frag_spv.h"
|
||||
|
||||
xxd -i /tmp/crtpi_frag.spv | \
|
||||
sed 's/unsigned char .*crtpi_frag_spv\[\]/static const uint8_t kcrtpi_frag_spv[]/' | \
|
||||
sed 's/unsigned int .*crtpi_frag_spv_len/static const size_t kcrtpi_frag_spv_size/' \
|
||||
> "${HEADERS_DIR}/crtpi_frag_spv.h"
|
||||
|
||||
xxd -i /tmp/upscale.frag.spv | \
|
||||
sed 's/unsigned char .*upscale_frag_spv\[\]/static const uint8_t kupscale_frag_spv[]/' | \
|
||||
sed 's/unsigned int .*upscale_frag_spv_len/static const size_t kupscale_frag_spv_size/' \
|
||||
> "${HEADERS_DIR}/upscale_frag_spv.h"
|
||||
|
||||
xxd -i /tmp/downscale.frag.spv | \
|
||||
sed 's/unsigned char .*downscale_frag_spv\[\]/static const uint8_t kdownscale_frag_spv[]/' | \
|
||||
sed 's/unsigned int .*downscale_frag_spv_len/static const size_t kdownscale_frag_spv_size/' \
|
||||
> "${HEADERS_DIR}/downscale_frag_spv.h"
|
||||
|
||||
# Prepend required includes to the headers
|
||||
for f in "${HEADERS_DIR}/postfx_vert_spv.h" "${HEADERS_DIR}/postfx_frag_spv.h"; do
|
||||
for f in "${HEADERS_DIR}/postfx_vert_spv.h" "${HEADERS_DIR}/postfx_frag_spv.h" "${HEADERS_DIR}/crtpi_frag_spv.h" "${HEADERS_DIR}/upscale_frag_spv.h" "${HEADERS_DIR}/downscale_frag_spv.h"; do
|
||||
echo -e "#pragma once\n#include <cstdint>\n#include <cstddef>\n$(cat "$f")" > "$f"
|
||||
done
|
||||
|
||||
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
||||
echo " postfx_vert_spv.h"
|
||||
echo " postfx_frag_spv.h"
|
||||
echo " crtpi_frag_spv.h"
|
||||
echo " upscale_frag_spv.h"
|
||||
echo " downscale_frag_spv.h"
|
||||
echo "Rebuild the project to use the new shaders."
|
||||
|
||||
Reference in New Issue
Block a user