# 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// # 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 `_NNNNN_.` (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 `` + `.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-.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 (`/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 `_NNNNN_.` (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 --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).