Mapping - Los clips salian del reves: mapper.js activaba UNPACK_FLIP_Y_WEBGL, pero el modelo va con origen arriba-izquierda y texImage2D ya sube la primera fila en t=0, asi que el flip la mandaba al final. Comprobado renderizando el mapper real en Chromium headless con una fuente mitad roja / mitad azul. - projectM se puede usar YA en una capa: los .milk de data/presets-projectm se traducen a Butterchurn en el navegador al elegirlos (milkdrop-preset- converter). La capa MilkDrop gana 'biblioteca' (butter | projectm) sin cambiar su firma, para no recrear el contexto WebGL al cambiar de una a otra. Medido: 100/100 presets de una muestra convierten, 84/84 de los que llevan shaders warp/comp; ~7 ms por preset. - Cada capa tiene su pestana arriba y su propia vista, y '+ CAPA' crea una y entra en ella: con varias capas, ir y volver a MAPPING no era viable. - Dos capas MilkDrop sin preset ya no salen identicas (cogian el indice 0): cada instancia elige uno al azar y lo escribe en el estado. - Una capa nueva ya no nace en la fuente "motor", que es el lienzo del motor activo y hacia que dos capas ensenaran lo mismo. Galeria de visuales - scripts/catalogar-visuales.py: ficha de cada clip (pelicula, personajes, duracion) y, sobre todo, si es una silueta de verdad y cuanta figura tiene. Distingue silueta de corte crudo por el negro puro del fondo: 0,63-0,90 frente a 0,04-0,06, sin zona gris. - El panel lista los clips agrupados por pelicula, con nombre legible y aviso de los que casi no tienen figura, en vez del nombre del archivo. - Soporte de siluetas con canal alfa (.webm VP9): transparencia de verdad, sin recorte por luminancia. El shader del mapper ya la respeta. Documentacion - docs/visuales.md nuevo; pendiente.md al dia con lo hecho y lo que queda. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
162 lines
7.6 KiB
Markdown
162 lines
7.6 KiB
Markdown
# Pendiente
|
||
|
||
Lo que sabemos que falta o falla, con el diagnóstico ya hecho para no volver a
|
||
investigarlo desde cero. Ordenado por lo que más duele.
|
||
|
||
---
|
||
|
||
## 1. Las capas no son de verdad independientes
|
||
|
||
**Lo que pasa.** Se pueden poner dos capas con visuales MilkDrop, pero acaban
|
||
siendo **la misma imagen**. Y no hay forma de decir "en esta zona el Mezclador,
|
||
en esta otra Butter", configurando cada una por su cuenta.
|
||
|
||
Son **tres cosas distintas** mezcladas en el mismo síntoma:
|
||
|
||
### 1a. Dos capas Butter sin preset elegido salen idénticas — ✅ HECHO
|
||
|
||
`web/stage/layers.js`, en `crearButter()`, hacía:
|
||
|
||
```js
|
||
let i = Math.max(0, nombres.indexOf(f.preset));
|
||
```
|
||
|
||
Con `preset: ""` (lo que quedaba si la lista de presets aún no había llegado
|
||
al panel al crear la capa), `indexOf("")` devuelve `-1` y el `Math.max(0, -1)`
|
||
lo dejaba en **0**: todas las capas así arrancaban en el mismo preset.
|
||
|
||
**Cómo quedó:** sin preset elegido, cada instancia coge uno **al azar** y lo
|
||
devuelve en `presetElegido`. `sync()` avisa por `deps.onPreset(id, preset)`,
|
||
que `stage.js` (`anotarPresetDeCapa`) escribe en el estado con `set_mapping`,
|
||
así que el panel enseña cuál le tocó en vez de dejar el desplegable en blanco.
|
||
No recrea la instancia: la firma de una capa butter sigue siendo el tipo a
|
||
secas.
|
||
|
||
### 1b. Las capas puestas en "El motor de MOTORES" comparten imagen — ✅ HECHO
|
||
|
||
Esa fuente es, literalmente, el lienzo del motor activo. Dos capas así van a
|
||
enseñar lo mismo siempre — no es un fallo, es lo que significa. El problema era
|
||
que **era la fuente por defecto** de toda capa nueva, así que el primer
|
||
resultado que veía cualquiera era "dos capas iguales".
|
||
|
||
**Cómo quedó:** una capa nueva ya no nace en "motor". `panel.js` tiene
|
||
`fuenteDeCapaNueva()`, que la estrena en MilkDrop con preset al azar
|
||
(contenido distinto desde el primer clic) y solo cae en "motor" si Butterchurn
|
||
todavía no ha dado su lista de presets. Lo usan tanto `+ Capa` como
|
||
`Máscara → Capa`.
|
||
|
||
### 1c. Falta el Mezclador (y Hydra) como fuente de capa — *(el trabajo de verdad)*
|
||
|
||
Hoy las fuentes son: motor, butter, shader, clip, cámara, negro. **No está el
|
||
Mezclador**, así que "una zona con el Mezclador y otra con Butter" solo se
|
||
puede hacer vía "motor", y entonces manda el selector global.
|
||
|
||
Por qué no está: el Mezclador **es** Hydra (genera código Hydra y lo ejecuta), y
|
||
Hydra está montado como instancia única global (`makeGlobal: true`, con `s0`,
|
||
`s1`, `o0` en el espacio global). Para tener dos mezcladores independientes hay
|
||
que instanciar Hydra varias veces con `makeGlobal: false` y usar la API por
|
||
instancia (`h.synth.src(...)`), lo que obliga a reescribir `buildMixerCode` para
|
||
que no dependa de los nombres globales.
|
||
|
||
**Coste realista:** medio día, más el gasto de GPU de un Hydra por capa. Antes
|
||
de meterse, decidir si compensa frente a la alternativa barata: dejar el
|
||
Mezclador solo como motor a pantalla completa y que las capas tiren de
|
||
MilkDrop/shader/clip, que es lo que ya funciona.
|
||
|
||
### 1d. Los dos paneles no están relacionados
|
||
|
||
MOTORES y MAPPING van cada uno por su lado: el preset que eliges en el motor
|
||
Butter y el de una capa MilkDrop son listas distintas, no comparten favoritos,
|
||
y lo que tocas en uno no se refleja en el otro.
|
||
|
||
**Hacia dónde:** una sola biblioteca de presets y una sola lista de favoritos,
|
||
usada por el motor Butter, por las capas del mapping y por projectM. Esto se
|
||
solapa con el punto 2: es el mismo trabajo de unificación.
|
||
|
||
---
|
||
|
||
## 2. projectM en el mapper — ✅ HECHO (camino A)
|
||
|
||
**Lo que pasaba.** Al elegir projectM no había forma de mapearlo ni de usarlo
|
||
en una capa, y ahí está **la biblioteca de verdad**: 9795 presets `.milk` en 11
|
||
categorías y 189 visuales. Butterchurn solo trae ~100 propios.
|
||
|
||
**Por qué no se podía.** projectM es un proceso nativo que pinta en su propia
|
||
ventana X11/Wayland. El compositor de mapping es WebGL dentro del navegador y
|
||
solo puede usar como textura lo que vive en la página.
|
||
|
||
**Cómo quedó.** Se descartó capturar la ventana (frágil en Wayland) y compilar
|
||
projectM a WebAssembly (un proyecto en sí). Se hizo el **camino A**: traducir
|
||
los `.milk` a presets de Butterchurn **en el propio navegador**, al elegirlos.
|
||
|
||
- `web/lib/milkdrop-preset-converter.min.js` (npm `milkdrop-preset-converter`,
|
||
añadido a `web/package.json` y a `install.sh`).
|
||
- `layers.js` → `cargarMilk(ruta)`: pide el `.milk` al backend (ya los servía),
|
||
lo convierte y se lo pasa a `viz.loadPreset()`. Cachea **la promesa**, no el
|
||
resultado, para que dos capas pidiendo el mismo preset lo conviertan una vez.
|
||
- La capa MilkDrop tiene ahora `biblioteca: "butter" | "projectm"`. La firma de
|
||
la capa **sigue siendo `'butter'`**, así que cambiar de biblioteca no recrea
|
||
la instancia ni gasta un contexto WebGL más.
|
||
- El panel tiene selector de biblioteca y, con projectM, los tres niveles
|
||
(categoría / visual / variante) más un botón de "uno al azar".
|
||
|
||
**Medido, no supuesto:**
|
||
|
||
- **100/100** presets de una muestra repartida por toda la biblioteca se
|
||
convierten sin excepción, incluidos **84/84** de los que llevan shaders
|
||
`warp_1`/`comp_1` (que eran justo los dudosos).
|
||
- Conversión de un `.milk` real en el navegador: **7 ms**.
|
||
- Una capa del mapper con `Dancer/Glowsticks/285.milk` renderiza sin un solo
|
||
aviso del escenario.
|
||
|
||
> Cuidado al medir esto: si el `.milk` no se descarga, Butterchurn **sigue
|
||
> pintando su preset de reserva**. Una prueba que solo mire "¿hay píxeles
|
||
> encendidos?" da OK igual. Por eso la comprobación exige además que el
|
||
> escenario no haya soltado ningún aviso. La primera versión de la prueba dio
|
||
> un falso OK exactamente por esto.
|
||
|
||
**Lo que queda de este punto:** la conversión no es perfecta al 100 % en
|
||
fidelidad — algún shader complejo puede verse distinto del original en
|
||
projectM. Se convierte y se ve, pero si un preset no convence, la salida es
|
||
probar otra variante. Y falta unificar los favoritos entre el motor projectM
|
||
y las capas (parte del punto 1d).
|
||
|
||
---
|
||
|
||
## 3. Los clips salían del revés en el mapping — ✅ ARREGLADO
|
||
|
||
**Lo que pasaba.** Al poner un vídeo en una capa del mapping, salía volteado
|
||
verticalmente (boca abajo).
|
||
|
||
**Por qué.** `web/stage/mapper.js`, en `initGL()`, hacía
|
||
`gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)`. Pero el modelo del mapper va
|
||
con el origen **arriba-izquierda** (`vUV.y = 0` es el borde de arriba de la
|
||
superficie) y `texImage2D` ya sube la primera fila de la imagen en `t = 0`.
|
||
Con el flip puesto, `t = 0` pasaba a ser la ÚLTIMA fila: todo del revés.
|
||
Se notaba con los clips porque una visual de MilkDrop es casi simétrica y no
|
||
canta.
|
||
|
||
**Comprobado**, no deducido: con una fuente mitad roja arriba / mitad azul
|
||
abajo, renderizando el `mapper.js` real en Chromium headless y leyendo el
|
||
píxel de arriba con `readPixels`, antes salía AZUL y ahora sale ROJO.
|
||
|
||
De paso se midió el **Mezclador** (Hydra, `s1.init({src})`) con la misma
|
||
prueba: ese sale bien, no hay que tocarlo. Si alguna vez se ve del revés ahí,
|
||
no es este fallo.
|
||
|
||
> Al medir esto, ojo con `readPixels` sobre un lienzo sin
|
||
> `preserveDrawingBuffer`: devuelve negro y es facilísimo leerlo como "está
|
||
> volteado". Hay que reservar el contexto con `preserveDrawingBuffer: true`
|
||
> ANTES de que la librería llame a `getContext`.
|
||
|
||
---
|
||
|
||
## 4. Menor: no hay fundido al cambiar de motor
|
||
|
||
Pasar de Mezcla a Butter es un corte seco. Dentro de una capa MilkDrop sí hay
|
||
transición suave (deslizador de 0–8 s), pero cruzar **dos motores** exige
|
||
renderizar los dos a la vez durante el cruce y fundirlos en el compositor.
|
||
|
||
Se puede hacer en el mapper (dibujar la capa dos veces, con la fuente vieja
|
||
bajando de alfa y la nueva subiendo), pero cuesta tener los dos motores vivos a
|
||
la vez unos segundos. Decidir si merece la pena.
|