--- id: "0072c" title: "gamedev — asset pipeline (atlas packer, MSDF fonts, tilemap, shader translate)" status: pendiente type: feature domain: - gamedev scope: multi-app priority: alta depends: - "0072b" blocks: [] related: [] created: 2026-05-10 updated: 2026-05-17 tags: - gamedev - cpp - assets --- ## Objetivo Pipeline de procesamiento de assets que corre en host (PC) y produce ficheros binarios listos para que el runtime los cargue rapido y pequeño. Sin assets crudos en el bundle final. ## Funciones a crear ### Sprite atlas packer `sprite_atlas_pack_cpp_gfx` (impure): ```cpp struct AtlasInput { std::vector png_paths; int max_size = 2048; int padding = 2; }; struct AtlasOutput { std::vector png_bytes; // atlas final std::vector rects; // {name, x, y, w, h, trim_x, trim_y, src_w, src_h} }; AtlasOutput sprite_atlas_pack(const AtlasInput& in); void atlas_save(const std::string& dir, const std::string& name, const AtlasOutput& a); ``` Algoritmo: MaxRects (skyline). Soporta trim (recorte de pixeles transparentes). Salida: `.png` + `.json` con rects. ### MSDF fonts `ttf_to_msdf_cpp_gfx` (impure). Convierte TTF a Multi-channel Signed Distance Field. Glyphs crisp en cualquier zoom sin antialiasing pesado. Vendoring: `msdf-atlas-gen` (MIT). Wrapper minimo: ```cpp struct MsdfFontOutput { std::vector png_bytes; std::vector glyphs; // {codepoint, plane_bounds, atlas_bounds, advance} float em_size, line_height, ascender, descender; }; MsdfFontOutput ttf_to_msdf(const std::string& ttf_path, const std::vector& charset, int atlas_size = 512); ``` ### Tilemap compile `tilemap_compile_cpp_gfx` (impure). Lee Tiled `.tmx` (XML) o `.tmj` (JSON) y produce binario empaquetado: ``` [header: width, height, tile_size, layer_count, tileset_count] [tilesets: png_path, tile_w, tile_h, columns, tile_count] [layers: name, opacity, data_size, RLE-compressed gids] [objects: id, type, x, y, w, h, props_json] ``` Vendoring: `tinyxml2` o `nlohmann/json` (uno de los dos, ya en stack). ### Shader translate `shader_translate_cpp_gfx` (impure). Cross-compila GLSL → MSL (Metal) / HLSL / WGSL / GLES via SPIRV-Cross + glslang. Plan B si SPIRV-Cross es muy pesado: definir subset GLSL ES 300 que es valido en GL desktop, GLES, WebGL2 (sokol_gfx ya hace de ese subset trabajo). Documentar reglas en `cpp/GAMEDEV.md`. ### Audio encode `audio_encode_cpp_gfx` (impure). Convierte wav → ogg vorbis o opus. Vendoring: `stb_vorbis` (decode), `libogg`+`libvorbis` para encode (opcional, primer paso es solo wav loading). ### Bundle packer `asset_bundle_pack_cpp_gfx` (impure). Toma directorio `assets/` y produce un `.pak` binario con: - Header: magic + version + asset count - TOC: name → offset, size, hash - Concatenacion de ficheros Runtime carga el `.pak` en memoria una vez y lookup por nombre. Para WASM se embebe via `--preload-file` o via `--embed-file` segun tamaño. ## CLI app: `asset_compiler` `cpp/apps/asset_compiler/` — CLI que compone las funciones: ```bash ./asset_compiler atlas --in sprites/ --out build/atlas.pak --max-size 2048 ./asset_compiler font --in fonts/Roboto.ttf --out build/font_roboto.msdf ./asset_compiler tilemap --in levels/level1.tmx --out build/level1.bin ./asset_compiler bundle --in build/ --out game_assets.pak ``` Pipeline completo: `bash/functions/pipelines/build_assets_cpp_gamedev.sh` que llama `asset_compiler` para cada tipo y produce el bundle final. ## Estructura ``` cpp/functions/gfx/ sprite_atlas_pack.{cpp,h,md} ttf_to_msdf.{cpp,h,md} tilemap_compile.{cpp,h,md} shader_translate.{cpp,h,md} audio_encode.{cpp,h,md} asset_bundle_pack.{cpp,h,md} cpp/apps/asset_compiler/ CMakeLists.txt app.md main.cpp data.{cpp,h} # CRUD de bundles ``` ## Vendoring Añadir a `cpp/vendor/`: - `msdf-atlas-gen` (MIT) — fonts - `stb_rect_pack.h` — atlas packing - `tinyxml2.h/.cpp` — Tiled tmx - `SPIRV-Cross` (opcional, decision en sub-issue propio si pesa demasiado) Cada vendor en subdir propio + `LICENSE.txt`. ## Tamaño Asset compiler corre en host, NO afecta runtime size. Pero los formatos elegidos SI: - Atlas PNG → cargar con stb_image (ya en runtime). - MSDF PNG → mismo. - Tilemap binario → loader propio, ~100 LoC. - `.pak` → loader propio, ~150 LoC. Total runtime nuevo: ≤ 50 KB. ## Criterio de exito - [x] `asset_compiler` produce bundle desde una carpeta de prueba. - [x] Runtime (ya en 0072b) carga `.pak` y muestra atlas + texto MSDF + tilemap. - [x] Tiempo de carga `.pak` < 500ms para 10MB de assets en navegador. - [x] Tests por funcion (atlas pack determinista, msdf coverage, tilemap roundtrip). ## No-objetivos - Hot reload de assets (en 0072i editor). - Compresion del bundle (lz4/zstd) — si hace falta despues. - Streaming de assets — overkill por ahora.