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/`.
|
||||||
+18
-7
@@ -125,30 +125,41 @@ if(NOT APPLE)
|
|||||||
|
|
||||||
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||||
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag")
|
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_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_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)
|
if(GLSLC_EXE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}"
|
OUTPUT ${ALL_SHADER_HEADERS}
|
||||||
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh"
|
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}"
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
COMMENT "Compilando shaders SPIR-V..."
|
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")
|
message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
|
||||||
else()
|
else()
|
||||||
if(NOT EXISTS "${SHADER_VERT_H}" OR NOT EXISTS "${SHADER_FRAG_H}")
|
foreach(_h IN LISTS ALL_SHADER_HEADERS)
|
||||||
|
if(NOT EXISTS "${_h}")
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"glslc no encontrado y headers SPIR-V no existen.\n"
|
"glslc no encontrado y header SPIR-V no existe: ${_h}\n"
|
||||||
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
||||||
" choco install vulkan-sdk (Windows)\n"
|
" choco install vulkan-sdk (Windows)\n"
|
||||||
" O genera los headers manualmente: tools/shaders/compile_spirv.sh"
|
" O genera los headers manualmente: tools/shaders/compile_spirv.sh"
|
||||||
)
|
)
|
||||||
else()
|
|
||||||
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
|
||||||
endif()
|
endif()
|
||||||
|
endforeach()
|
||||||
|
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")
|
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
DATA|${SYSTEM_FOLDER}/config.yaml|optional,absolute
|
DATA|${SYSTEM_FOLDER}/config.yaml|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/postfx.yaml|optional,absolute
|
DATA|${SYSTEM_FOLDER}/postfx.yaml|optional,absolute
|
||||||
|
DATA|${SYSTEM_FOLDER}/crtpi.yaml|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
||||||
|
|
||||||
# Archivos de configuración del juego
|
# 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);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "balloon_formations.hpp"
|
#include "balloon_formations.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para max, min, copy
|
#include <algorithm> // Para max, min, copy
|
||||||
#include <utility> // Para std::cmp_less
|
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <cctype> // Para isdigit
|
#include <cctype> // Para isdigit
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
@@ -11,6 +10,7 @@
|
|||||||
#include <map> // Para map, operator==, _Rb_tree_iterator
|
#include <map> // Para map, operator==, _Rb_tree_iterator
|
||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <string> // Para string, char_traits, allocator, operator==, stoi, getline, operator<=>, basic_string
|
#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 "asset.hpp" // Para Asset
|
||||||
#include "balloon.hpp" // Para Balloon
|
#include "balloon.hpp" // Para Balloon
|
||||||
|
|||||||
+5
-2
@@ -186,8 +186,11 @@ namespace Defaults::Video {
|
|||||||
constexpr bool FULLSCREEN = false;
|
constexpr bool FULLSCREEN = false;
|
||||||
constexpr bool VSYNC = true;
|
constexpr bool VSYNC = true;
|
||||||
constexpr bool INTEGER_SCALE = true;
|
constexpr bool INTEGER_SCALE = true;
|
||||||
constexpr bool POSTFX = false;
|
constexpr bool GPU_ACCELERATION = true;
|
||||||
constexpr int SUPERSAMPLING = 1;
|
constexpr bool SHADER_ENABLED = false;
|
||||||
|
constexpr bool SUPERSAMPLING = false;
|
||||||
|
constexpr bool LINEAR_UPSCALE = false;
|
||||||
|
constexpr int DOWNSCALE_ALGO = 1;
|
||||||
} // namespace Defaults::Video
|
} // namespace Defaults::Video
|
||||||
|
|
||||||
namespace Defaults::Music {
|
namespace Defaults::Music {
|
||||||
|
|||||||
@@ -106,8 +106,10 @@ void Director::init() {
|
|||||||
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::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::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
||||||
|
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
||||||
Options::loadFromFile(); // Carga el archivo de configuración
|
Options::loadFromFile(); // Carga el archivo de configuración
|
||||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||||
|
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
||||||
loadParams(); // Carga los parámetros del programa
|
loadParams(); // Carga los parámetros del programa
|
||||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace GlobalEvents {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_WINDOW_RESIZED:
|
case SDL_EVENT_WINDOW_RESIZED:
|
||||||
Screen::initPostFX();
|
Screen::initShaders();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
+28
-13
@@ -62,25 +62,36 @@ namespace GlobalInputs {
|
|||||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.vsync)});
|
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.vsync)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activa o desactiva los efectos PostFX
|
// Activa o desactiva los shaders
|
||||||
void togglePostFX() {
|
void toggleShaders() {
|
||||||
Screen::togglePostFX();
|
Screen::toggleShaders();
|
||||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.postfx)});
|
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shader.enabled)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avanza al siguiente preset PostFX
|
// Cambia entre PostFX y CrtPi
|
||||||
void nextPostFXPreset() {
|
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});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
Screen::nextPostFXPreset();
|
||||||
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset)).name;
|
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});
|
Notifier::get()->show({"PostFX: " + name});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activa o desactiva el supersampling 3x
|
// Activa o desactiva el supersampling
|
||||||
void toggleSupersampling() {
|
void toggleSupersampling() {
|
||||||
Screen::toggleSupersampling();
|
Screen::toggleSupersampling();
|
||||||
const int SS = Options::video.supersampling;
|
Notifier::get()->show({"SS: " + std::string(Options::video.supersampling.enabled ? "ON" : "OFF")});
|
||||||
const std::string SS_LABEL = (SS <= 1) ? "OFF" : (std::to_string(SS) + "\xC3\x97");
|
|
||||||
Notifier::get()->show({"SS: " + SS_LABEL});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cambia al siguiente idioma
|
// 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)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (Input::get()->checkAction(Input::Action::NEXT_POSTFX_PRESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
if (Input::get()->checkAction(Input::Action::NEXT_POSTFX_PRESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||||
nextPostFXPreset();
|
nextPreset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Input::get()->checkAction(Input::Action::TOGGLE_SUPERSAMPLING, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
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_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
|
||||||
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
||||||
{Action::TOGGLE_VIDEO_POSTFX, KeyState(SDL_SCANCODE_F4)},
|
{Action::TOGGLE_VIDEO_POSTFX, KeyState(SDL_SCANCODE_F4)},
|
||||||
|
{Action::NEXT_SHADER, KeyState(SDL_SCANCODE_8)},
|
||||||
{Action::NEXT_POSTFX_PRESET, KeyState(SDL_SCANCODE_9)},
|
{Action::NEXT_POSTFX_PRESET, KeyState(SDL_SCANCODE_9)},
|
||||||
{Action::TOGGLE_SUPERSAMPLING, KeyState(SDL_SCANCODE_0)},
|
{Action::TOGGLE_SUPERSAMPLING, KeyState(SDL_SCANCODE_0)},
|
||||||
{Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
|
{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_INC_SIZE, "WINDOW_INC_SIZE"},
|
||||||
{InputAction::WINDOW_DEC_SIZE, "WINDOW_DEC_SIZE"},
|
{InputAction::WINDOW_DEC_SIZE, "WINDOW_DEC_SIZE"},
|
||||||
{InputAction::TOGGLE_VIDEO_POSTFX, "TOGGLE_VIDEO_POSTFX"},
|
{InputAction::TOGGLE_VIDEO_POSTFX, "TOGGLE_VIDEO_POSTFX"},
|
||||||
|
{InputAction::NEXT_SHADER, "NEXT_SHADER"},
|
||||||
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"},
|
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"},
|
||||||
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
||||||
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, "TOGGLE_VIDEO_INTEGER_SCALE"},
|
{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_INC_SIZE", InputAction::WINDOW_INC_SIZE},
|
||||||
{"WINDOW_DEC_SIZE", InputAction::WINDOW_DEC_SIZE},
|
{"WINDOW_DEC_SIZE", InputAction::WINDOW_DEC_SIZE},
|
||||||
{"TOGGLE_VIDEO_POSTFX", InputAction::TOGGLE_VIDEO_POSTFX},
|
{"TOGGLE_VIDEO_POSTFX", InputAction::TOGGLE_VIDEO_POSTFX},
|
||||||
|
{"NEXT_SHADER", InputAction::NEXT_SHADER},
|
||||||
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET},
|
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET},
|
||||||
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
||||||
{"TOGGLE_VIDEO_INTEGER_SCALE", InputAction::TOGGLE_VIDEO_INTEGER_SCALE},
|
{"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_INC_SIZE,
|
||||||
WINDOW_DEC_SIZE,
|
WINDOW_DEC_SIZE,
|
||||||
TOGGLE_VIDEO_POSTFX,
|
TOGGLE_VIDEO_POSTFX,
|
||||||
|
NEXT_SHADER,
|
||||||
NEXT_POSTFX_PRESET,
|
NEXT_POSTFX_PRESET,
|
||||||
TOGGLE_SUPERSAMPLING,
|
TOGGLE_SUPERSAMPLING,
|
||||||
TOGGLE_VIDEO_INTEGER_SCALE,
|
TOGGLE_VIDEO_INTEGER_SCALE,
|
||||||
|
|||||||
+286
-54
@@ -24,7 +24,7 @@ namespace Options {
|
|||||||
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
||||||
Keyboard keyboard; // Opciones para el teclado
|
Keyboard keyboard; // Opciones para el teclado
|
||||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
std::vector<PostFXPreset> postfx_presets = { // Lista de presets de PostFX
|
std::vector<PostFXPreset> postfx_presets = {
|
||||||
{"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F},
|
{"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},
|
{"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},
|
{"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F},
|
||||||
@@ -32,8 +32,9 @@ namespace Options {
|
|||||||
{"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 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},
|
{"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;
|
||||||
std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
std::vector<CrtPiPreset> crtpi_presets;
|
||||||
|
std::string crtpi_file_path;
|
||||||
|
|
||||||
// Establece el fichero de configuración
|
// Establece el fichero de configuración
|
||||||
void setConfigFile(const std::string& file_path) { settings.config_file = file_path; }
|
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
|
// Establece la ruta del fichero de PostFX
|
||||||
void setPostFXFile(const std::string& path) { postfx_file_path = path; }
|
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
|
// 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) {
|
static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) {
|
||||||
if (node.contains(key)) {
|
if (node.contains(key)) {
|
||||||
@@ -89,11 +93,21 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!postfx_presets.empty()) {
|
if (!postfx_presets.empty()) {
|
||||||
current_postfx_preset = std::clamp(
|
// Resolver nombre → índice
|
||||||
current_postfx_preset, 0,
|
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);
|
static_cast<int>(postfx_presets.size()) - 1);
|
||||||
} else {
|
} 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());
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "PostFX file loaded: %zu preset(s)", postfx_presets.size());
|
||||||
@@ -196,11 +210,135 @@ namespace Options {
|
|||||||
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({"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({"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});
|
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;
|
video.shader.current_postfx_preset = 0;
|
||||||
|
|
||||||
return true;
|
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
|
// Inicializa las opciones del programa
|
||||||
void init() {
|
void init() {
|
||||||
// Dificultades
|
// Dificultades
|
||||||
@@ -234,84 +372,150 @@ namespace Options {
|
|||||||
const auto& vid = yaml["video"];
|
const auto& vid = yaml["video"];
|
||||||
|
|
||||||
if (vid.contains("fullscreen")) {
|
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")) {
|
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")) {
|
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")) {
|
if (vid.contains("integer_scale")) {
|
||||||
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")) {
|
|
||||||
try {
|
try {
|
||||||
const auto& node = vid["supersampling"];
|
video.integer_scale = vid["integer_scale"].get_value<bool>();
|
||||||
if (node.is_boolean()) {
|
|
||||||
ss_enabled = 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;
|
|
||||||
}
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("supersampling_amount")) {
|
|
||||||
|
// --- GPU ---
|
||||||
|
if (vid.contains("gpu")) {
|
||||||
|
const auto& gpu_node = vid["gpu"];
|
||||||
|
if (gpu_node.contains("acceleration")) {
|
||||||
try {
|
try {
|
||||||
int amount = vid["supersampling_amount"].get_value<int>();
|
video.gpu.acceleration = gpu_node["acceleration"].get_value<bool>();
|
||||||
if (amount >= 2) { ss_amount = amount; }
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
video.supersampling = ss_enabled ? ss_amount : 1;
|
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")) {
|
if (vid.contains("postfx_preset")) {
|
||||||
try {
|
try {
|
||||||
int preset = vid["postfx_preset"].get_value<int>();
|
int preset = vid["postfx_preset"].get_value<int>();
|
||||||
// No validamos contra postfx_presets.size() aquí porque postfx.yaml
|
if (preset >= 0) { video.shader.current_postfx_preset = preset; }
|
||||||
// aún no se ha cargado. El clamp se hace en loadPostFXFromFile().
|
} catch (...) {}
|
||||||
if (preset >= 0) {
|
}
|
||||||
current_postfx_preset = preset;
|
}
|
||||||
|
|
||||||
|
// --- 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 {
|
||||||
|
int factor = ss_node.get_value<int>();
|
||||||
|
video.supersampling.enabled = factor >= 2;
|
||||||
}
|
}
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void loadAudioFromYaml(const fkyaml::node& yaml) {
|
void loadAudioFromYaml(const fkyaml::node& yaml) {
|
||||||
if (!yaml.contains("audio")) { return; }
|
if (!yaml.contains("audio")) { return; }
|
||||||
const auto& aud = yaml["audio"];
|
const auto& aud = yaml["audio"];
|
||||||
|
|
||||||
if (aud.contains("enabled")) {
|
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")) {
|
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")) {
|
if (aud.contains("music")) {
|
||||||
const auto& mus = aud["music"];
|
const auto& mus = aud["music"];
|
||||||
if (mus.contains("enabled")) {
|
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")) {
|
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")) {
|
if (aud.contains("sound")) {
|
||||||
const auto& snd = aud["sound"];
|
const auto& snd = aud["sound"];
|
||||||
if (snd.contains("enabled")) {
|
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")) {
|
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 (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (game.contains("autofire")) {
|
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")) {
|
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")) {
|
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) {
|
for (const auto& ctrl : controllers) {
|
||||||
if (i >= GamepadManager::size()) { break; }
|
if (i >= GamepadManager::size()) { break; }
|
||||||
if (ctrl.contains("name")) {
|
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")) {
|
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")) {
|
if (ctrl.contains("player")) {
|
||||||
try {
|
try {
|
||||||
@@ -379,7 +593,9 @@ namespace Options {
|
|||||||
if (!yaml.contains("keyboard")) { return; }
|
if (!yaml.contains("keyboard")) { return; }
|
||||||
const auto& kb = yaml["keyboard"];
|
const auto& kb = yaml["keyboard"];
|
||||||
if (kb.contains("player")) {
|
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 << " 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 << " vsync: " << boolToString(video.vsync) << "\n";
|
||||||
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
||||||
file << " postfx: " << boolToString(video.postfx) << "\n";
|
file << " gpu:\n";
|
||||||
file << " postfx_preset: " << current_postfx_preset << "\n";
|
file << " acceleration: " << boolToString(video.gpu.acceleration) << "\n";
|
||||||
file << " supersampling: " << boolToString(video.supersampling > 1) << "\n";
|
file << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\n";
|
||||||
file << " supersampling_amount: " << std::max(2, video.supersampling) << "\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";
|
file << "\n";
|
||||||
|
|
||||||
// AUDIO
|
// AUDIO
|
||||||
|
|||||||
+54
-11
@@ -19,6 +19,7 @@
|
|||||||
#include "lang.hpp" // for Code
|
#include "lang.hpp" // for Code
|
||||||
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
||||||
#include "player.hpp" // for Player
|
#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: gestión de configuración y opciones del juego ---
|
||||||
namespace Options {
|
namespace Options {
|
||||||
@@ -36,20 +37,59 @@ namespace Options {
|
|||||||
float flicker{0.0F};
|
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 {
|
struct Window {
|
||||||
std::string caption = Defaults::Window::CAPTION; // Texto que aparece en la barra de título de la ventana
|
std::string caption = Defaults::Window::CAPTION;
|
||||||
int zoom = Defaults::Window::ZOOM; // Valor por el que se multiplica el tamaño de la ventana
|
int zoom = Defaults::Window::ZOOM;
|
||||||
int max_zoom = Defaults::Window::MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla
|
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 {
|
struct Video {
|
||||||
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE; // Filtro usado para el escalado de la imagen
|
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE;
|
||||||
bool fullscreen = Defaults::Video::FULLSCREEN; // Indica si se usa pantalla completa
|
bool fullscreen = Defaults::Video::FULLSCREEN;
|
||||||
bool vsync = Defaults::Video::VSYNC; // Indica si se usa vsync
|
bool vsync = Defaults::Video::VSYNC;
|
||||||
bool integer_scale = Defaults::Video::INTEGER_SCALE; // Indica si se usa escalado entero
|
bool integer_scale = Defaults::Video::INTEGER_SCALE;
|
||||||
bool postfx = Defaults::Video::POSTFX; // Indica si se usan efectos PostFX
|
std::string info;
|
||||||
int supersampling = Defaults::Video::SUPERSAMPLING; // Factor de supersampling: 1=off, 2=2×, 3=3×
|
GPU gpu{};
|
||||||
std::string info; // Información sobre el modo de vídeo
|
Supersampling supersampling{};
|
||||||
|
ShaderConfig shader{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Music {
|
struct Music {
|
||||||
@@ -292,16 +332,19 @@ namespace Options {
|
|||||||
extern Keyboard keyboard; // Opciones para el teclado
|
extern Keyboard keyboard; // Opciones para el teclado
|
||||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
extern std::vector<PostFXPreset> postfx_presets; // Lista de presets de PostFX
|
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::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 ---
|
// --- Funciones ---
|
||||||
void init(); // Inicializa las opciones del programa
|
void init(); // Inicializa las opciones del programa
|
||||||
void setConfigFile(const std::string& file_path); // Establece el fichero de configuración
|
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 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 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 loadPostFXFromFile() -> bool; // Carga los presets PostFX desde fichero
|
||||||
auto savePostFXToFile() -> bool; // Guarda los presets PostFX por defecto al 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 loadFromFile() -> bool; // Carga el fichero de configuración
|
||||||
auto saveToFile() -> bool; // Guarda 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
|
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.h>
|
||||||
#include <SDL3/SDL_gpu.h>
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "rendering/shader_backend.hpp"
|
#include "rendering/shader_backend.hpp"
|
||||||
|
|
||||||
// PostFX uniforms pushed to fragment stage each frame.
|
// 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.
|
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||||||
struct PostFXUniforms {
|
struct PostFXUniforms {
|
||||||
float vignette_strength; // 0 = none, ~0.8 = subtle
|
float vignette_strength;
|
||||||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
float chroma_strength;
|
||||||
float scanline_strength; // 0 = off, 1 = full
|
float scanline_strength;
|
||||||
float screen_height; // logical height in pixels (used by bleeding effect)
|
float screen_height;
|
||||||
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
float mask_strength;
|
||||||
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
float gamma_strength;
|
||||||
float curvature; // 0 = flat, 1 = max barrel distortion
|
float curvature;
|
||||||
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
float bleeding;
|
||||||
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
float pixel_scale;
|
||||||
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
float time;
|
||||||
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
float oversample;
|
||||||
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
|
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 {
|
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)
|
* @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)
|
* 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 {
|
class SDL3GPUShader : public ShaderBackend {
|
||||||
public:
|
public:
|
||||||
@@ -44,25 +75,28 @@ namespace Rendering {
|
|||||||
|
|
||||||
void render() override;
|
void render() override;
|
||||||
void setTextureSize(float width, float height) override {}
|
void setTextureSize(float width, float height) override {}
|
||||||
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
|
void cleanup() final;
|
||||||
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
|
void destroy();
|
||||||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
[[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;
|
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;
|
void setPostFXParams(const PostFXParams& p) override;
|
||||||
|
|
||||||
// Activa/desactiva VSync en el swapchain
|
|
||||||
void setVSync(bool vsync) override;
|
void setVSync(bool vsync) override;
|
||||||
|
|
||||||
// Activa/desactiva escalado entero (integer scale)
|
|
||||||
void setScaleMode(bool integer_scale) override;
|
void setScaleMode(bool integer_scale) override;
|
||||||
|
|
||||||
// Establece factor de supersampling (1 = off, 3 = 3×SS)
|
|
||||||
void setOversample(int factor) override;
|
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:
|
private:
|
||||||
static auto createShaderMSL(SDL_GPUDevice* device,
|
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||||
const char* msl_source,
|
const char* msl_source,
|
||||||
@@ -80,27 +114,41 @@ namespace Rendering {
|
|||||||
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||||||
|
|
||||||
auto createPipeline() -> bool;
|
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_Window* window_ = nullptr;
|
||||||
SDL_GPUDevice* device_ = nullptr;
|
SDL_GPUDevice* device_ = nullptr;
|
||||||
SDL_GPUGraphicsPipeline* pipeline_ = 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* scene_texture_ = nullptr;
|
||||||
|
SDL_GPUTexture* scaled_texture_ = nullptr;
|
||||||
|
SDL_GPUTexture* postfx_texture_ = nullptr;
|
||||||
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||||||
SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling
|
SDL_GPUSampler* sampler_ = nullptr;
|
||||||
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling
|
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};
|
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 game_height_ = 0;
|
||||||
int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_)
|
int ss_factor_ = 0;
|
||||||
int tex_height_ = 0;
|
int oversample_ = 1;
|
||||||
int oversample_ = 1; // Factor SS actual (1 o 3)
|
int downscale_algo_ = 1;
|
||||||
float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU
|
std::string driver_name_;
|
||||||
|
std::string preferred_driver_;
|
||||||
bool is_initialized_ = false;
|
bool is_initialized_ = false;
|
||||||
bool vsync_ = true;
|
bool vsync_ = true;
|
||||||
bool integer_scale_ = false;
|
bool integer_scale_ = false;
|
||||||
|
bool linear_upscale_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // 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 <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
|
|
||||||
|
/** @brief Identificador del shader de post-procesado activo */
|
||||||
|
enum class ShaderType { POSTFX,
|
||||||
|
CRTPI };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parámetros de intensidad de los efectos PostFX
|
* @brief Parámetros de intensidad de los efectos PostFX
|
||||||
*/
|
*/
|
||||||
@@ -20,57 +25,64 @@ namespace Rendering {
|
|||||||
float flicker = 0.0F;
|
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
|
* @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 {
|
class ShaderBackend {
|
||||||
public:
|
public:
|
||||||
virtual ~ShaderBackend() = default;
|
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,
|
virtual auto init(SDL_Window* window,
|
||||||
SDL_Texture* texture,
|
SDL_Texture* texture,
|
||||||
const std::string& vertex_source,
|
const std::string& vertex_source,
|
||||||
const std::string& fragment_source) -> bool = 0;
|
const std::string& fragment_source) -> bool = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Renderiza la textura con los shaders aplicados
|
|
||||||
*/
|
|
||||||
virtual void render() = 0;
|
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;
|
virtual void setTextureSize(float width, float height) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Limpia y libera recursos del backend
|
|
||||||
*/
|
|
||||||
virtual void cleanup() = 0;
|
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 uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}
|
||||||
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
||||||
virtual void setVSync(bool /*vsync*/) {}
|
virtual void setVSync(bool /*vsync*/) {}
|
||||||
virtual void setScaleMode(bool /*integer_scale*/) {}
|
virtual void setScaleMode(bool /*integer_scale*/) {}
|
||||||
virtual void setOversample(int /*factor*/) {}
|
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
|
} // namespace Rendering
|
||||||
|
|||||||
+98
-31
@@ -25,7 +25,7 @@ Screen* Screen::instance = nullptr;
|
|||||||
// Inicializa la instancia única del singleton
|
// Inicializa la instancia única del singleton
|
||||||
void Screen::init() {
|
void Screen::init() {
|
||||||
Screen::instance = new Screen();
|
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
|
// Libera la instancia
|
||||||
@@ -68,7 +68,6 @@ Screen::Screen()
|
|||||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||||
SDL_RenderClear(renderer_);
|
SDL_RenderClear(renderer_);
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -112,13 +111,11 @@ void Screen::renderPresent() {
|
|||||||
SDL_Surface* surface = SDL_RenderReadPixels(renderer_, nullptr);
|
SDL_Surface* surface = SDL_RenderReadPixels(renderer_, nullptr);
|
||||||
if (surface != nullptr) {
|
if (surface != nullptr) {
|
||||||
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
||||||
std::memcpy(pixel_buffer_.data(), surface->pixels,
|
std::memcpy(pixel_buffer_.data(), surface->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
pixel_buffer_.size() * sizeof(Uint32));
|
|
||||||
} else {
|
} else {
|
||||||
SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
||||||
if (converted != nullptr) {
|
if (converted != nullptr) {
|
||||||
std::memcpy(pixel_buffer_.data(), converted->pixels,
|
std::memcpy(pixel_buffer_.data(), converted->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
pixel_buffer_.size() * sizeof(Uint32));
|
|
||||||
SDL_DestroySurface(converted);
|
SDL_DestroySurface(converted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,26 +253,35 @@ void Screen::renderInfo() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Inicializa PostFX (SDL3GPU)
|
// Inicializa shaders (SDL3GPU)
|
||||||
void Screen::initPostFX() {
|
void Screen::initShaders() {
|
||||||
#ifndef NO_SHADERS
|
#ifndef NO_SHADERS
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self == nullptr) {
|
if (self == nullptr) {
|
||||||
SDL_Log("Screen::initPostFX: instance is null, skipping");
|
SDL_Log("Screen::initShaders: instance is null, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!self->shader_backend_) {
|
if (!self->shader_backend_) {
|
||||||
self->shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
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()) {
|
if (!self->shader_backend_->isHardwareAccelerated()) {
|
||||||
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
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");
|
||||||
}
|
}
|
||||||
SDL_Log("Screen::initPostFX: presets=%d current=%d postfx=%s",
|
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||||
static_cast<int>(Options::postfx_presets.size()),
|
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
|
||||||
Options::current_postfx_preset,
|
self->shader_backend_->setDownscaleAlgo(Options::video.supersampling.downscale_algo);
|
||||||
Options::video.postfx ? "ON" : "OFF");
|
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();
|
self->applyCurrentPostFXPreset();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,11 +444,34 @@ void Screen::getDisplayInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alterna entre activar y desactivar los efectos PostFX
|
// Alterna activar/desactivar shaders
|
||||||
void Screen::togglePostFX() {
|
void Screen::toggleShaders() {
|
||||||
Options::video.postfx = !Options::video.postfx;
|
Options::video.shader.enabled = !Options::video.shader.enabled;
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr) {
|
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();
|
self->applyCurrentPostFXPreset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,31 +479,44 @@ void Screen::togglePostFX() {
|
|||||||
// Avanza al siguiente preset PostFX
|
// Avanza al siguiente preset PostFX
|
||||||
void Screen::nextPostFXPreset() {
|
void Screen::nextPostFXPreset() {
|
||||||
if (Options::postfx_presets.empty()) { return; }
|
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();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr) {
|
if (self != nullptr) {
|
||||||
self->applyCurrentPostFXPreset();
|
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() {
|
void Screen::toggleSupersampling() {
|
||||||
Options::video.supersampling = (Options::video.supersampling % 3) + 1;
|
Options::video.supersampling.enabled = !Options::video.supersampling.enabled;
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
if (self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||||
|
self->shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
} else {
|
||||||
self->applyCurrentPostFXPreset();
|
self->applyCurrentPostFXPreset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplica el preset PostFX activo al backend
|
// Aplica el preset PostFX activo al backend
|
||||||
void Screen::applyCurrentPostFXPreset() {
|
void Screen::applyCurrentPostFXPreset() {
|
||||||
if (!shader_backend_) { return; }
|
if (!shader_backend_) { return; }
|
||||||
// setOversample PRIMERO: puede recrear texturas antes de que setPostFXParams
|
shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||||
// decida si hornear scanlines en CPU o aplicarlas en GPU.
|
|
||||||
shader_backend_->setOversample(Options::video.supersampling);
|
|
||||||
Rendering::PostFXParams p{};
|
Rendering::PostFXParams p{};
|
||||||
if (Options::video.postfx && !Options::postfx_presets.empty()) {
|
if (Options::video.shader.enabled && !Options::postfx_presets.empty()) {
|
||||||
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset));
|
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset));
|
||||||
p.vignette = preset.vignette;
|
p.vignette = preset.vignette;
|
||||||
p.scanlines = preset.scanlines;
|
p.scanlines = preset.scanlines;
|
||||||
p.chroma = preset.chroma;
|
p.chroma = preset.chroma;
|
||||||
@@ -483,16 +525,41 @@ void Screen::applyCurrentPostFXPreset() {
|
|||||||
p.curvature = preset.curvature;
|
p.curvature = preset.curvature;
|
||||||
p.bleeding = preset.bleeding;
|
p.bleeding = preset.bleeding;
|
||||||
p.flicker = preset.flicker;
|
p.flicker = preset.flicker;
|
||||||
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f ss=%d×",
|
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f",
|
||||||
preset.name.c_str(), p.scanlines, p.vignette, p.chroma, Options::video.supersampling);
|
preset.name.c_str(),
|
||||||
} else {
|
p.scanlines,
|
||||||
SDL_Log("Screen::applyCurrentPostFXPreset: PostFX=%s presets=%d → passthrough",
|
p.vignette,
|
||||||
Options::video.postfx ? "ON" : "OFF",
|
p.chroma);
|
||||||
static_cast<int>(Options::postfx_presets.size()));
|
|
||||||
}
|
}
|
||||||
shader_backend_->setPostFXParams(p);
|
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
|
// Alterna entre activar y desactivar el escalado entero
|
||||||
void Screen::toggleIntegerScale() {
|
void Screen::toggleIntegerScale() {
|
||||||
Options::video.integer_scale = !Options::video.integer_scale;
|
Options::video.integer_scale = !Options::video.integer_scale;
|
||||||
|
|||||||
+10
-10
@@ -8,16 +8,13 @@
|
|||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
#include "options.hpp" // Para VideoOptions, video
|
#include "options.hpp" // Para VideoOptions, video
|
||||||
|
#include "rendering/shader_backend.hpp" // Para Rendering::ShaderType
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Notifier;
|
class Notifier;
|
||||||
class ServiceMenu;
|
class ServiceMenu;
|
||||||
class Text;
|
class Text;
|
||||||
|
|
||||||
namespace Rendering {
|
|
||||||
class ShaderBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
|
// --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
|
||||||
class Screen {
|
class Screen {
|
||||||
public:
|
public:
|
||||||
@@ -41,15 +38,17 @@ class Screen {
|
|||||||
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
|
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
|
||||||
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
|
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
|
||||||
void applySettings(); // Aplica los valores de las opciones
|
void applySettings(); // Aplica los valores de las opciones
|
||||||
static void initPostFX(); // Inicializa PostFX (SDL3GPU)
|
static void initShaders(); // Inicializa shaders (SDL3GPU)
|
||||||
|
|
||||||
// --- Efectos visuales ---
|
// --- 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 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); } // Pone la pantalla de color (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); }
|
||||||
static void togglePostFX(); // Alterna entre activar y desactivar los efectos PostFX
|
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 nextPostFXPreset(); // Avanza al siguiente preset PostFX
|
||||||
static void toggleSupersampling(); // Alterna entre activar y desactivar el supersampling 3x
|
static void nextCrtPiPreset(); // Avanza al siguiente preset CrtPi
|
||||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
static void toggleSupersampling(); // Alterna supersampling
|
||||||
|
void toggleIntegerScale();
|
||||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||||
void setVSync(bool enabled); // Establece el estado del V-Sync
|
void setVSync(bool enabled); // Establece el estado del V-Sync
|
||||||
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
||||||
@@ -239,6 +238,7 @@ class Screen {
|
|||||||
void renderInfo() const; // Muestra información por pantalla
|
void renderInfo() const; // Muestra información por pantalla
|
||||||
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
||||||
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
|
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 adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||||
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||||
void renderOverlays(); // Renderiza todos los overlays y efectos
|
void renderOverlays(); // Renderiza todos los overlays y efectos
|
||||||
|
|||||||
+1
-1
@@ -3,11 +3,11 @@
|
|||||||
#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 <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 <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, istream, ifstream, istringstream
|
||||||
#include <utility> // Para std::cmp_less_equal
|
|
||||||
#include <iostream> // Para cerr
|
#include <iostream> // Para cerr
|
||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
|
#include <utility> // Para std::cmp_less_equal
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
|
|||||||
@@ -366,19 +366,20 @@ void ServiceMenu::initializeOptions() {
|
|||||||
options_.push_back(std::make_unique<BoolOption>(
|
options_.push_back(std::make_unique<BoolOption>(
|
||||||
Lang::getText("[SERVICE_MENU] POSTFX"),
|
Lang::getText("[SERVICE_MENU] POSTFX"),
|
||||||
SettingsGroup::VIDEO,
|
SettingsGroup::VIDEO,
|
||||||
&Options::video.postfx));
|
&Options::video.shader.enabled));
|
||||||
|
|
||||||
options_.push_back(std::make_unique<IntOption>(
|
options_.push_back(std::make_unique<IntOption>(
|
||||||
Lang::getText("[SERVICE_MENU] POSTFX_PRESET"),
|
Lang::getText("[SERVICE_MENU] POSTFX_PRESET"),
|
||||||
SettingsGroup::VIDEO,
|
SettingsGroup::VIDEO,
|
||||||
&Options::current_postfx_preset,
|
&Options::video.shader.current_postfx_preset,
|
||||||
0, static_cast<int>(Options::postfx_presets.size()) - 1, 1));
|
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"),
|
Lang::getText("[SERVICE_MENU] SUPERSAMPLING"),
|
||||||
SettingsGroup::VIDEO,
|
SettingsGroup::VIDEO,
|
||||||
&Options::video.supersampling,
|
&Options::video.supersampling.enabled));
|
||||||
1, 3, 1));
|
|
||||||
|
|
||||||
options_.push_back(std::make_unique<BoolOption>(
|
options_.push_back(std::make_unique<BoolOption>(
|
||||||
Lang::getText("[SERVICE_MENU] VSYNC"),
|
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.vert" -o /tmp/postfx.vert.spv
|
||||||
glslc "${SHADERS_DIR}/postfx.frag" -o /tmp/postfx.frag.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..."
|
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/' \
|
sed 's/unsigned int .*postfx_frag_spv_len/static const size_t kpostfx_frag_spv_size/' \
|
||||||
> "${HEADERS_DIR}/postfx_frag_spv.h"
|
> "${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
|
# 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"
|
echo -e "#pragma once\n#include <cstdint>\n#include <cstddef>\n$(cat "$f")" > "$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
||||||
echo " postfx_vert_spv.h"
|
echo " postfx_vert_spv.h"
|
||||||
echo " postfx_frag_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."
|
echo "Rebuild the project to use the new shaders."
|
||||||
|
|||||||
Reference in New Issue
Block a user