Files
fn_registry/docs/comfyui-godot-integration.md
T
egutierrez 9508fff282 docs: añade diseño de integración ComfyUI → Godot (puente de assets)
Mapa de ambas estructuras (output/ de ComfyUI + res://assets/ de Godot 4),
tabla tipo-de-asset → carpeta destino → import settings clave, y propuesta
de pipeline export_asset_to_godot que compone helpers atómicos + reimport
headless (gap confirmado: 0 funciones godot en el registry).

Documenta el gotcha de Godot 4: el filtro Nearest del pixelart se setea
global (default_texture_filter=0) o por override, no por .import por defecto.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:25:29 +02:00

312 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).