Files
fn_registry/dev/issues/0072c-gamedev-asset-pipeline.md
T

4.9 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0072c gamedev — asset pipeline (atlas packer, MSDF fonts, tilemap, shader translate) pendiente feature
gamedev
multi-app alta
0072b
2026-05-10 2026-05-17
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):

struct AtlasInput {
    std::vector<std::string> png_paths;
    int max_size = 2048;
    int padding = 2;
};

struct AtlasOutput {
    std::vector<uint8_t> png_bytes;  // atlas final
    std::vector<SpriteRect> 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: <name>.png + <name>.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:

struct MsdfFontOutput {
    std::vector<uint8_t> png_bytes;
    std::vector<GlyphInfo> 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<uint32_t>& 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:

./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

  • asset_compiler produce bundle desde una carpeta de prueba.
  • Runtime (ya en 0072b) carga .pak y muestra atlas + texto MSDF + tilemap.
  • Tiempo de carga .pak < 500ms para 10MB de assets en navegador.
  • 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.