docs: detalla el pipeline de shaders i la física al document d'arquitectura

This commit is contained in:
2026-05-29 11:56:14 +02:00
parent 2e4030c2f2
commit 582bd0ee30
+80 -24
View File
@@ -378,19 +378,41 @@ OFF, la escena offscreen sale tal cual (passthrough), útil para A/B testing.
- **[Curtain](source/core/graphics/curtain.hpp)** — cortinilla negra para - **[Curtain](source/core/graphics/curtain.hpp)** — cortinilla negra para
transiciones; se pinta siempre la última. transiciones; se pinta siempre la última.
### 5.5 Shaders ### 5.5 Shaders: fuentes, compilación y selección
Las fuentes GLSL viven en [shaders/](shaders/) (`line.vert/frag`, `bloom.frag`, Las fuentes GLSL viven en [shaders/](shaders/): `line.vert.glsl`, `line.frag.glsl`,
`postfx.vert/frag`). Se compilan a SPIR-V (Linux/Windows) y MSL (macOS) y se `postfx.vert.glsl`, `postfx.frag.glsl`, `bloom.frag.glsl`. **No se cargan de disco en
**embeben** como cabeceras en el binario: ver runtime**: se embeben como arrays/strings en el binario.
[gpu/spv/](source/core/rendering/gpu/spv/) y [gpu/msl/](source/core/rendering/gpu/msl/).
La selección por plataforma la hace el `GpuDevice`. No se cargan shaders de disco
en runtime.
> No verificado en detalle: los pormenores del pipeline de compilación de shaders **Pipeline de compilación (SPIR-V, Linux/Windows).** Lo orquesta
> (qué target de CMake genera los `.spv.h`/`.msl.h`). Los headers embebidos existen [CMakeLists.txt:139-187](CMakeLists.txt#L139). La lógica clave:
> y se referencian desde las pipelines; el mecanismo exacto de generación habría que
> confirmarlo en [CMakeLists.txt](CMakeLists.txt) y [tools/shaders/](tools/shaders/). - Para cada `.glsl` hay un header destino en
[gpu/spv/](source/core/rendering/gpu/spv/) (p. ej. `line_vert_spv.h`).
- CMake busca `glslc` (`find_program(GLSLC_EXE ...)`). Hay **tres caminos**:
1. `glslc` presente → un `add_custom_command` regenera los headers SPV cuando
cambian los `.glsl`, vía el target `shaders` del que depende el ejecutable.
2. `glslc` ausente pero **los headers ya están commiteados** → se usan tal cual
(los `.spv.h` están versionados en el repo).
3. `glslc` ausente **y** faltan headers → `FATAL_ERROR` pidiendo instalar
`shaderc`/`vulkan-sdk`.
- La conversión binario→header la hace el script
[tools/shaders/compile_spirv.cmake](tools/shaders/compile_spirv.cmake): invoca
`glslc -O -fshader-stage=<vert|frag>` para producir el `.spv`, lee el binario como
hex (`file(READ ... HEX)`) y escribe un header con
`static const uint8_t LINE_VERT_SPV[] = { 0x.., ... };` y su `_SIZE`. Es
multiplataforma puro CMake (no necesita `bash` ni `xxd`).
**MSL (macOS).** Los headers Metal en [gpu/msl/](source/core/rendering/gpu/msl/)
(`line_vert.msl.h`, etc.) están **escritos a mano** (no los genera CMake), como
strings literales C++.
**Selección SPV vs MSL: es _compile-time_, no runtime.** La hace
[shader_factory.hpp](source/core/rendering/gpu/shader_factory.hpp) con `#ifdef __APPLE__`:
en Apple expone `createShaderMSL(...)` (`SDL_GPU_SHADERFORMAT_MSL`), y en el resto
`createShaderSPIRV(...)` (`SDL_GPU_SHADERFORMAT_SPIRV`). Cada pipeline llama al helper
disponible con el header embebido correspondiente. (Es decir: no es `GpuDevice` quien
elige el backend de shader, sino el preprocesador al compilar.)
--- ---
@@ -637,6 +659,43 @@ primer toque y muere al segundo durante ese estado.
[StageLoader](source/game/stage_system/stage_loader.hpp) — modelo y carga del [StageLoader](source/game/stage_system/stage_loader.hpp) — modelo y carga del
YAML de stages. YAML de stages.
### 10.6 Dos capas de colisión: física vs gameplay
Conviene no confundirlas, porque conviven:
**1. Física** — [PhysicsWorld](source/core/physics/physics_world.hpp) /
[physics_world.cpp](source/core/physics/physics_world.cpp). Es un mundo 2D
minimalista de arcade. Cada frame, `update(dt)` hace tres pasos:
1. **Integración** semi-implícita de Euler con damping exponencial
(`v += (F·invMass)·dt; v *= exp(-damping·dt); x += v·dt`) sobre cada
[RigidBody](source/core/physics/rigid_body.hpp) no estático. Un cuerpo con
`mass=0` (`inverse_mass=0`) es estático (masa infinita).
2. **Rebote contra los bordes** del `PLAYAREA` (`resolveBoundsCollisions`): reposiciona
el cuerpo dentro del rect y refleja la componente normal de la velocidad por su
`restitution`. Antes de reflejar, invoca un `BoundsHitCallback` opcional con la
velocidad de impacto entrante (lo usa GameScene para los efectos de borde).
3. **Colisiones cuerpo-cuerpo** (`resolveBodyCollisions`): broadphase trivial
**O(n²)** (suficiente para ~23 cuerpos), círculo-círculo, con corrección posicional
de penetración + **impulso elástico** `j = -(1+e)(v_rel·n) / (1/mₐ + 1/m_b)`
(referencia Box2D / Chris Hecker, en `resolveBodyPair`). Los cuerpos con `radius=0`
(las balas, cinemáticas puras) **no** participan aquí.
Los `RigidBody` los poseen las entidades; el mundo solo guarda punteros no-owning
(`addBody`/`removeBody`).
**2. Gameplay** — [collision_system.cpp](source/game/systems/collision_system.cpp)
(ver [§10.4](#104-colisiones)), que decide *qué pasa* (daño, score, muerte). Usa los
helpers de [collision.hpp](source/core/physics/collision.hpp): `checkCollision`
(círculo-círculo discreto, distancia al cuadrado sin `sqrt`) y `checkCollisionSwept`
(segment-círculo, para que una bala rápida no atraviese un enemigo entre frames —
*anti-tunneling*). Estos checks usan el `collision_radius` de la **entidad**
(con amplificador opcional de hitbox), no el `radius` del body.
En resumen: la **física** mueve y rebota los cuerpos; el **gameplay** detecta los
contactos relevantes para las reglas. Una bala no rebota físicamente (radius 0) pero sí
provoca daño vía el check *swept*.
--- ---
## 11. IA del modo demo (attract) ## 11. IA del modo demo (attract)
@@ -765,18 +824,15 @@ Viven en [game/effects/](source/game/effects/) y son managers con pools:
### Notas de honestidad sobre la cobertura ### Notas de honestidad sobre la cobertura
- Las secciones de **render** ([§5](#5-renderizado-de-la-lógica-al-píxel)), **bucle** - Todas las secciones se verificaron leyendo directamente los ficheros y firmas
([§3](#3-bucle-principal)), **escenas** ([§4](#4-sistema-de-escenas)), **eventos citados, incluyendo el **pipeline de compilación de shaders**
globales** ([§9](#9-comunicación-entre-módulos)), **GameScene/IA demo** ([§5.5](#55-shaders-fuentes-compilación-y-selección): `CMakeLists.txt` +
([§10](#10-lógica-del-juego)[§11](#11-ia-del-modo-demo-attract)) se verificaron `tools/shaders/compile_spirv.cmake` + `shader_factory.hpp`) y el interior de la
leyendo directamente los ficheros y firmas citados. **física** ([§10.6](#106-dos-capas-de-colisión-física-vs-gameplay):
- El **pipeline de compilación de shaders** (cómo se generan los `.spv.h`/`.msl.h`) `physics_world.cpp` + `collision.hpp` + `rigid_body.hpp`).
no se ha trazado al detalle; los headers embebidos existen y se usan, pero el - Lo que **no** se ha trazado a fondo y queda como lectura directa del código si hace
paso de build exacto queda por confirmar en [CMakeLists.txt](CMakeLists.txt) / falta: los detalles finos de animación de cada overlay (curvas de easing del
[tools/shaders/](tools/shaders/). `Notifier`/`ServiceMenu`) y la coreografía interna completa de `LogoScene` y
- El detalle interno de la **PhysicsWorld** (algoritmo de resolución de colisiones `TitleScene` (más allá de sus estados). Son descriptivos, no estructurales.
físicas) se ha descrito a alto nivel; para el detalle, ver
[physics_world.cpp](source/core/physics/physics_world.cpp) y
[collision.hpp](source/core/physics/collision.hpp).
</content> </content>
</invoke> </invoke>