---
name: comfyui_build_view_3d_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_view_3d_workflow(model_file: str, *, animation: bool = False, width: int = 1024, height: int = 1024) -> dict"
description: "Construye el dict API-format de un visor 3D minimo de ComfyUI con el nodo nativo Load3D (display 'Load 3D & Animation', comfy_extras.nodes_load_3d, categoria 3d) para VISUALIZAR un GLB/GLTF/OBJ/FBX/STL/PLY existente, orbitando con el raton, sin ejecutar el grafo (no es output node). animation=True usa Load3DAdvanced (input viewport_state, control avanzado de camara); animation=False usa Load3D (input image de estado del visor, el del report 0079). Pura, sin red ni I/O."
tags: [comfyui, ml, view-3d, load3d, mesh, workflow, viewer]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: model_file
    desc: "Ruta del modelo RELATIVA al input/ del servidor ComfyUI (ej. '3d/fox_mv_textured.glb'). El archivo debe existir ya bajo ~/ComfyUI/input/3d/ para que el visor lo cargue (Load3D solo lista ese directorio)."
  - name: animation
    desc: "Si True usa Load3DAdvanced (viewport_state, control avanzado de camara/viewport para inspeccionar modelos animados); si False (default) usa Load3D, el visor estandar. Ambos reproducen animaciones embebidas del modelo en el frontend. keyword-only."
  - name: width
    desc: "Ancho del viewport del nodo en px. keyword-only."
  - name: height
    desc: "Alto del viewport del nodo en px. keyword-only."
output: "dict en API format con un unico nodo '1'. Con animation=False: class_type 'Load3D', inputs {model_file, image, width, height}. Con animation=True: class_type 'Load3DAdvanced', inputs {model_file, viewport_state, width, height}. Cargable con comfyui_load_workflow_ui (inyecta en la UI del navegador) o POSTeable a /prompt."
tested: true
tests: ["Load3D simple con model_file/width/height", "animation=True usa Load3DAdvanced", "determinismo: misma entrada -> mismo dict (builder puro)"]
test_file_path: "python/functions/ml/tests/test_comfyui_build_view_3d_workflow.py"
file_path: "python/functions/ml/comfyui_build_view_3d_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_view_3d_workflow import comfyui_build_view_3d_workflow

wf = comfyui_build_view_3d_workflow("3d/fox_mv_textured.glb")
# wf == {"1": {"class_type": "Load3D",
#              "inputs": {"model_file": "3d/fox_mv_textured.glb", "image": "",
#                         "width": 1024, "height": 1024}}}

# Inyectar en la UI abierta (visor interactivo, orbita con el raton):
# from browser.comfyui_load_workflow_ui import comfyui_load_workflow_ui
# comfyui_load_workflow_ui(wf, server_url_substr="8188")

# Variante avanzada (control de camara/viewport):
wf_adv = comfyui_build_view_3d_workflow("3d/walk_cycle.glb", animation=True)
# wf_adv["1"]["class_type"] == "Load3DAdvanced"
```

O lanzable directo con: `./fn run comfyui_build_view_3d_workflow` (imprime los dos workflows de ejemplo).

## Cuando usarla

Cuando ya tengas un mesh GLB/OBJ (p.ej. la salida de `comfyui_image_to_3d_oneshot`,
descargada con `comfyui_fetch_output_mesh`) y quieras VERLO con su textura/color dentro
de un nodo de ComfyUI, interactivo. Construye aquí el dict del visor y cárgalo en la UI
con `comfyui_load_workflow_ui`. Es shape+textura: el visor Three.js pinta el material PBR
del GLB (report 0079: el zorro se ve naranja, no gris). Para añadir el nodo SIN reemplazar
el grafo abierto del usuario, el método no-destructivo es inyectarlo vía CDP
(`LiteGraph.createNode('Load3D')` + `app.graph.add`), ver report 0079.

## Gotchas

- **`model_file` debe ser ruta RELATIVA a `input/`** (p.ej. `3d/fox.glb`), y el archivo
  debe existir bajo `~/ComfyUI/input/3d/`. `Load3D` solo lista/carga ese directorio: si
  el GLB vive en `output/3D/`, cópialo a `input/3d/` antes (eso es I/O, fuera de esta
  función pura). Sin la copia el combo `model_file` solo ofrece `none`.
- **No es output node**: `Load3D`/`Load3DAdvanced` renderizan en el frontend (Three.js)
  SIN ejecutar el grafo (no hace falta Queue). Si quieres mostrar un GLB que produce un
  pipeline al ejecutar, usa `Preview3D` (output node, requiere queue) — no es esta función.
- **Requiere ComfyUI >= 0.26.0** (nodos nativos `Load3D`/`Load3DAdvanced`, módulo
  `comfy_extras.nodes_load_3d`). En versiones anteriores el server rechaza el workflow.
- El flag `animation` elige la VARIANTE de nodo, no un modo "play": ambos visores ya
  reproducen las animaciones embebidas del modelo en el frontend. `Load3DAdvanced` aporta
  `viewport_state` (control fino de cámara), útil para inspeccionar la órbita de un modelo
  animado; `Load3D` da además un preview `image` del visor.
- Pura: sólo arma el dict, no toca red ni disco ni valida contra el server. Valida con
  `comfyui_validate_workflow` si dudas de que el nodo exista en tu versión.
