feat(gamedev): comfyui_build_directional_sprite_workflow — sprite multi-direccional 2.5D (SV3D turntable / Zero123)
Builder puro (dict API format) que a partir del sprite frontal de un personaje construye el workflow ComfyUI de N vistas direccionales consistentes (8-way N/NE/E/SE/S/SW/W/NW o 4-way) rotando la figura en 3D. SV3D (orbit turntable) por defecto, Stable Zero123 (batch por azimuth) como fallback de menor VRAM. Es el puente 2.5D del catalogo gamedev-2d: consistencia rotacional real (el mismo modelo rotado) frente a sprite_sheet (OpenPose 2D re-poza, identidad inconsistente). Helper directional_sprite_view_order(directions) mapea frame i -> direccion i. Funcion pura: solo construye el grafo; coste GPU al enviar con comfyui_submit_workflow. Probado e2e en GPU: goblin enemy_creature_00001_ -> SV3D 8 direcciones elevation 15, 8 frames 576x576 en 75s, pico 7145/8192 MiB (prompt_id 8b9f75de). Consistencia rotacional medida: MAE adyacentes 27 < frente-espalda 29.6, spread de paleta 3.83. Report: reports/0187. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,12 @@ ruido), estos parten de una **imagen de entrada** y la transforman. Cuatro sub-e
|
||||
varios lados; el nodo `ImagePadForOutpaint` extiende el canvas **y genera** la máscara feathered de
|
||||
la franja nueva (no la recibe el caller), y el sampler genera ahí contenido coherente. Cambia el
|
||||
**tamaño** del asset (recortar/extender un fondo o parallax a otra resolución/aspect), no lo de dentro.
|
||||
- **multi-vista 3D / 2.5D** (`directional_sprite`): parte del sprite **frontal** de un personaje y lo
|
||||
**rota en 3D** (SV3D turntable u Stable Zero123 órbita) para producir N vistas direccionales del MISMO
|
||||
personaje (8-way N/NE/E/SE/S/SW/W/NW o 4-way). A diferencia de `sprite_sheet` (re-poza con OpenPose 2D,
|
||||
re-dibuja la silueta → identidad inconsistente entre ángulos), aquí la difusión 3D gira la figura sobre
|
||||
su eje, así casco/arma/paleta son los mismos en cada dirección (**consistencia rotacional**). Cambia el
|
||||
**ángulo de cámara**, no la pose ni el material.
|
||||
|
||||
Cubren el eje que el critic de generación (`reports/0178`) no exploró: derivar de un asset o
|
||||
del dibujo del dev, no inventar un tipo nuevo desde texto.
|
||||
@@ -118,6 +124,7 @@ del dibujo del dev, no inventar un tipo nuevo desde texto.
|
||||
| `comfyui_build_asset_variant_workflow_py_ml` | `(input_image, variant, *, checkpoint="dreamshaper_8…", denoise=0.5, style="game asset", size=512, seed=0, lora=None, …) -> dict` | UNA **variante coherente de un asset 2D ya generado** (img2img): parte del sprite/icono que existe en `input_image` y produce su versión de **otro material/paleta/tier/estado** (`ice element`, `fire element`, `battle-damaged`, `golden tier 2`, `corrupted`) manteniendo **silueta, pose y composición** del original. Compone `comfyui_build_img2img_workflow` (LoadImage → VAEEncode → KSampler con `denoise`) + `comfyui_inject_lora` (estilo opcional) + `ImageScale` opcional (`size` normaliza la base a size×size; `size=None` preserva las dimensiones exactas sin deformar). El prompt es `{variant}, {style}, same composition, same pose, same silhouette, …`. **`denoise` es la palanca**: ~0.3 invisible, **0.45-0.6 recomendado** (cambia material/paleta, conserva forma), ~0.8 deriva la pose y se acerca a txt2img. Set de variantes del MISMO asset = mismo `input_image`/`style`/`seed`, varía solo `variant`. **DISTINTO de los builders txt2img** (`enemy_creature`, `item_icon`…): esos generan un tipo desde cero; éste transforma uno concreto. **NO inyecta Rembg** (img2img preserva el fondo/alpha del original según la base). ⚠️ la imagen base debe existir en `input/` del server (subir con `POST /upload/image`); pura, no valida (usar `comfyui_validate_workflow` antes de enviar); asset NO cuadrado + `size` fijo + `crop="disabled"` deforma → `size=None` o `crop="center"`. Probado e2e en GPU con SD1.5 — variante `ice element, frozen` del goblin `enemy_creature_00001_.png` denoise 0.5 seed 7 512×512 (`prompt_id 5e4a5d3d`): silueta conservada (luminance corr 0.63) + paleta a frío (blueness B−R −1.6→+1.9), `reports/0181`. SD1.5. |
|
||||
| `comfyui_build_sprite_from_sketch_workflow_py_ml` | `(sketch_image, subject, *, control_type="lineart", checkpoint="dreamshaper_8…", style="game asset, clean, centered", strength=0.8, size=512, seed=0, lora=None, preprocess=True, controlnet_name=None, …) -> dict` | UN **sprite pintado a partir del BOCETO del dev**, guiado por **ControlNet** (sub-eje sketch→ControlNet, **NO img2img**). Recibe el dibujo tosco que existe en `sketch_image` (boceto/lineart/garabato) + `subject` (qué es), y genera un sprite en estilo de juego que **conserva la forma dibujada**: el dev marca la silueta, la IA pone material/color/acabado. Mecanismo: `txt2img` base (ruido, `EmptyLatentImage`, `denoise 1.0`) cuyo positivo pasa por `ControlNetApply` atado al mapa de líneas del boceto. `control_type` elige el **preprocesador** (`LineArtPreprocessor` / `ScribblePreprocessor` / `CannyEdgePreprocessor`, interpuesto entre el boceto y el ControlNet por un helper) y, por defecto, el **modelo CN emparejado**. Compone `comfyui_build_txt2img_workflow` + `comfyui_inject_controlnet` + `comfyui_inject_lora` (estilo opcional). **`strength` es la palanca**: 0 = ignora el dibujo (txt2img puro), ~0.8 recomendado (respeta forma dejando limpiar a la IA), 1.0 = se ciñe estricto. **DISTINTO de `asset_variant`** (img2img conserva forma+color de una imagen ya pintada) y de los txt2img (`enemy_creature`…, inventan la forma desde texto): éste conserva **solo la forma** del dibujo. ⚠️ el boceto debe existir en `input/` (subir con `POST /upload/image`); pura, no valida (usar `comfyui_validate_workflow` antes de enviar); `preprocess=False` solo si el sketch ya es un lineart limpio. **GOTCHA del server 8GB: solo `canny`/`depth`/`openpose` SD1.5 instalados** — para `lineart`/`scribble` pasa `controlnet_name="control_v11p_sd15_canny_fp16.safetensors"` u usa `control_type="canny"` (out-of-the-box); pendiente humano descargar `control_v11p_sd15_lineart_fp16`/`scribble`. Probado e2e en GPU con SD1.5 — boceto del goblin `enemy_creature_00001_.png` → `CannyEdgePreprocessor` → ControlNet canny, `subject="dark fantasy goblin warrior"` strength 0.85 seed 123 512×512 (`prompt_id ea6fc372`): pose/orejas/hombrera/lanza dentada/espada del dibujo conservadas, repintado en estilo de juego, `reports/0182`. SD1.5. |
|
||||
| `comfyui_build_inpaint_asset_workflow_py_ml` | `(input_image, mask_image, prompt, *, checkpoint="dreamshaper_8…", denoise=1.0, style="game asset", grow_mask=6, size=None, seed=0, lora=None, mode="vae_encode", …) -> dict` | EDITA **solo una región** de un asset 2D ya pintado (**inpaint**, sub-eje propio). Recibe el asset en `input_image` + una **máscara** `mask_image` (BLANCO = editar, NEGRO = conservar) + `prompt` de qué poner ahí, y repinta **únicamente** la zona enmascarada dejando el resto del sprite intacto (cambiar/añadir un arma, quitar un casco, poner un escudo, reparar una zona dañada). Mecanismo (`mode="vae_encode"`): `VAEEncodeForInpaint` codifica el latente respetando la máscara y dilata su borde `grow_mask` px para difuminar la costura; `KSampler` (`denoise` alto) regenera solo esa región con `{prompt}, {style}, seamless blend…`. Compone `comfyui_build_inpaint_workflow` (base) + `comfyui_inject_lora` (estilo opcional); `size` escala imagen **Y** máscara de forma consistente (escalar solo una las desalinea). **`grow_mask` es la palanca de costura** (6-10 px difumina el borde lo/nuevo); `denoise` 1.0 reescribe entero, ~0.5-0.7 repara suave. **DISTINTO de `asset_variant`** (img2img reescribe TODO el asset) y de `sprite_from_sketch` (ControlNet parte de un dibujo de líneas para un sprite nuevo): éste edita **un trozo** delimitado por la máscara. **ERROR-PATH**: si el server no expone `VAEEncodeForInpaint`, pasar `mode="noise_mask"` → degrada a `VAEEncode` + `SetLatentNoiseMask` (+ `GrowMask`); `mask_image` vacío lanza `ValueError`. ⚠️ asset y máscara deben existir en `input/` (subir con `POST /upload/image`) y compartir resolución (o usar `size`); `ImageScale` aquí NO ofrece `lanczos` (válidos `bilinear`/`nearest-exact`/`area`/`bicubic`); pura, no valida. Probado e2e en GPU con SD1.5 — máscara circular (R70) sobre la mano del goblin `enemy_creature_00001_.png`, `prompt="a glowing blue magic orb"` grow_mask 8 denoise 1.0 seed 7 (`prompt_id 88b52c66`): orbe azul en la región, **resto idéntico** (diff medio dentro 40.3 vs fuera 1.97 → ratio 20.4×; 44.6% px cambiados dentro vs 1.7% fuera), `reports/0183`. SD1.5. |
|
||||
| `comfyui_build_directional_sprite_workflow_py_ml` | `(input_image, *, directions=8, model="sv3d", elevation=0.0, size=None, orbit_frames=None, seed=0, ckpt=None, …) -> dict` | UN **sprite MULTI-DIRECCIONAL** del MISMO personaje rotado en 3D (**multi-vista 2.5D**, sub-eje propio): parte de la imagen **frontal** del personaje (fondo limpio, en `input/`) y construye el workflow que genera N vistas direccionales CONSISTENTES (8-way N/NE/E/SE/S/SW/W/NW o 4-way) para top-down/iso/shooter 8-way. `model="sv3d"` (default) = `SV3D_Conditioning` produce un **orbit turntable** de N frames equiespaciados en 360° en una pasada (mejor consistencia, `sv3d_p.safetensors`, nativo 576²); `model="zero123"` = `StableZero123_Conditioning_Batched` da un **batch** de N vistas por azimuth (fallback menor VRAM, `stable_zero123.ckpt`, nativo 256²). `elevation` (~15-30) da picado para cámara cenital; `orbit_frames` (SOLO sv3d) densifica el orbit (21 nativo) para submuestrear; el módulo expone `directional_sprite_view_order(directions)` (frame i = dirección i). **DISTINTO de `sprite_sheet`** (OpenPose 2D re-poza la silueta → identidad inconsistente): aquí la difusión 3D ROTA la figura sobre su eje → casco/arma/paleta idénticos en cada dirección (rotación 3D real, no re-dibujo). Construye, NO genera (el coste GPU es el `submit`); **pura, no valida** (la imagen frontal debe existir en `input/`). Hermana **pura** de `comfyui_generate_views_from_image` (orquestador impuro para recon 3D, 4 cardinales). ⚠️ VRAM RTX 3070 8GB: SV3D es modelo de vídeo, pesa — 8 frames@576² → pico **7145 MiB**; limpiar GPU antes (`POST /free`); OOM → baja `size`/`directions` o cae a zero123, NO matar procesos; `comfyui_wait_result` lanza `TimeoutError` pero el job completa (sondear `/history`). Probado e2e en GPU con SV3D — goblin `enemy_creature_00001_.png` (compuesto sobre blanco 576²) → 8 direcciones elevation 15 seed 7, **8 frames** 576² en 75 s, consistencia rotacional medida (MAE adyacentes 27 < frente↔espalda 29.6, spread de paleta 3.83 = mismo personaje en las 8 vistas; `prompt_id 8b9f75de`, `reports/0187`). SV3D/Zero123. |
|
||||
| `comfyui_build_outpaint_asset_workflow_py_ml` | `(input_image, prompt, *, left=0, right=0, top=0, bottom=0, feather=40, checkpoint="dreamshaper_8…", denoise=1.0, style="game background", grow_mask=0, seed=0, lora=None, …) -> dict` | EXTIENDE **el lienzo** de un asset 2D ya pintado (**outpaint**, sub-eje propio). Recibe el asset en `input_image` + cuánto extender por cada lado (`left`/`right`/`top`/`bottom` px) + `prompt` de qué generar fuera de los bordes, y **agranda el canvas** generando contenido coherente con el original más allá de sus bordes (recortar/extender un fondo, parallax, card_art o splash a otra resolución/aspect ratio). Mecanismo: el nodo nativo `ImagePadForOutpaint` amplía el lienzo y **EMITE** a la vez la imagen extendida **y** la máscara feathered de la franja nueva (la genera el grafo, **NO** la recibe el caller); `VAEEncodeForInpaint` codifica respetando esa máscara y `KSampler` (`denoise` alto) genera lo nuevo con `{prompt}, {style}, seamless extension…`. Compone `comfyui_build_inpaint_workflow` (base; su `LoadImageMask` se elimina y `VAEEncodeForInpaint` se reconecta a las dos salidas del pad) + `comfyui_inject_lora` (estilo opcional). **`feather` difumina la costura** (40 px por defecto, no debe pasarse de la extensión); `grow_mask` (0 por defecto) dilata adicionalmente el borde si aparece costura dura. **DISTINTO de `inpaint_asset`**: éste **no recibe máscara** (la genera el pad) y cambia el **tamaño** del asset extendiendo hacia fuera, mientras inpaint edita una región **interior** con máscara externa del mismo tamaño. **ERROR-PATH**: `input_image`/`prompt` vacíos o las cuatro extensiones en 0 tras redondear (`left=3`→0) lanzan `ValueError`; si el server no expone `ImagePadForOutpaint`, consultar `/object_info`. ⚠️ el asset debe existir en `input/` (subir con `POST /upload/image`); las extensiones se redondean a múltiplo de 8 (`250→248`); pura, no valida. Probado e2e en GPU con SD1.5 — fondo `seamless_00004_.png` 512×512 extendido `right=256` feather 40 denoise 1.0 seed 7 (`prompt_id aa33de05`): canvas **512→768×512** (+256), original conservado (diff medio 7.2 lejos del borde) + franja nueva con contenido coherente (std 28.9, dist de paleta 28.6), `reports/0185`. SD1.5. |
|
||||
|
||||
## Funciones de post-proceso y puente (`gamedev-2d`, CPU)
|
||||
@@ -201,9 +208,12 @@ comfyui_export_asset_to_godot(rgba["out_path"], "vfx", PROJ)
|
||||
un pack entero compartiendo checkpoint/style/lora/seed (issue 0087, ver tabla de
|
||||
pipelines arriba). One-shots por-asset individuales (pixelart/sprite/VFX) siguen
|
||||
encadenándose a mano; candidatos a promoción cuando el patrón se repita.
|
||||
- **Sprite turnaround multi-vista** (orquestar N poses con identidad fija + juez):
|
||||
el builder `comfyui_build_sprite_sheet_workflow` produce UN frame; la orquestación
|
||||
multi-pose es pipeline pendiente (plan `reports/0137` T2).
|
||||
- **Sprite turnaround multi-vista** (N direcciones del mismo personaje con identidad fija):
|
||||
cubierto por `comfyui_build_directional_sprite_workflow` (rotación 3D SV3D/Zero123,
|
||||
consistencia rotacional medida — `reports/0187`). Lo que sigue **pendiente** es la
|
||||
orquestación multi-POSE 2D con juez (re-pozar un personaje en N acciones manteniendo
|
||||
identidad, distinto de rotarlo): `comfyui_build_sprite_sheet_workflow` produce UN frame;
|
||||
el pipeline multi-pose con juez sigue pendiente (plan `reports/0137` T2).
|
||||
- **Paletas lospec por red** (`load_lospec_palette`): no incluido. `pixelize` usa
|
||||
paletas fijas embebidas (game-boy/pico-8/nes) o lista de hex, sin HTTP.
|
||||
- **TileSet / SpriteFrames `.tres`**: Godot no los deriva solos; `export_asset_to_godot`
|
||||
|
||||
Reference in New Issue
Block a user