diff --git a/flow-state/src/checks/gallery.js b/flow-state/src/checks/gallery.js index b2801fb..e3ace7d 100644 --- a/flow-state/src/checks/gallery.js +++ b/flow-state/src/checks/gallery.js @@ -23,7 +23,7 @@ import { featureProviderFor } from '../audio/FeatureTrack.js'; import { songBank } from '../audio/songbank.js'; import { generateLook } from '../look/LookGenerator.js'; import { describeIdentity } from '../look/Identity.js'; -import { frameDescriptor } from './variety/descriptors.js'; +import { frameDescriptor, motionDescriptor } from './variety/descriptors.js'; import { descriptorDistance, STRUCTURAL } from './variety/signature.js'; const THUMB = { width: 256, height: 144 }; @@ -111,9 +111,18 @@ export function renderScene(engine, module, contexts) { // A few frames of warm-up so anything with state is past its first frame. for (let f = ctx.frame - 6; f < ctx.frame; f++) engine.renderFrame(f); const pixels = Uint8Array.from(engine.readPixels(engine.renderFrame(ctx.frame))); + // A second frame, so MOTION is measured rather than silently scored zero. + // + // The descriptor has five structural blocks and this only ever built + // four of them, so `motion` came back 0.000 for every scene in the + // library and dragged the mean down by a fifth across the board. It + // looked like a property of the scenes; it was a missing render. + const moved = Uint8Array.from(engine.readPixels(engine.renderFrame(ctx.frame + 5))); thumbs.push(pixels); - descriptors.push(frameDescriptor(pixels, THUMB.width, THUMB.height)); + const still = frameDescriptor(pixels, THUMB.width, THUMB.height); + const motion = motionDescriptor(pixels, moved, THUMB.width, THUMB.height); + descriptors.push({ ...still, motion: motion.scale.concat(motion.layout) }); } // How different this scene's six outputs are from each other, on the same diff --git a/flow-state/src/scenes/shader/isometric-blocks.js b/flow-state/src/scenes/shader/isometric-blocks.js index 35d4146..6597edd 100644 --- a/flow-state/src/scenes/shader/isometric-blocks.js +++ b/flow-state/src/scenes/shader/isometric-blocks.js @@ -19,6 +19,14 @@ export const isometricBlocks = { params: { grid: { type: 'float', range: [2, 10], default: 4.5, uniform: 'u_grid', bias: 'density', slowAxis: true }, height: { type: 'float', range: [0.1, 0.7], default: 0.35, uniform: 'u_height', bias: 'energy' }, + // Displacement: how far each block wanders from its slot on the grid. + // + // Every block sat exactly on the lattice, so the plan of the field was + // the same plan in every song and only the heights moved — which is + // what the gallery was reporting as a layout distance of 0.005, near + // enough to nothing. Displacing them per cell puts the SONG in the + // arrangement rather than only in the elevation. + scatter: { type: 'float', range: [0, 0.85], default: 0.3, uniform: 'u_scatter', bias: 'density' }, plan: { type: 'float', range: [0.2, 0.5], default: 0.4, uniform: 'u_plan' }, tilt: { type: 'float', range: [0.35, 0.75], default: 0.55, uniform: 'u_isoTilt' }, light: { type: 'float', range: [0, 1.4], default: 0.65, uniform: 'u_light' }, @@ -80,12 +88,18 @@ vec4 scene(vec2 uv, vec2 p) { float row = rowHere - float(7 - j); vec2 cell = vec2(colX, row); - float groundY = (row + stagger) * tilt * cw; // where this cell meets the ground - float h = columnHeight(cell, t); + // Each cell steps off its slot by a fixed amount of its own, so the + // plan of the field is the song's rather than the lattice's. Bounded + // under half a cell: past that blocks cross each other and the + // isometric read — which is the whole point of the scene — breaks down. + vec2 offset = (hash22(cell + u_seed + 3.7) - 0.5) * u_scatter * cw * 0.9; + + float groundY = (row + stagger) * tilt * cw + offset.y; // where this cell meets the ground + float h = columnHeight(cell, t) * (1.0 + offset.x * 2.0); float dy = p.y - groundY; // height up the block face // Side wall: the plan swept up from the ground line to the top face. - float inX = smoothstep(u_plan * cw, u_plan * cw * 0.85, abs(fx)); + float inX = smoothstep(u_plan * cw, u_plan * cw * 0.85, abs(fx - offset.x)); float wall = inX * step(0.0, dy) * step(dy, h); float lit = 0.5 + 0.5 * sat(fx / (u_plan * cw) * 0.8 + 0.5);