---
name: comfyui_build_directional_sprite_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_directional_sprite_workflow(input_image: str, *, directions: int = 8, model: str = \"sv3d\", elevation: float = 0.0, size: int | None = None, orbit_frames: int | None = None, seed: int = 0, ckpt: str | None = None, steps: int = 20, cfg: float | None = None, min_cfg: float = 1.0, sampler_name: str = \"euler\", scheduler: str = \"karras\", filename_prefix: str = \"directional_sprite\") -> dict"
description: "Construye el dict (API format) del workflow ComfyUI de un sprite MULTI-DIRECCIONAL: a partir de UNA imagen frontal de un personaje (fondo limpio) genera N vistas direccionales CONSISTENTES (el mismo personaje rotado en 3D, no re-dibujado) para un juego top-down / isométrico / shooter 8-way. Dos modelos 3D nativos del server: SV3D (orbit turntable en una pasada, mejor consistencia, 3D_sv3d_p.safetensors) o Stable Zero123 (batch de vistas por azimuth, fallback de menor VRAM, 3D_stable_zero123.ckpt). Es el puente 2.5D que faltaba en gamedev-2d: a diferencia de sprite_sheet (re-poza con OpenPose 2D, identidad inconsistente entre angulos), aqui la difusion 3D ROTA la figura sobre su eje, asi casco/arma/paleta son los mismos en cada direccion. Funcion pura: solo construye el grafo (class_types/inputs verificados contra /object_info); el coste GPU esta al enviar con comfyui_submit_workflow. La imagen frontal debe existir ya en el input/ del servidor."
tags: [comfyui, ml, gamedev-2d, sprite, directional, sv3d, zero123, turntable, multiview, stable-diffusion, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: input_image
    desc: "Nombre del archivo de la imagen frontal del personaje dentro del input/ del servidor ComfyUI (lo carga LoadImage). Debe existir ya (subir con POST /upload/image o copiar a ~/ComfyUI/input/). Idealmente personaje centrado, encuadrado entero, mirando a camara, sobre fondo limpio (SV3D/Zero123 rinden mucho mejor sin fondo). No puede estar vacio."
  - name: directions
    desc: "Numero de direcciones a generar. 8 (default) = 8-way N/NE/E/SE/S/SW/W/NW (shooter 8-direcciones, top-down/iso); 4 = N/E/S/W (RPG clasico). Cualquier N>=1 vale (vistas equiespaciadas a 360/N grados); 4 y 8 reciben nombres cardinales via directional_sprite_view_order. keyword-only."
  - name: model
    desc: "'sv3d' (default, orbita turntable en una pasada, mejor consistencia angular, checkpoint 3D_sv3d_p.safetensors) o 'zero123' (batch de vistas con control de azimuth, fallback de menor VRAM, checkpoint 3D_stable_zero123.ckpt, nativo 256px). keyword-only."
  - name: elevation
    desc: "Elevacion de camara en grados. 0 = orbita en el ecuador (vista lateral pura). Top-down/isometrico suele querer PICADO (mirar al personaje algo desde arriba): subir a ~15-30. keyword-only."
  - name: size
    desc: "Lado en px de cada vista. None (default) usa la resolucion nativa del modelo (576 sv3d / 256 zero123). Bajarlo reduce VRAM a costa de detalle (p.ej. 320 para SV3D si OOM). keyword-only."
  - name: orbit_frames
    desc: "SOLO sv3d. Numero de frames del orbit que sintetiza SV3D_Conditioning. None (default) = directions (una imagen por direccion, salida directa). Subirlo (p.ej. 21, el nativo de SV3D) da una orbita mas densa y consistente de la que el caller submuestrea las direcciones (frame mas cercano por azimuth); a mas frames, mas VRAM. Ignorado por zero123. keyword-only."
  - name: seed
    desc: "Semilla del KSampler (0 = determinista; cambiar para variar la orbita). keyword-only."
  - name: ckpt
    desc: "Nombre del checkpoint en el servidor. None (default) usa el del modelo (3D_sv3d_p.safetensors / 3D_stable_zero123.ckpt). Pasar 'sv3d_u.safetensors' para la orbita uniforme en el ecuador. keyword-only."
  - name: steps
    desc: "Pasos de sampling del KSampler. keyword-only."
  - name: cfg
    desc: "Guidance scale. None (default) usa el del modelo (2.5 sv3d / 4.0 zero123). keyword-only."
  - name: min_cfg
    desc: "SOLO sv3d. CFG del primer frame para VideoLinearCFGGuidance (interpola de min_cfg a cfg a lo largo del orbit). keyword-only."
  - name: sampler_name
    desc: "Algoritmo del KSampler. Default 'euler'. keyword-only."
  - name: scheduler
    desc: "Scheduler del KSampler. Default 'karras'. keyword-only."
  - name: filename_prefix
    desc: "Prefijo del archivo de salida del SaveImage. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow (claves = node_ids string, valores = class_type + inputs). SV3D: ImageOnlyCheckpointLoader + LoadImage + SV3D_Conditioning + VideoLinearCFGGuidance + KSampler + VAEDecode + SaveImage (los N frames del orbit). Zero123: ImageOnlyCheckpointLoader + LoadImage + StableZero123_Conditioning_Batched + KSampler + VAEDecode + SaveImage (un batch de directions vistas). El frame i (i-esima imagen del SaveImage, azimuth creciente desde la frontal) = direccion i de directional_sprite_view_order(directions). El modulo expone ademas directional_sprite_view_order(directions) -> lista de nombres de direccion alineada por indice con los frames."
tested: true
tests: ["sv3d: estructura + orbit (video_frames=directions, size nativa 576)", "orbit_frames override", "zero123: StableZero123_Conditioning_Batched, azimuth equiespaciado, size 256", "cfg/ckpt por defecto segun modelo", "elevation/seed reflejados", "directional_sprite_view_order para 4/8/N", "errores: input vacio, model invalido, directions<1", "determinismo"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_directional_sprite_workflow.py"
file_path: "python/functions/ml/comfyui_build_directional_sprite_workflow.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_directional_sprite_workflow import (
    comfyui_build_directional_sprite_workflow,
    directional_sprite_view_order,
)
from ml.comfyui_submit_workflow import comfyui_submit_workflow

# 'goblin_front.png' = sprite frontal del personaje, fondo limpio, ya en input/ del server.
wf = comfyui_build_directional_sprite_workflow(
    "goblin_front.png",
    directions=8,          # 8-way N/NE/E/SE/S/SW/W/NW
    model="sv3d",          # turntable consistente (default)
    elevation=15.0,        # algo de picado (top-down/iso)
    size=576,
    seed=7,
)
# wf["3"]["class_type"] == "SV3D_Conditioning"; wf["7"] == SaveImage (los 8 frames del orbit)
pid = comfyui_submit_workflow(wf)["prompt_id"]
# sondear /history/{pid} -> comfyui_fetch_output_image por frame.
# El frame i corresponde a la direccion i:
print(directional_sprite_view_order(8))  # ['S','SE','E','NE','N','NW','W','SW']
```

Probado end-to-end (27/06/2026): goblin `enemy_creature_00001_.png` (compuesto sobre blanco
576×576) → SV3D `3D_sv3d_p.safetensors` 8 direcciones elevation 15 seed 7. `prompt_id
8b9f75de-9dcb-41f6-ae4c-7d52d34ed238`, 8 frames `dir_sprite_goblin_0000{1..8}_.png`, 75 s,
VRAM pico 7145/8192 MiB. Consistencia rotacional medida: MAE adyacentes 27 (rotación
gradual) < frente↔espalda 29.6 (la espalda difiere más = rotación 3D real), spread de paleta
entre frames 3.83 (identidad de color consistente — el mismo goblin en las 8 vistas).

## Cuando usarla

- Cuando necesites un personaje visto desde **varias direcciones de movimiento** con
  **identidad consistente**: enemigos de un 8-way shooter, NPCs de un top-down, unidades de
  un RTS isométrico, sprite del jugador en un roguelike cenital.
- Tienes **UN sprite frontal** (lo generaste con `enemy_creature`/`topdown_sprite`, o lo
  dibujaste) y quieres las otras direcciones SIN re-dibujarlas a mano.
- Elige `directions`: 8 para movimiento diagonal completo, 4 para RPG clásico.
- Sube algo de `elevation` (~15-30) para juegos con cámara picada (iso/top-down); déjalo en
  0 para un turntable lateral puro.
- **No** la uses si quieres re-pozar al personaje (correr, atacar, saltar) en una sola vista
  — eso es `comfyui_build_sprite_sheet_workflow` (OpenPose). Esto **rota**, no anima la pose.

## Gotchas

- **La imagen frontal debe existir ya en `input/`** del servidor (subir con
  `POST /upload/image` o copiar a `~/ComfyUI/input/`). La función es pura y NO valida que
  exista ni que el checkpoint esté instalado; valida con `comfyui_validate_workflow` antes
  de enviar a un server desconocido.
- **Fondo limpio obligatorio para buen resultado.** SV3D/Zero123 rinden mucho mejor con el
  personaje aislado sobre fondo plano (blanco o transparente compuesto sobre blanco). Un
  fondo con escena confunde la rotación 3D. Recorta/compón el sprite antes (Rembg o
  `Image.alpha_composite` sobre blanco).
- **VRAM (RTX 3070 8GB):** SV3D es un modelo de vídeo; aún en lowvram pesa. Medido: 8 frames
  a 576² → pico **7145 MiB**, margen estrecho. Limpia la GPU antes
  (`POST /free {"unload_models":true,"free_memory":true}`) — no convive con un juego AAA
  abierto (~2.7 GB). Si **OOM**: baja `size` (576→320), baja `directions` (8→4), o cae a
  `model="zero123"` (vistas independientes, batch más barato). NO hace falta matar procesos.
- **`comfyui_wait_result` lanza `TimeoutError`** si el orbit no termina dentro del timeout,
  pero el job sigue en GPU y completa — recupera los frames sondeando `/history/{prompt_id}`
  por su cuenta (envolver el wait en try/except o sondear directamente). El golden tardó 75 s,
  pero con `orbit_frames=21` o `size` alto puede irse a minutos.
- **SV3D entrena a 21 frames.** Con `orbit_frames` distinto de 21 (p.ej. el default
  `=directions=8`) el orbit es más espaciado pero funciona (probado a 8). Para máxima
  fidelidad angular usa `orbit_frames=21` y submuestrea las `directions` vistas (frame más
  cercano por azimuth) — a costa de más VRAM y tiempo.
- **Mapeo dirección↔frame:** el frame 0 es la vista frontal (personaje de frente); el frame
  i es el azimuth i·360/N. `directional_sprite_view_order(directions)` da la lista de nombres
  alineada por índice con los frames. La etiqueta cardinal ("S" = front/mirando abajo) es
  convención gamedev top-down; ajústala a tu motor si difiere.
- **Distinto de `comfyui_generate_views_from_image`** (impura): esa orquesta
  `/object_info` + submit + wait + fetch para reconstrucción 3D multi-vista (4 cardinales
  front/right/back/left). Este builder es el equivalente **puro** orientado a sprite
  direccional gamedev, hermano de `comfyui_build_img2vid_workflow` (builder puro frente al
  orquestador). Para el flujo completo build→submit→wait→fetch, compón este builder con esas
  funciones del registry.

## Capability growth log

(sin cambios desde v1.0.0)
