¿Qué hace model-by-coding (modelado mediante código)?
Construye un modelo 3D de un objeto físico a partir de fotografías, completamente en código, y luego conviértelo en una película de despiece y en un GIF. El flujo es estricto en una cosa: el modelo se verifica contra las fotos en cada paso, por lo que la película muestra el objeto que fue fotografiado, no algo solo parecido.
Cuándo usarlo
- Un producto de superficie dura (ventilador, lámpara, altavoz, cámara, herramienta, electrodoméstico de cocina) debe ser explicado o mostrado por partes en un video corto;
- El objeto existe físicamente y se puede fotografiar desde varios ángulos;
- El resultado es un clip de despiece en bucle para un README, una publicación o una demo;
- Un conjunto de fotos existente debe convertirse en un modelo editable y rerenderizable, no en una malla escaneada.
Cuándo no usarlo
- Se necesitan activos fotorrealistas: usa fotogrametría o un escáner;
- El objeto es orgánico, blando o se deforma (tela, plantas, rostros);
- No se dispone de fotografías, o el objeto solo es visible en un video;
- Una sola imagen fija es suficiente: los pasos de HyperFrames entonces son innecesarios;
- El tema es una animación de texto/UI: este flujo se basa en geometría.
Flujo
| paso | producir | puerta de aceptación |
|---|---|---|
| 1. Medir | una dimensión de referencia, una tabla de proporciones, un objeto DIM | proporciones verificadas en ≥2 fotos, cada suposición marcada como inferida |
| 2. Modelar | una escena Three.js construida en código, un paquete IIFE, la página visor | el estado ensamblado no tiene huecos; p = 1 separa cada pieza |
| 3. Verificar | seis láminas foto/render en la misma pose | silueta y proporciones de características dentro de tolerancia en las seis |
| 4. Filmar | una composición HyperFrames, informe check, MP4 final | ok: true, sin errores, duración coincide con data-duration |
| 5. Cortar | un GIF en bucle | comienza y termina en poses estables, menos de ~1 MB |
Un presupuesto realista para un objeto es de unas horas a dos días, la mayor parte en los pasos 2–3. Saltarse el paso 3 es lo que convierte una semana de modelado en un modelo incorrecto entregado a tiempo.
Paso 1 — Medir a partir de las fotos
Reúne seis poses (frontal, lateral real, trasera, trasera 3/4, superior, inferior), elige una dimensión grande como referencia, mídela con un escaneo radial y expresa todo lo demás como proporción de ella. Cuenta las características discretas (nervaduras, aspas, radios) por su periodo angular en lugar de a ojo. Mantén cada número en un objeto DIM con un comentario que indique su foto de origen y si es medido o inferido; el tamaño real absoluto es una decisión, no una medición.
Profundidad: references/01-modeling.md §0–2.
Paso 2 — Modelar en código
Un módulo de escena con una API de estado puro: create, setView, setExplode, setExplodeMap, setSpin, setHeadTilt, setLift, render, getAnchors, resize, dispose. Convención del mundo: el objeto mira hacia +Z, la base en y = 0, metros. Construye las carcasas como perfiles torneados, la rejilla como cintas planas (no tubos redondos), las aspas como láminas paramétricas y registra cada pieza a través de una función que almacena su posición ensamblada, desplazamiento de explosión y desplazamiento de anclaje. Nombra las piezas una vez; la película, las etiquetas, los anclajes y el sonido se basan en esos nombres.
Profundidad: references/01-modeling.md §3–6.
Paso 3 — Verificar contra las fotos
Compila el paquete con esbuild, renderiza un fotograma por pose en Chrome headless y compara cada render con su foto como una lámina lado a lado recortada al cuadro delimitador del objeto. Lee las proporciones y la ubicación de las piezas como fracciones del cuadro de cada objeto; no ajustes círculos a bordes sobre un fondo ocupado — el ajuste se engancha a arcos del fondo. Seis poses, algunas iteraciones cada una.
Profundidad: references/03-verify.md.
Paso 4 — Renderizar la película
Construye la composición según el contrato de HyperFrames: un elemento raíz de composición con data-duration, una línea de tiempo GSAP pausada en window.__timelines["main"], Three.js redibujado solo desde hf-seek (nunca desde rAF/timers), medios con su propia temporización marcados como class="clip" con data-start/data-duration, una mezcla de audio como clip de audio, texto a 28 px o más grande, y el paquete grande como assets/vendor/*.min.js. Escribe la lista de planos antes de animar; controla la cámara, la explosión y las etiquetas desde un único script de dirección para que las señales de audio puedan compartir sus constantes. Ejecuta check, lee el informe, luego render --quality delivery.
Profundidad: references/02-render.md.
Paso 5 — Cortar el GIF
Elige un segmento estable de 3–4 s (normalmente la pausa de explosión), córtalo con gif-master (ancho 480, 12 fps, bucle) o con el comando de paleta de dos pasos de ffmpeg, y verifica el tamaño, el número de fotogramas y el bucle antes de publicar. Profundidad:
references/04-gif.md,
scripts/make_gif.md.
Restricciones estrictas
Estas son las reglas que rompen el flujo cuando se violan; no son preferencias de estilo.
- Contrato primero. El objeto mira hacia +Z, base en y = 0, metros, cámara
target + dist * (sin(yaw)cos(pitch), sin(pitch), cos(yaw)cos(pitch)), yaw/pitch en grados. Cada script y la película lo comparten. - Estado puro. La API de la escena debe renderizar el mismo estado a los mismos píxeles. Sin bucle de animación, sin reloj de pared, sin
Math.random()en el código del que dependa el fotograma. - Fotograma = f(t). La cámara, la explosión, el giro, las etiquetas, las tarjetas y el sonido son funciones del tiempo de la película; nada más.
- Contrato de composición.
data-composition-id,data-start,data-duration,data-width,data-heighten el elemento raíz; línea de tiempo pausada registrada comowindow.__timelines["<id>"]; redibujado dentro dehf-seekusandoe.detail.time. - Clips. Los medios con su propio inicio y duración (audio, video, inserciones) llevan
class="clip",data-start,data-duration; el audio ademásdata-track-index. El DOM animado por la línea de tiempo no necesita atributos de clip. - Legibilidad. Nada informativo por debajo de 28 px a 1080p (el umbral de tipo del verificador es 28 px = 2.6% del lado corto).
- Pesos empaquetados. three.js y GSAP como
.min.jsenassets/vendor/; fuentes locales; sin red durante el renderizado. - Los scripts son genéricos. Sin nombre de objeto, sin ruta absoluta, sin directorio personal en
scripts/; cada script debe ejecutarse desde un checkout limpio. - Comandos de verificación. Cada comando y parámetro escrito en docs, scripts o la película debe ejecutarse una vez contra las versiones fijas antes de ser confiado.
Enrutamiento de referencias
| tarea | leer |
|---|---|
| medición, proporciones, perfiles torneados, parámetros de rejilla/aspas, piezas, explosión, anclajes | references/01-modeling.md |
| contrato de composición, dirección/guion gráfico, sincronización de audio, comandos y salidas de HyperFrames | references/02-render.md |
| bucle de capturas de pantalla, láminas de comparación, lectura de ángulos, error de ajuste de círculo | references/03-verify.md |
| tarjeta de operación de gif-master, créditos, receta ffmpeg para GIF | references/04-gif.md |
| repositorio, lanzamiento en GitHub, PR de lista comunitaria, YouTube/X | references/05-publish.md |
| catálogo de síntoma → causa → solución (iluminación, sombras, nervaduras, tipo, paquetes, Chrome) | references/06-gotchas.md |
Herramientas incluidas con este skill:
| archivo | función |
|---|---|
scripts/view.html | visor de un solo fotograma; pose y paquete desde parámetros de consulta |
scripts/verify_sheet.py | lámina lado a lado foto-vs-render (--photo --render --out --bbox) |
scripts/hf_read_check.py | resume un informe de verificación de HyperFrames; --fail-on-error para CI |
scripts/make_gif.md | tarjeta de operación del navegador/ffmpeg para el GIF |
Secuencia ejecutable mínima
Los comandos se ejecutan desde la raíz del repositorio contra el ejemplo trabajado en examples/fan/; para un objeto nuevo, sustituye src/scene.js, photos/ y video/. Los archivos de trabajo van a %TEMP% para que el checkout se mantenga limpio. Mantén los parámetros — son los verificados. Ejecuta en PowerShell.
$work = Join-Path $env:TEMP "mbc-verify"; New-Item -ItemType Directory -Force $work | Out-Null
# 1. empaquetar la escena (plantilla para un objeto nuevo; necesita tener instalado `three`).
# El ejemplo incluye su paquete en examples/fan/video/assets/vendor/fan.bundle.min.js,
# por lo que este paso se puede omitir para el ejemplo.
npx --yes [email protected] src/scene.js --bundle --format=iife --global-name=Scene `
--minify --target=chrome120 --outfile=dist/scene.bundle.min.js
# 2. capturar una pose de verificación del ejemplo (1920x1080, perfil nuevo por ejecución)
$chrome = "C:\Program Files\Google\Chrome\Application\chrome.exe"
$png = Join-Path $work "front.png"
$udd = Join-Path $env:TEMP ("view-" + [guid]::NewGuid().ToString("N"))
& $chrome --headless=new --use-angle=d3d11 --window-size=1920,1080 `
--hide-scrollbars --virtual-time-budget=12000 `
--screenshot=$png --user-data-dir=$udd `
"file:///$(($PWD.Path).Replace('\','/'))/scripts/view.html?bundle=../examples/fan/video/assets/vendor/fan.bundle.min.js&yaw=-10&pitch=12&dist=0.85&ty=0.185&fov=30&noshadow=1"
# 3. comparar ese render con la fotografía frontal en la misma pose
python scripts/verify_sheet.py --photo examples/fan/reference/fan.jpg --render $png `
--out (Join-Path $work "front-sheet.png") --bbox "130,245,1195,1455"
# 4. verificar la película contra el contrato de HyperFrames, luego leer el informe
Set-Location examples/fan/video
# necesita assets/audio/mix.wav (activo del lanzamiento; ver examples/fan/README.md)
npx --yes [email protected] check --at 21.15,27.6,35.18
$report = Get-ChildItem .hyperframes/artifacts/checks/*.json |
Sort-Object LastWriteTime | Select-Object -Last 1
python ../../../scripts/hf_read_check.py $report.FullName --fail-on-error
npx --yes [email protected] snapshot --at 29.6
# 5. renderizar el master de entrega
npx --yes [email protected] render --quality delivery
Set-Location ../../..
# 6. cortar el GIF con ffmpeg (ruta del navegador: scripts/make_gif.md)
$mp4 = (Get-ChildItem examples/fan/video/renders/*.mp4 | Sort-Object LastWriteTime | Select-Object -Last 1).FullName
ffmpeg -hide_banner -loglevel error -y `
-ss 29.6 -t 3.8 -i "$mp4" `
-vf "fps=12,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=sierra2_4a" `
-loop 0 (Join-Path $work "teardown.gif")
El paso 3 imprime los dos cuadros de recorte; la ejecución verificada imprime saved ...\front-sheet.png 1112x702 | photo (130, 245, 1195, 1455) | render (652, 264, 1252, 1080).
Salidas esperadas: check escribe video/.hyperframes/artifacts/checks/<uuid>.json; snapshot escribe video/.hyperframes/artifacts/snapshots/<uuid>/frame-00-at-29.6s.png; render escribe video/renders/video_<YYYY-MM-DD>_<HH-MM-SS>.mp4 (aproximadamente 36 MB para 43.5 s a 1920×1080/30 fps, aproximadamente 2.5 minutos de captura con una GPU de hardware).
Requisitos
- Node.js (probado 22.19.0) con
npx; esbuild 0.24.2 vía npx. - Python 3.11 (probado 3.11.1) con Pillow para la lámina; numpy opcional.
- ffmpeg en PATH (probado con una compilación git del 2025-12-28).
- Chrome para capturas de pantalla headless (probado 157.0.8103.40); la ruta anterior es la predeterminada de Windows.
- HyperFrames fijado en 0.9.36 mediante
npx --yes [email protected]. checkyrenderejecutan Chrome headless con--browser-gpuen automático: GPU de hardware cuando está presente, software (SwiftShader) en caso contrario;previewfunciona mejor con una GPU.
Definición de terminado
- seis láminas de comparación se leen aceptablemente, con las desviaciones listadas y explicadas;
hf_read_check.py --fail-on-errorsale con 0 en el informe de verificación final;- el MP4 de entrega existe y su duración es igual al
data-durationde la composición; - el GIF se repite limpiamente y es lo suficientemente pequeño para publicar;
- cada comando de este skill se ha ejecutado una vez en un checkout limpio.