// STAGE: the effigy. The protagonist as a SOLID, held large and turning.
//
// The soloist shows you the song's form; this shows you the song's OBJECT. The
// difference is what happens over eight bars: a silhouette rotated is the same
// picture at every angle, so a shot of one has nowhere to go, while an assembly
// turning reveals a limb, closes a hole, and reads as something you are walking
// around. That is the whole reason the identity grew a third dimension — see
// look/Identity.js generateForm.
//
// The object turns and the camera does not, deliberately. A moving camera would
// make the changing outline the SHOT's doing; here it is the song's.

export const effigy = {
    name: 'Effigy',
    family: 'geometric',
    // One object against a dark wash — most of the frame is legitimately empty,
    // and it reads as something standing in a space rather than as the space.
    kind: 'fragment',
    consumes: ['form', 'ink', 'staging'],
    texture: 0.6,
    traits: ['shape', 'camera', 'style'],

    params: {
        size:   { type: 'float', range: [0.45, 1.1],  default: 0.7,  uniform: 'u_size' },
        dist:   { type: 'float', range: [1.8, 3.4],   default: 2.4,  uniform: 'u_dist', slowAxis: true },
        lift:   { type: 'float', range: [-1.0, 1.6],  default: 0.5,  uniform: 'u_lift' },
        lens:   { type: 'float', range: [1.0, 2.6],   default: 1.6,  uniform: 'u_lens' },
        turn:   { type: 'float', range: [0.03, 0.7],  default: 0.18, uniform: 'u_turn', bias: 'motion', rate: true },
        tumble: { type: 'float', range: [0.0, 0.8],   default: 0.25, uniform: 'u_tumble' },
        glow:   { type: 'float', range: [0.0, 1.0],   default: 0.35, uniform: 'u_glow', bias: 'energy' },
        palette:{ type: 'palette', count: 5 },
    },

    reactive: {
        glow:   { feature: 'beat',    amount: 0.4, response: 'spike' },
        tumble: { feature: 'bandLow', amount: 0.3 },
    },

    shader: `
vec4 scene(vec2 uv, vec2 p) {
    p = sigCamera(p);
    float t = u_time * u_turn + u_seed;

    vec3 col = mix(pal(0) * 0.13, pal(1) * 0.17, uv.y) + pal(1) * 0.06 * (1.0 - length(p) * 0.4);

    // The song's own element size decides how big its object is, exactly as it
    // decides how big every other stage's elements are — pulled toward it
    // rather than replaced by it, the same bargain the soloist strikes. A
    // stage that took stageScale() outright rendered a 0.7%-of-frame speck on
    // tiny-scale tracks, which passes "renders something" and is not a shot.
    float s = max(u_size * mix(1.0, stageScale(), 0.5), 0.15);

    // A locked-off lens looking at the origin, standing back in units of the
    // object's own radius, so dist means "how much room around it" whatever
    // size the track decided on. Lens is multiplied back into the distance for
    // the same reason: it should change PERSPECTIVE — how much the near side
    // flares — and not how big the subject is. Left coupled, a long lens and a
    // small song scale multiplied into a subject a few percent of frame across.
    vec3 ro = vec3(0.0, u_lift, -u_dist * u_lens / s);
    vec3 fw = normalize(-ro);
    vec3 rt = normalize(cross(vec3(0.0, 1.0, 0.0), fw));
    vec3 up = cross(fw, rt);
    vec3 rd = normalize(fw * u_lens + rt * p.x + up * p.y);

    // The object's own turn, plus a bounded nod on a second axis so the shape
    // is read from more than one band of angles. Both are applied to the RAY,
    // which is the same thing as turning the object and costs no transform.
    float ca = cos(t), sa = sin(t);
    mat3 spin = mat3(ca, 0.0, -sa, 0.0, 1.0, 0.0, sa, 0.0, ca);
    float nod = sin(u_time * 0.11 + u_seed) * u_tumble;
    float cn = cos(nod), sn = sin(nod);
    mat3 nodM = mat3(1.0, 0.0, 0.0, 0.0, cn, sn, 0.0, -sn, cn);
    mat3 turn = nodM * spin;

    float eye = length(ro);
    vec3 n;
    float hit = castMarch(turn * ro, turn * rd, eye + 3.0, n);
    if (hit > 0.0) {
        // The normal comes back in object space; the transpose puts it back in
        // the world, which is where the lighting and the view direction live.
        vec3 nw = n * turn;
        col = castLit(nw, rd);
        // Sits in the track's air: the far side falls toward the background
        // rather than staying lit all the way round.
        col = mix(col, pal(0) * 0.2, clamp((hit - eye) * 0.3 + 0.35, 0.0, 0.7));
    } else {
        // Just outside the silhouette: a halo so the form reads against the
        // wash even when the palette is flat, and a contour drawn at the
        // track's line weight — the object's outline in the song's own hand.
        // Measured at the closest-approach plane and scaled back into scene
        // units, so the line is the track's weight rather than the lens's.
        float near = castSDF3(turn * (ro + rd * eye)) * s / max(u_dist, 1e-3);
        col += pal(3) * u_glow * 0.5 * exp(-max(near, 0.0) * 6.0);
        col = mix(col, pal(4), inkStroke(near) * (0.4 + u_glow * 0.6));
    }

    return vec4(inkValue(col), 1.0);
}
`,
};

export default effigy;
