# Integración ComfyUI → Godot: puente de assets ordenado y gestionado

Diseño del puente entre la generación de assets en **ComfyUI** (`~/ComfyUI/`) y su consumo en
proyectos **Godot 4** (`~/gamedev/projects/`). El objetivo es que un asset generado (sprite,
tileset, pixelart, spritesheet VFX, audio, malla 3D GLB) viaje a un proyecto Godot a la carpeta
correcta, con los *import settings* adecuados a su tipo, sin romper los archivos `.import`
existentes ni desordenar el proyecto.

- **Fecha:** 26/06/2026
- **Alcance:** mapa de ambas estructuras + convención de carpetas destino + tabla
  tipo-de-asset → carpeta Godot → import settings + propuesta de función(es) del registry para
  automatizar el traslado. Es documento de diseño; la implementación se delega a `fn-constructor`.
- **Fuera de alcance:** generación (GPU, otro agente), implementación de las funciones, descarga
  de modelos.

Documento hermano del catálogo de capacidades de generación: `~/ComfyUI/CAPABILITIES.md` (fuera
del repo) y `docs/capabilities/comfyui-overview.md` (versionable). Este documento añade la pata
que faltaba: **qué pasa con el asset una vez generado**.

---

## 1. Mapa de ComfyUI (origen de los assets)

ComfyUI vive en `~/ComfyUI/` (clon del repo, no versionado en `fn_registry`). Las carpetas
relevantes para el puente son:

```
~/ComfyUI/
├── output/                         # ★ AQUÍ caen TODOS los assets generados
│   ├── *.png                       #   imágenes (sprites, pixelart, tiles)  — ~150 hoy
│   ├── *.webp                      #   spritesheets animados / vídeo corto (SaveAnimatedWEBP)
│   ├── *.mp4                       #   vídeo (SaveVideo)
│   ├── *.glb / *.obj / *.ply       #   mallas 3D (SaveGLB)
│   └── 3D/                         #   subcarpeta de salidas 3D (texturizadas, multi-vista)
├── input/                          # imágenes de entrada (img2img, image-to-3d)
├── models/ + /mnt/2tb/comfyui_models/   # checkpoints/loras/vae/... (vía extra_model_paths.yaml)
├── user/default/workflows/         # grafos UI, agrupados por capacidad (01_txt2img, 02_img2img, …)
├── skills_library/<slug>/          # recetas de estilo reproducibles (recipe.json + samples)
└── custom_nodes/                   # nodos extra (PixelArt-Detector, IPAdapter, Hunyuan3D, …)
```

- **`output/` es el único punto de origen del puente.** Cada nodo `Save*` escribe ahí con un
  patrón de nombre `<prefijo>_NNNNN_.<ext>` (sufijo numérico de 5 dígitos). Ejemplos reales en
  disco hoy: `bench160_3000_00001_.png`, `svd_motion_hi_00001_.webp`, `3d_robot_mesh_00001_.glb`,
  `output/3D/character_clean_textured_00001_.glb`.
- **Modelos centralizados** en `/mnt/2tb/comfyui_models/` vía `extra_model_paths.yaml`
  (`is_default: true`). No intervienen en el puente (son insumo de generación, no asset de salida).
- **Tipos que generamos hoy** (recuento real del `output/`): `png` (mayoría), `glb` (15), `mp4`
  (6), `webp` (2), `obj`/`ply` (formatos 3D crudos). **Audio aún no se genera en ComfyUI** (no hay
  `wav`/`ogg`/`mp3` en `output/`); el plan de audio existe como report aparte. El puente lo
  contempla igualmente porque Godot lo consume y porque el audio puede llegar de otra fuente.
- Catálogo navegable de qué sabemos generar y con qué función/grafo: `~/ComfyUI/CAPABILITIES.md`.

---

## 2. Mapa de Godot (destino de los assets)

Los proyectos viven en `~/gamedev/projects/` (fuera de `fn_registry`, igual que ComfyUI). Hay una
**biblioteca maestra** de assets en `~/gamedev/assets/` y, por proyecto, una copia local de los
assets que usa. Proyectos reales localizados:

- `~/gamedev/projects/crossy_road/` (juego "LizardRoad", Godot 4.7, móvil portrait 640×1280)
- `~/gamedev/projects/risk/` (pilotaje previo)

Estructura canónica de un proyecto Godot 4 (tomada de `crossy_road`, que es el patrón real):

```
~/gamedev/projects/crossy_road/
├── project.godot                   # config del proyecto (nombre, autoloads, rendering, display)
├── .godot/                         # ★ caché de import (regenerable) — NUNCA se versiona ni se toca a mano
│   └── imported/                   #   binarios .ctex/.sample/... generados desde los .import
├── assets/                         # assets del proyecto (copia local de la biblioteca)
│   ├── biomas/        agua.png + agua.png.import (par obligatorio por cada asset)
│   ├── kenney/        packs CC0
│   ├── external/      otros CC0
│   └── audio/sfx/     step.wav + step.wav.import
├── scenes/            *.tscn  (escenas)
├── scripts/           *.gd + *.gd.uid
├── addons/godot_ai/   addon del MCP (control del editor desde Claude)
└── export_presets.cfg / android/  (build móvil)
```

### Cómo importa Godot 4 cada asset — el archivo `.import`

**Regla de oro:** en Godot, **cada asset es un par `<archivo>` + `<archivo>.import`**. El
`.import` es un INI que declara el `importer`, el `type` de recurso resultante, un `uid://`
estable y los parámetros de importación. Godot genera el binario importado en `.godot/imported/`
y lo regenera al reimportar. **Romper o desincronizar el `.import` = el asset no carga o se
reimporta con settings por defecto.** Por eso el puente debe respetar este par.

Ejemplos reales de `.import` por tipo (de `crossy_road`):

**Textura** (`importer="texture"`, `type="CompressedTexture2D"`):
```ini
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cedkstexk3ciw"
path="res://.godot/imported/agua.png-<hash>.ctex"
[params]
compress/mode=0          ; 0=Lossless (correcto para pixelart), 2=VRAM
mipmaps/generate=false   ; off para 2D/pixelart
process/fix_alpha_border=true
detect_3d/compress_to=1
```

**Audio WAV** (`importer="wav"`, `type="AudioStreamWAV"`):
```ini
[remap]
importer="wav"
type="AudioStreamWAV"
[params]
edit/loop_mode=0         ; 0=Disabled (sfx), 1=Forward (música en bucle)
compress/mode=2
force/mono=false
```

**Malla 3D GLB** (`importer="scene"`, produce `PackedScene`): el GLB se importa como escena glTF;
sus opciones (escala raíz, generación de colisión, manejo de materiales/texturas) viven en el
`.import` de tipo `scene`. No había ninguno en los proyectos actuales (son 2D), pero es el
importer canónico de Godot para `.glb`/`.gltf`.

### El gotcha del filtro de textura (Godot 4 ≠ Godot 3)

En **Godot 4** el filtro Nearest/Linear **no es un campo del `.import` por defecto** (en Godot 3
sí lo era). El filtro se controla de dos formas:

1. **Global del proyecto (recomendado, KISS):** Project Settings → Rendering → Textures →
   Canvas Textures → Default Texture Filter → **Nearest**. En `project.godot` es la clave
   `rendering/textures/canvas_textures/default_texture_filter=0` (0 = Nearest). Tras cambiarlo hay
   que **reimportar** las texturas.
2. **Override por asset:** en el panel Import de una textura concreta, Texture > Filter →
   `Nearest` (override del default). Esto sí escribe la opción en su `.import`.

> **Hallazgo (read-only) en `crossy_road`:** su `project.godot` **no** declara
> `default_texture_filter`, así que usa el default **Linear** → cualquier asset pixelart se ve
> **borroso** (sus `biomas/*.png` tienen `mipmaps/generate=false` y `compress/mode=0`, correcto,
> pero les falta el Nearest). Para un proyecto pixelart, setear el global una vez es el primer
> paso del puente. (No se modificó nada; queda anotado para el constructor.)

---

## 3. El puente: convención de carpetas destino en Godot

Convención propuesta para la copia local de assets dentro de cada proyecto, alineada con lo que
ya existe (`assets/audio/sfx/`, `assets/kenney/`) y extendida a todos los tipos que generamos:

```
res://assets/
├── sprites/        # PNG individuales: personajes, props, objetos sueltos
├── tilesets/       # PNG de tiles + el recurso TileSet .tres derivado
├── vfx/            # spritesheets / animaciones (WEBP, o PNG en grid) → SpriteFrames/AtlasTexture
├── audio/
│   ├── sfx/        # efectos cortos (WAV) — loop OFF
│   └── music/      # música (OGG/WAV) — loop ON
├── models/         # mallas 3D GLB (+ sus texturas/materiales)
└── _generated/     # opcional: zona de aterrizaje de lo recién traído de ComfyUI, antes de clasificar
```

- **`pixelart` no es una carpeta**, es un *atributo transversal*: un sprite, tile o VFX puede ser
  pixelart. Lo que cambia es el import setting (Nearest + Lossless), no la ubicación. El pixelart
  va a `sprites/` / `tilesets/` / `vfx/` según su rol, y el proyecto entero se marca Nearest a
  nivel global cuando es un juego pixelart.
- **Biblioteca maestra vs copia local:** `~/gamedev/assets/` es la fuente ordenada; cada proyecto
  guarda dentro su copia (`<proyecto>/assets/...`) porque Godot referencia por rutas `res://`
  relativas a la raíz del proyecto. El puente copia de `~/ComfyUI/output/` → bien a la biblioteca
  maestra, bien directo a un proyecto.

### Naming y versionado

- ComfyUI nombra `<prefijo>_NNNNN_.<ext>` (p. ej. `svd_motion_hi_00001_.webp`). Al exportar,
  **renombrar a snake_case limpio y semántico** quitando el sufijo `_NNNNN_` y los guiones bajos
  de cola: `svd_motion_hi_00001_.webp` → `explosion_loop.webp`.
- Sin espacios ni mayúsculas en nombres de archivo (consistencia con `res://` y multiplataforma).
- Versionado: sufijo opcional `_vN` cuando se itera un asset que ya está en uso
  (`hero_idle.png` → `hero_idle_v2.png`), para no pisar el `uid://` del que ya referencian escenas.
  **Nunca** sobrescribir un asset en uso sin querer: cambia su contenido pero conserva su `uid`,
  lo que puede ser deseable (hot-swap) o no (regresión visual). Decisión explícita por asset.

---

## 4. Tabla: tipo de asset ComfyUI → carpeta Godot → import settings clave

| Tipo (ComfyUI) | Ext salida | Carpeta Godot destino | Importer Godot (`type`) | Import settings clave |
|---|---|---|---|---|
| **Sprite individual** | `.png` | `res://assets/sprites/` | `texture` → `CompressedTexture2D` | `compress/mode=0` (Lossless); `mipmaps/generate=false`; repeat off; filtro = default del proyecto (Linear si arte vectorial, Nearest si pixelart) |
| **Pixelart** (sprite/tile) | `.png` | `sprites/` o `tilesets/` (según rol) | `texture` → `CompressedTexture2D` | **CRÍTICO: Nearest** (global `default_texture_filter=0` o override por asset); `compress/mode=0` Lossless; `mipmaps/generate=false`; repeat off |
| **Tileset** | `.png` | `res://assets/tilesets/` | `texture` + recurso **`TileSet` (.tres)** manual/script | Nearest (suele ser pixelart); además crear `TileSet` con tamaño de celda (region grid) — Godot **no** deriva el TileSet automáticamente del PNG |
| **VFX spritesheet** | `.webp` / `.png` (grid) | `res://assets/vfx/` | `texture` → **`SpriteFrames`** (AnimatedSprite2D) o **`AtlasTexture`** por frame | Nearest si pixelart; definir `hframes`/`vframes` o regiones por frame; Godot **no** convierte un sheet a `SpriteFrames` solo: se hace en editor o por script de import |
| **Audio SFX** | `.wav` | `res://assets/audio/sfx/` | `wav` → `AudioStreamWAV` | `edit/loop_mode=0` (Disabled); `compress/mode` según peso |
| **Audio música** | `.ogg` / `.wav` | `res://assets/audio/music/` | `oggvorbisstr` (OGG) / `wav` | **loop ON** (`edit/loop_mode=1` en WAV; `loop=true` en OGG) |
| **Malla 3D** | `.glb` (preferido) | `res://assets/models/` | `scene` → `PackedScene` (glTF) | escala raíz coherente; generar colisión si se necesita; si la textura es pixelart, poner el material en Nearest; preferir `.glb` sobre `.obj`/`.ply` (los lleva embebidos) |

Notas que la tabla condensa:

- **Godot no automatiza dos conversiones clave:** (1) PNG de tiles → recurso `TileSet`, y (2)
  spritesheet → `SpriteFrames`. Ambas requieren un paso manual en editor o un script de import
  (o un addon, p. ej. el TexturePacker importer / aseprite-importers). El puente puede generar el
  `.tres` por script a partir de la geometría conocida del grid.
- **WEBP animado (SVD):** nuestros `svd_*.webp` son clips de Stable Video Diffusion. Para usarlos
  como animación 2D en Godot, lo robusto es **descomponer en frames** (PNG en grid) y construir
  `SpriteFrames`, no cargar el WEBP animado tal cual.
- **3D pixelart/low-poly:** el GLB importa como escena; cuidar que el material no aplique filtro
  Linear a una textura pixelart (se setea en el material/import de la malla).

---

## 5. Propuesta de función(es) del registry (diseño, NO implementación)

Búsqueda en el registry: `mcp__registry__fn_search query="godot export asset import"` → **0
resultados**. Gap limpio. Hoy llevar un asset de ComfyUI a Godot es manual (copiar + abrir editor
+ tocar import a mano). Alineado con la doctrina del registry (registry-first + crecer por
composición de helpers atómicos, issue 0087), la propuesta es **un pipeline one-shot que compone
helpers pequeños**:

### Pipeline principal

```
export_asset_to_godot_py_pipelines  (impura, kind=pipeline, tag de grupo: godot, comfyui)

Firma:
  export_asset_to_godot(
      asset_path: str,          # ruta en ~/ComfyUI/output/ (o cualquier archivo)
      kind: str,                # "sprite" | "pixelart" | "tileset" | "vfx" | "sfx" | "music" | "model"
      godot_project: str,       # ruta raíz del proyecto Godot destino
      dest_name: str = "",      # nombre limpio destino (default: snake_case del origen sin _NNNNN_)
      pixelart: bool = False,   # fuerza Nearest + Lossless en la textura
      loop: bool = False,       # audio en bucle (música)
      reimport: bool = True,    # lanza reimport headless al final
  ) -> dict                     # {dest_res_path, import_written, reimported, warnings[]}
```

Comportamiento:
1. Resolver la carpeta destino por `kind` (tabla §4) dentro de `res://assets/`.
2. Copiar el archivo con nombre limpio (snake_case, sin sufijo `_NNNNN_`).
3. Escribir/asegurar el `.import` adecuado al tipo (texture/wav/scene) con los settings clave.
4. Si `pixelart=True`, además asegurar el global del proyecto
   (`default_texture_filter=0` en `project.godot`) o el override por asset.
5. Si `reimport=True`, lanzar reimport headless para que Godot regenere `.godot/imported/`.
6. Devolver el `res://` final + avisos (p. ej. "tileset copiado pero falta crear el TileSet .tres",
   "WEBP animado: descomponer en frames antes de SpriteFrames").

### Helpers atómicos que compone (delegar a `fn-constructor` en paralelo)

| Helper (id tentativo) | Pureza | Qué hace |
|---|---|---|
| `godot_asset_dest_dir_py_core` | pura | mapea `kind` → subdir de `res://assets/` (tabla §4) |
| `godot_clean_asset_name_py_core` | pura | quita sufijo `_NNNNN_`, normaliza a snake_case, sin espacios |
| `godot_write_texture_import_py_infra` | impura | escribe `.import` de textura (compress/mipmaps/filter) preservando `uid` si ya existe |
| `godot_write_audio_import_py_infra` | impura | escribe `.import` de audio (loop_mode) |
| `godot_ensure_pixelart_project_py_infra` | impura | setea `default_texture_filter=0` en `project.godot` (idempotente) |
| `godot_reimport_headless_bash_infra` | impura | `godot --headless --path <proj> --import` para regenerar la caché |
| `godot_build_spriteframes_tres_py_infra` | impura | genera `.tres` de `SpriteFrames`/`AtlasTexture` a partir de un sheet + geometría de grid |
| `godot_build_tileset_tres_py_infra` | impura | genera `.tres` de `TileSet` a partir de un PNG + tamaño de celda |

### DoD esbozado (según `dod_quality.md`)

- **Golden:** `export_asset_to_godot("~/ComfyUI/output/hero.png", "pixelart",
  "~/gamedev/projects/crossy_road")` deja `res://assets/sprites/hero.png` + su `.import` con
  Nearest + Lossless, el `project.godot` con `default_texture_filter=0`, y la reimport headless
  sale con exit 0; assert: el `.import` contiene el override Nearest y el binario aparece en
  `.godot/imported/`.
- **Edge 1 (audio música):** `kind="music", loop=True` → `.import` con `edit/loop_mode=1`.
- **Edge 2 (GLB 3D):** `kind="model"` copia a `res://assets/models/` y la escena glTF carga
  (reimport sin error).
- **Edge 3 (tileset):** copia el PNG y **avisa** que falta el `.tres` del TileSet (o lo genera con
  `godot_build_tileset_tres`), sin romper nada.
- **Error path:** `kind` desconocido → error claro sin copiar nada; `godot_project` sin
  `project.godot` → aborta y no escribe; nunca sobrescribe un asset en uso sin `dest_name`
  explícito.
- **Idempotencia:** re-exportar el mismo asset preserva el `uid://` existente (no rompe las
  referencias de las escenas que ya lo usan).

### ¿Automatizar el import vía MCP godot-ai?

El addon `godot_ai` (MCP, server `127.0.0.1:8000/mcp`) está presente en `crossy_road` y `risk`.
Con el editor abierto, `filesystem_manage` op `reimport` (recibe `paths`) puede forzar reimport
desde el editor vivo. **Pero** la convención (`CONVENTIONS.md` de gamedev) ya observa que el
reimport por MCP suele ser **innecesario**: `project_run` recompila desde disco al arrancar, y un
`godot --headless --import` regenera la caché sin editor abierto. **Recomendación:** el puente usa
**reimport headless por CLI** (`godot_reimport_headless_bash_infra`) como mecanismo por defecto
(no requiere editor abierto ni MCP), y deja el MCP como opción cuando el editor ya está vivo y se
quiere refrescar en caliente.

---

## 6. Resumen operativo (TL;DR)

1. **Origen:** todo asset generado cae en `~/ComfyUI/output/` (PNG/WEBP/MP4/GLB).
2. **Destino:** `res://assets/{sprites,tilesets,vfx,audio/{sfx,music},models}/` dentro del
   proyecto Godot; cada asset es un par `archivo` + `archivo.import`.
3. **Import por tipo:** textura (Lossless, mipmaps off, **Nearest si pixelart**), audio (loop
   off=sfx / on=música), GLB (escena glTF). Tabla §4.
4. **Gotcha Godot 4:** el Nearest pixelart se setea **global** (`default_texture_filter=0`) o por
   override de asset — **no** es un flag por defecto del `.import`. `crossy_road` hoy está en
   Linear (pixelart borroso): anotado.
5. **Godot no automatiza** PNG→TileSet ni sheet→SpriteFrames: paso manual o script de import.
6. **Automatización:** pipeline `export_asset_to_godot` (gap confirmado, 0 funciones hoy) que
   compone helpers atómicos + reimport headless. Diseño en §5; implementación se delega a
   `fn-constructor`.

## Fuentes

- ComfyUI: `~/ComfyUI/CAPABILITIES.md`, `~/ComfyUI/extra_model_paths.yaml`, listado de
  `~/ComfyUI/output/` (read-only).
- Godot: `~/gamedev/CONVENTIONS.md`, `~/gamedev/README.md`,
  `~/gamedev/projects/crossy_road/project.godot` y sus `*.import` (read-only).
- Godot 4 pixel art texture filter: [GDQuest — Setting up pixel art graphics in Godot 4](https://www.gdquest.com/library/pixel_art_setup_godot4/),
  [Godot Forum — How to import pixel art in Godot 4](https://forum.godotengine.org/t/how-to-import-pixel-art-in-godot-4/7105).
- Godot 4 sprite sheets: [Godot docs — 2D sprite animation](https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html),
  [godot-4-aseprite-importers](https://github.com/nklbdev/godot-4-aseprite-importers).
