# 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) | ✅ | `ba6fd00` | | **7b+c — Swap atómico a SDL3 GPU + line_renderer al pipeline** | ✅ | `fa7da4c` | | 7d — Validación visual del usuario en hardware real | ⏳ pendiente | — | | 8 — Postprocesado, color, paleta por tipo | 🔲 | — | | 9 — Refactor de GameScene (2.877 LOC → módulos) | 🔲 | — | | 10 — Tuning final de masa/restitución/damping | 🔲 | — | | 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 7d — validación del usuario) **El proyecto ya no contiene `SDL_Renderer` ni una sola línea.** Todo el rendering pasa por SDL3 GPU. Estado tras `fa7da4c`: - Backend: Vulkan en Linux/Windows, Metal pendiente en macOS (shaders MSL todavía no se generan en build, solo SPIR-V). - `SDLManager` posee un `Rendering::Renderer` (alias de `GpuFrameRenderer`). `clear()` → `beginFrame()`, `present()` → `endFrame()`. Letterbox vía `setViewport` mid-frame. VSync vía `SDL_SetGPUSwapchainParameters`. - `linea(renderer, x1,y1,x2,y2, brightness, thickness)`: la implementación empuja la línea como quad extrudido (`pushLine`) al batch del frame. El grosor es configurable por línea o global (default 1.5 px). - Todo el resto del juego (entities, effects, scenes, title, vector_text, starfield, shape_renderer) pasa `Rendering::Renderer*` opaco. Solo `line_renderer.cpp` toca métodos del backend. Smoke test xvfb: ✅ compila, arranca con `Backend GPU: vulkan`, carga shapes y termina limpio. La pantalla del xvfb sale negra porque el host no tiene aceleración 3D (VMware), pero eso no es un bug del juego — es xvfb que no muestra el swapchain Vulkan. **Acción pendiente del usuario**: probar el binario en hardware real con Vulkan nativo para validar que las líneas se dibujan correctamente. Si hay problemas visuales, los ajustes que más probablemente harán falta son: 1. Convención de orientación de los triángulos (FRONTFACE_COUNTER_CLOCKWISE está en el pipeline; si se ven invertidos, cambiar a CLOCKWISE). 2. Si las líneas se ven demasiado finas o gruesas: `setLineThickness(N)` global o pasar `thickness` por llamada. 3. Si hay flicker o screen tearing: revisar `Options::rendering.vsync`. Tras la validación visual, la migración técnica de SDL3 GPU está cerrada y podemos pasar a **Fase 8** (postprocesado / paleta de colores por tipo de entidad) o **Fase 9** (refactor de GameScene). ## 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.