ba6fd00b54
Preparacion del pipeline GPU. Codigo nuevo aislado en
core/rendering/gpu/; el runtime sigue usando SDL_Renderer hasta
Fase 7b. Tras 7a el juego sigue funcionando identico.
Shaders (shaders/):
- line.vert.glsl: vertex shader, transforma de pixeles logicos a NDC
via uniform buffer LineUniforms{viewport_w, viewport_h}.
- line.frag.glsl: pinta el color RGBA interpolado.
Build:
- CMakeLists.txt: step nuevo que compila *.glsl a build/shaders/*.spv
con glslc. ALL depende del target 'shaders' para incluirlo en cada
build. Falla en cmake config si glslc no esta instalado.
Wrappers C++ (source/core/rendering/gpu/):
- gpu_device.hpp/cpp: GpuDevice, claim del window, loadShader desde
.spv. Backends solicitados: Vulkan + Metal (sin DirectX).
- gpu_line_pipeline.hpp/cpp: GpuLinePipeline. Vertex layout
(vec2 pos + vec4 color), primitive TRIANGLELIST (lineas como
quads), alpha blending estandar, sin culling ni depth.
- gpu_frame_renderer.hpp/cpp: GpuFrameRenderer, API alto nivel:
beginFrame / pushLine / endFrame. Extrusion perpendicular en CPU
por linea (thickness libre por linea). Un draw call por frame
con vertex+index buffers transitorios.
Plan: 7b swap del SDL_Renderer al GpuFrameRenderer en SDLManager.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
140 lines
6.8 KiB
Markdown
140 lines
6.8 KiB
Markdown
# Plan de migración Orni Attack → SDL3 GPU + física vectorial
|
||
|
||
Documento maestro de la rama `rewrite/physics-gpu`. Resumen del progreso y
|
||
qué queda. Si pierdes el contexto de la sesión, **léeme primero**.
|
||
|
||
## Visión
|
||
|
||
Modernizar la base técnica del juego antes de tunear jugabilidad:
|
||
1. Limpiar legacy (polares, mezcla catalán/inglés, header guards).
|
||
2. Adoptar formato 16:9 (1280×720).
|
||
3. Importar subsistemas robustos de AEEA (audio, *input deferido*).
|
||
4. Implementar física vectorial 2D con rigid bodies + colisiones elásticas.
|
||
5. **Migrar renderizado a SDL3 GPU sin fallback** ([memoria: project-no-sdl-fallback](./.claude/projects/-home-sergio-gitea-orni-attack/memory/project_no_sdl_fallback.md)).
|
||
6. Postprocesado y color.
|
||
|
||
El tuning de feeling se hace al final, post-GPU.
|
||
|
||
## Estado actual
|
||
|
||
Rama de trabajo: **`rewrite/physics-gpu`** (pusheada a Gitea).
|
||
Tag de seguridad: **`beta-3.0`** (snapshot de `main` antes de empezar).
|
||
|
||
| Fase | Estado | Commits |
|
||
|---|---|---|
|
||
| 0 — Limpieza de código muerto (primitives, polígonos, Bresenham, polares) | ✅ | `6cf990b` |
|
||
| 1a — `Punt → Vec2` con operadores modernos | ✅ | `cd38101` |
|
||
| 1b — Renames de entidades + métodos virtuales a camelBack | ✅ | `ae5cc1c` |
|
||
| 1c — Renames de escenas | ✅ | `5871d29` |
|
||
| 1d — Renames effects/stage_system/locales | ✅ | `7ee359b` |
|
||
| 1e — Pragma once + locales restantes + comentarios castellano | ✅ | `bf83f16` |
|
||
| (fix) — clave YAML `quadrat → cuadrado` post-sweep | ✅ | `56533ca` |
|
||
| 2 — Cambio a 1280×720 (16:9) | ✅ | `a4f6a55` |
|
||
| 3 — Import del subsistema de Audio desde AEEA | ✅ | `ed98ef6` |
|
||
| **4** — Input parcial AEEA | ⏭️ **saltado** (decisión usuario) | — |
|
||
| 5 — Infraestructura física (`RigidBody`, `PhysicsWorld`) | ✅ | `0fd9360` |
|
||
| 6a+b — `body_` en Entity + `world_` en GameScene | ✅ | `0574077` |
|
||
| 6c — Migrar Ship | ✅ | `2fe22ff` |
|
||
| 6d — Migrar Enemy | ✅ | `27242f5` |
|
||
| 6e — Migrar Bullet | ✅ | `9993b2d` |
|
||
| **7a — Infra GPU (shaders + wrappers, runtime dormido)** | ✅ | (este commit) |
|
||
| 7b — Swap SDL_Renderer → SDL_GPUDevice (clear/present por GPU) | 🔲 | — |
|
||
| 7c — Pipeline de líneas (quads con thickness configurable) | 🔲 | — |
|
||
| 7d — Refactor de firmas (SDL_Renderer* → FrameContext*) | 🔲 | — |
|
||
| 7e — Cleanup + smoke test final | 🔲 | — |
|
||
| 8 — Postprocesado, color, paleta por tipo | 🔲 | — |
|
||
| 9 — Refactor de GameScene (2.877 LOC → módulos) | 🔲 | — |
|
||
| 10 — Tuning final de masa/restitución/damping | 🔲 | — |
|
||
|
||
## Lo que queda inmediato (Fase 7b)
|
||
|
||
**Swap del runtime de SDL_Renderer a SDL_GPUDevice.**
|
||
|
||
Fase 7a completada: infraestructura GPU dormida (no llamada en runtime aún).
|
||
- `shaders/line.{vert,frag}.glsl` → compilados en build a `build/shaders/*.spv`
|
||
(CMake step con `glslc`). Vertex layout: vec2 position + vec4 color.
|
||
Transformación a NDC vía uniform buffer `LineUniforms{viewport_w, viewport_h}`.
|
||
- `core/rendering/gpu/`:
|
||
- `GpuDevice`: claim del window, loadShader desde .spv.
|
||
- `GpuLinePipeline`: pipeline TRIANGLELIST con vertex layout + alpha blending.
|
||
- `GpuFrameRenderer`: API `beginFrame / pushLine / endFrame`.
|
||
Extrusión perpendicular en CPU por línea (thickness configurable libre).
|
||
Un draw call por frame con un vertex/index buffer transitorio.
|
||
|
||
Backend: Vulkan (Linux/Windows) y Metal (macOS). Sin DirectX.
|
||
|
||
Fase 7b siguiente:
|
||
1. En `SDLManager::iniciar`, sustituir `SDL_CreateRenderer` por
|
||
`Rendering::GPU::GpuFrameRenderer::init(window, 1280, 720)`.
|
||
2. `SDLManager::neteja(r,g,b)` → `gpu_renderer_.beginFrame(r/255, g/255, b/255)`.
|
||
3. `SDLManager::presenta()` → `gpu_renderer_.endFrame()`.
|
||
4. Borrar `renderer_` (SDL_Renderer*) del SDLManager.
|
||
5. Validar arranque con xvfb: pantalla negra (sin líneas todavía), sin crash.
|
||
|
||
Después de 7b el juego se verá NEGRO porque ningún sistema sabe pintar líneas
|
||
todavía. Eso se arregla en 7c (line_renderer apunta al pipeline GPU) y 7d
|
||
(refactor de firmas SDL_Renderer* → FrameContext*).
|
||
|
||
**Fase 10 (tuning)** queda pendiente para después de SDL3 GPU + postpro.
|
||
|
||
## Memoria del proyecto
|
||
|
||
Las preferencias y decisiones persistentes están en:
|
||
`~/.claude/projects/-home-sergio-gitea-orni-attack/memory/MEMORY.md` y los
|
||
archivos enlazados desde ahí. Léelos al empezar sesión nueva.
|
||
|
||
Resumen:
|
||
- **No fallback a SDL_Renderer** en Fase 7 — solo GPU o falla.
|
||
- **Naming**: `.clang-tidy` exige `CamelCase` tipos, `camelBack` métodos,
|
||
`lower_case_` miembros privados, `UPPER_CASE` constantes.
|
||
- **Costuras del título tras 16:9** son tuning artístico, no bug, post-Fase 10.
|
||
- **Push automático** tras cada commit en `rewrite/physics-gpu`.
|
||
- **Smoke test xvfb** (`timeout 5 xvfb-run -a ./build/orni 2>&1 | head -40`)
|
||
tras builds importantes para validar arranque.
|
||
|
||
## Convenciones técnicas
|
||
|
||
- **Lenguaje código**: inglés. **Lenguaje comentarios**: castellano. **Strings
|
||
de UI**: valenciano (preservados).
|
||
- **Coordenadas**: cartesianas (`Vec2`). Polares prohibidas (legacy).
|
||
- **Resolución lógica**: 1280×720. Constantes en `core/defaults.hpp`.
|
||
- **Audio**: import de AEEA. API: `Audio::get()->playSound/playMusic` con
|
||
crossfade y efectos.
|
||
- **Física**: `Physics::RigidBody` en `core/physics/rigid_body.hpp`,
|
||
`Physics::PhysicsWorld` en `core/physics/physics_world.hpp/.cpp`.
|
||
Entity tiene `body_` como member. Flujo tri-fase por frame en
|
||
`GameScene::update`:
|
||
1. `physics_world_.update(dt)` — integrar y resolver
|
||
2. `entity.postUpdate(dt)` — sincronizar mirror (center_, angle_)
|
||
3. Lógica del juego (input, AI, etc.) — aplica fuerzas/cambia velocity
|
||
|
||
## Cómo reanudar tras compactación
|
||
|
||
1. Lee este archivo.
|
||
2. Lee `MEMORY.md` (memorias persistentes).
|
||
3. `git log --oneline -20` para ver últimos commits.
|
||
4. `git status` para ver si hay trabajo en curso sin commitear.
|
||
5. Si la fase actual estaba a medias: continúa donde se quedó (este archivo
|
||
indica en qué fase estamos).
|
||
6. Tras cada commit, push automático: `git push origin rewrite/physics-gpu`.
|
||
|
||
## Configuración física actual (Fase 6e)
|
||
|
||
| Entidad | mass | radius (world) | restitution | linear_damping | angular_damping |
|
||
|---|---|---|---|---|---|
|
||
| Ship | 10.0 | 12 | 0.6 | 1.5 | 0.0 |
|
||
| Enemy Pentagon | 5.0 | 20 | 1.0 | 0.0 | 0.0 |
|
||
| Enemy Quadrat | 8.0 | 20 | 1.0 | 0.0 | 0.0 |
|
||
| Enemy Molinillo | 4.0 | 20 | 1.0 | 0.0 | 0.0 |
|
||
| Bullet | 0.5 | **0** (cinemática) | 0 | 0 | 0 |
|
||
| Wall (PLAYAREA bounds) | ∞ (estático) | — | — | — | — |
|
||
|
||
Nota: Bullet usa `radius=0` en el body físico para que no participe en las
|
||
colisiones del world (ni body-body ni bounds). El radio de gameplay
|
||
(`Defaults::Entities::BULLET_RADIUS = 3`) lo expone vía `getCollisionRadius()`
|
||
y lo consumen `check_collision` y la detección de salida del PLAYAREA en
|
||
`Bullet::update`.
|
||
|
||
Las paredes son implícitas: `physics_world_.setBounds(PLAYAREA)` en
|
||
`GameScene::init`. No son `RigidBody` separados, sino un AABB que rebota.
|