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

19 KiB
Raw Blame History

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"):

[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"):

[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_.webpexplosion_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.pnghero_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/ textureCompressedTexture2D 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) textureCompressedTexture2D 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/ textureSpriteFrames (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/ wavAudioStreamWAV 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/ scenePackedScene (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