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>
7.6 KiB
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:
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(npmmilkdrop-preset-converter, añadido aweb/package.jsony ainstall.sh).layers.js→cargarMilk(ruta): pide el.milkal backend (ya los servía), lo convierte y se lo pasa aviz.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
.milkreal en el navegador: 7 ms. - Una capa del mapper con
Dancer/Glowsticks/285.milkrenderiza sin un solo aviso del escenario.
Cuidado al medir esto: si el
.milkno 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
readPixelssobre un lienzo sinpreserveDrawingBuffer: devuelve negro y es facilísimo leerlo como "está volteado". Hay que reservar el contexto conpreserveDrawingBuffer: trueANTES de que la librería llame agetContext.
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.