#include <metal_stdlib>
using namespace metal;

struct Uniforms {
    float2 resolution;
    float  time;
    float  timeDelta;
    float2 mouse;
    uint   frame;
    uint   pad;
};

struct Params {
    float speed;
    float belly;
    float neck;
    float flutes;
    float twist;
    float gloss;
    float heat;
    float volume;
};

struct RKVertexOut {
    float4 position [[position]];
    float2 uv;
};

vertex RKVertexOut rk_vertex(uint vid [[vertex_id]]) {
    float2 p = float2((vid << 1) & 2, vid & 2);
    RKVertexOut o;
    o.position = float4(p * 2.0 - 1.0, 0.0, 1.0);
    o.uv = p;
    return o;
}

#line 1 "shader.metal"
// Ray Kiln · Pottery Wheel
//
// @episode Pottery Wheel
// @length 60
// @variation 30
// @variations 1,2,4,5,6,7,9,10
// @still 14.0
// @teaches Solids of revolution, twisting, a glaze material
// @category Fragment
// @tags raymarching, signed distance fields, solids of revolution, twisting, glaze, fresnel, procedural sound
// @final Pottery wheel | A fluted pot turning on the wheel, its glaze mirroring the kiln and pooling dark in the grooves, bare clay at its foot.
//
// A pot on a potter's wheel, drawn by raymarching. A pot is a solid of revolution: one curve, the radius at each
// height, spun round an axis. Flutes and throwing lines carve its surface, and a twist winds the flutes into a
// spiral as the wheel turns. The glaze is a material: it mirrors the kiln by Schlick's Fresnel, catches the light in
// a sharp highlight, and pools darker where the grooves keep it from running off.
// Each @param line below becomes a slider in the host app and a row in the page's parameters table:
//   // @param name default min max description

// @param speed 0.5 0.0 2.0 How fast the wheel turns and the camera circles
// @param belly 0.6 0.0 1.0 How far the pot swells at its widest
// @param neck 0.5 0.0 1.0 How much the pot narrows below the lip
// @param flutes 0.5 0.0 1.0 How deep the flutes are carved
// @param twist 0.6 -2.0 2.0 How far the flutes wind round the pot from foot to lip
// @param gloss 0.7 0.0 1.0 How glassy the glaze is: matte to mirror
// @param heat 0.5 0.0 1.0 Slides the glaze from deep ember to white-hot
// @param volume 0.8 0.0 1.0 Loudness of the sound

constant float TAU = 6.2831853;
constant float FLOOR = -1.0;
constant float HEIGHT = 1.5;

// Floating 3D Scene's rotation.
float2 rot(float2 p, float a) {
    float c = cos(a), s = sin(a);
    return float2(c * p.x - s * p.y, s * p.x + c * p.y);
}

// The kiln palette from Flowing Color Field.
float3 kiln(float t) {
    float3 c0 = float3(0.020, 0.012, 0.020);
    float3 c1 = float3(0.280, 0.040, 0.030);
    float3 c2 = float3(0.880, 0.260, 0.050);
    float3 c3 = float3(1.000, 0.680, 0.200);
    float3 c4 = float3(1.000, 0.970, 0.840);
    t = clamp(t, 0.0, 1.0);
    float3 c = mix(c0, c1, smoothstep(0.00, 0.22, t));
    c = mix(c, c2, smoothstep(0.22, 0.50, t));
    c = mix(c, c3, smoothstep(0.50, 0.76, t));
    return mix(c, c4, smoothstep(0.76, 1.00, t));
}

// Glass Orb's Fresnel: Schlick's approximation.
float fresnel(float cosi, float ior) {
    float f0 = (ior - 1.0) / (ior + 1.0);
    f0 *= f0;
    return f0 + (1.0 - f0) * pow(1.0 - clamp(cosi, 0.0, 1.0), 5.0);
}

// ---- The pot ---------------------------------------------------------------------------------------------------

// The profile: the pot's radius at height y, from its foot at 0 to its lip at
// HEIGHT. A foot, a belly that swells in the lower half, a neck that narrows
// under the lip, and the lip flaring out again.
float profile(float y, float belly, float neck) {
    float h = clamp(y / HEIGHT, 0.0, 1.0);
    float r = 0.34 + 0.46 * belly * sin(3.14159 * pow(h, 0.85));
    r -= 0.22 * neck * exp(-pow((h - 0.78) / 0.1, 2.0));
    r += 0.10 * smoothstep(0.88, 1.0, h);
    return r;
}

// The carving: flutes that run up the pot and throwing lines that run round
// it, as a small change to the radius. The angle each flute sits at is
// shifted by the height, so a twist winds them into a spiral.
float carve(float y, float a, float flutes, float twist) {
    float h = clamp(y / HEIGHT, 0.0, 1.0);
    float spiral = a + twist * h * TAU * 0.5;
    float ridge = 0.5 + 0.5 * cos(12.0 * spiral);
    float band = smoothstep(0.08, 0.3, h) * smoothstep(1.0, 0.8, h);
    float lines = 0.004 * sin(y * 44.0) * smoothstep(0.0, 0.1, h);
    return -0.1 * flutes * ridge * ridge * ridge * band + lines;
}

// The radius of the pot at a height and an angle: the profile, carved.
float radius(float y, float a, float4 shape) {
    return profile(y, shape.x, shape.y) + carve(y, a, shape.z, shape.w);
}

// The solid of revolution: a point's distance from the axis, less the radius
// at its height and angle. The surface leans where the profile slopes, so
// the distance is scaled down by that slope to stay safe to march.
float revolve(float3 p, float4 shape) {
    float r = length(p.xz);
    float a = atan2(p.z, p.x);
    float d = r - radius(p.y, a, shape);
    float dy = radius(p.y + 0.01, a, shape) - radius(p.y - 0.01, a, shape);
    return d / sqrt(1.0 + dy * dy * 2500.0);
}

// The pot: the revolved profile, cut to its height, hollowed from the inside
// down to a thick base so the lip is a ring and the dark inside shows.
float pot(float3 p, float4 shape) {
    float d = revolve(p, shape);
    float slab = abs(p.y - HEIGHT * 0.5) - HEIGHT * 0.5;
    float body = max(d, slab);
    float cavity = max(d + 0.08, 0.2 - p.y);
    return max(body, -cavity);
}

// The wheel: a thick disc the pot sits on, a stem down to the floor.
float wheel(float3 p) {
    float2 q = float2(length(p.xz), p.y);
    float head = max(q.x - 1.05, abs(q.y + 0.08) - 0.08);
    float stem = max(q.x - 0.18, abs(q.y + 0.58) - 0.42);
    return min(head, stem);
}

// The scene: the pot first, then the wheel, with the nearer one winning.
// The .w of the answer says which: 1 for the pot, 2 for the wheel.
float2 scene(float3 p, float t, float4 shape) {
    p.xz = rot(p.xz, -t);
    float dp = pot(p, shape);
    float dw = wheel(p);
    return dp < dw ? float2(dp, 1.0) : float2(dw, 2.0);
}

// The direction the surface faces: how the distance changes a small step
// along each axis.
float3 potNormal(float3 p, float t, float4 shape) {
    const float2 e = float2(0.002, 0.0);
    return normalize(float3(
        scene(p + e.xyy, t, shape).x - scene(p - e.xyy, t, shape).x,
        scene(p + e.yxy, t, shape).x - scene(p - e.yxy, t, shape).x,
        scene(p + e.yyx, t, shape).x - scene(p - e.yyx, t, shape).x));
}

// How closed in a point is: a few short steps out along the normal, and
// how much nearer than each step the scene is. The grooves come out dark.
float grooveShade(float3 p, float3 n, float t, float4 shape) {
    float occ = 0.0;
    for (int i = 1; i <= 4; i++) {
        float h = 0.03 * float(i);
        occ += (h - scene(p + n * h, t, shape).x) / h;
    }
    return clamp(1.0 - occ * 0.25, 0.0, 1.0);
}

// ---- The room ---------------------------------------------------------------------------------------------------

// Where the light comes from: high, in front and to the left of the camera,
// so it goes round with it and the pot is always lit from the viewer's side.
float3 lightDir(float t) {
    float a = t * 0.18 + 0.9;
    return normalize(float3(sin(a), 1.1, cos(a)));
}

// The dark of the kiln, warmer toward the horizon, its hot mouth glowing
// where the light comes from.
float sky(float3 rd, float3 l) {
    float up = abs(rd.y);
    float mouth = pow(max(dot(rd, l), 0.0), 6.0);
    return 0.08 + 0.14 * exp(-up * 6.0) + 0.55 * mouth;
}

// The floor: tiles of fired clay with dark grout, the wheel's shadow over
// them, fading into the kiln's haze far off.
float floorLevel(float3 p, float3 l) {
    float2 g = p.xz * 1.6;
    float2 f = abs(fract(g) - 0.5);
    float grout = smoothstep(0.47, 0.44, max(f.x, f.y));
    float2 id = floor(g);
    float tone = 0.28 + 0.08 * fract(sin(dot(id, float2(12.9, 78.2))) * 437.5);
    float3 toWheel = float3(0.0, -0.08, 0.0) - p;
    float along = dot(toWheel, l);
    float miss = length(toWheel - l * along);
    float shadow = along > 0.0 ? smoothstep(0.9, 1.6, miss) : 1.0;
    return tone * grout * (0.35 + 0.65 * shadow);
}

// What a ray sees of the room: the floor if it heads down, otherwise the
// kiln's dark.
float env(float3 ro, float3 rd, float t) {
    float3 l = lightDir(t);
    float back = sky(rd, l);
    if (rd.y < -0.001) {
        float d = (FLOOR - ro.y) / rd.y;
        if (d > 0.0) {
            float3 p = ro + rd * d;
            return mix(back, floorLevel(p, l), exp(-d * 0.12));
        }
    }
    return back;
}

// ---- The glaze ---------------------------------------------------------------------------------------------------

// The glaze: a lit surface that mirrors the kiln. Bare clay at the foot;
// above it the kiln palette, dark where the glaze pools in the grooves and
// lighter over a ridge. Fresnel sets how much of the kiln is mirrored, and
// the highlight is the light itself.
float3 glaze(float3 p, float3 n, float3 rd, float occ, float t,
             float gloss, float heat) {
    float3 l = lightDir(t);
    float diffuse = max(dot(n, l), 0.0);
    float3 rr = reflect(rd, n);
    float mirror = env(p + n * 0.01, rr, t);
    float f = fresnel(dot(-rd, n), 1.0 + 1.2 * gloss);
    float spec = pow(max(dot(rr, l), 0.0), mix(6.0, 120.0, gloss))
               * mix(0.08, 2.2, gloss);
    float pool = 1.0 - occ;
    float level = (0.18 + 0.55 * diffuse) * occ + (heat - 0.5) * 0.5;
    float3 colour = kiln(level - 0.3 * pool);
    colour = mix(colour, colour * float3(0.9, 0.7, 0.6), pool);
    float3 seen = mix(colour, kiln(mirror + 0.25), f * gloss)
                + spec * kiln(0.95);
    float edge = 0.14 + 0.02 * sin(atan2(p.z, p.x) * 5.0);
    float bare = smoothstep(edge + 0.02, edge - 0.02, p.y);
    float3 clay = float3(0.42, 0.28, 0.2) * (0.25 + 0.75 * diffuse) * occ;
    return mix(seen, clay, bare);
}

// The wheel head: dark iron, its rim catching the light, grooves turning.
float3 ironShade(float3 p, float3 n, float3 rd, float t) {
    float3 l = lightDir(t);
    float diffuse = max(dot(n, l), 0.0);
    float3 rr = reflect(rd, n);
    float spec = pow(max(dot(rr, l), 0.0), 30.0) * 0.5;
    float r = length(p.xz);
    float rings = 0.85 + 0.15 * sin(r * 60.0);
    float3 base = float3(0.16, 0.14, 0.14) * rings;
    return base * (0.3 + 0.7 * diffuse) + spec * float3(0.9, 0.8, 0.7);
}

float3 cameraPos(float t) {
    float a = t * 0.18;
    return float3(3.3 * sin(a), 1.25 + 0.3 * sin(t * 0.13), 3.3 * cos(a));
}

float4 shade(float2 uv, constant Uniforms& u, constant Params& p) {
    float t = u.time * p.speed;
    float side = min(u.resolution.x, u.resolution.y);
    float2 q = (uv - 0.5) * u.resolution / side * 2.0;
    float4 shape = float4(p.belly, p.neck, p.flutes, p.twist);
    float3 ro = cameraPos(t);
    float3 fw = normalize(float3(0.0, 0.62, 0.0) - ro);
    float3 rt = normalize(cross(fw, float3(0.0, 1.0, 0.0)));
    float3 up = cross(rt, fw);
    float3 rd = normalize(q.x * rt + q.y * up + 2.3 * fw);

    // March to the scene: the carved surface is not an exact distance, so
    // each step goes 70% of the way.
    float dist = 0.0;
    float which = 0.0;
    for (int i = 0; i < 160; i++) {
        float2 s = scene(ro + rd * dist, t, shape);
        if (s.x < 0.001) { which = s.y; break; }
        dist += s.x * 0.7;
        if (dist > 14.0) break;
    }

    float3 col;
    if (which == 0.0) {
        col = kiln(env(ro, rd, t) + (p.heat - 0.5) * 0.4);
    } else {
        float3 pos = ro + rd * dist;
        float3 n = potNormal(pos, t, shape);
        float occ = grooveShade(pos, n, t, shape);
        if (which == 1.0) {
            float3 local = float3(rot(pos.xz, -t), pos.y).xzy;
            col = glaze(local, n, rd, occ, t, p.gloss, p.heat);
        } else {
            col = ironShade(pos, n, rd, t) * occ;
        }
        col = mix(col, kiln(0.08), smoothstep(6.0, 14.0, dist));
    }
    return float4(clamp(col, 0.0, 1.0), 1.0);
}

// ---- Sound -----------------------------------------------------------------------------------------------------
// The wheel at work. A low hum from the wheel's motor, its pitch following the speed; the hiss of wet hands on
// turning clay, which swells once a turn where the fingers meet the thickest part; a knock as each flute passes the
// potter's thumb, twelve to a turn, soft when the flutes are shallow; and under it all the slow breath of the kiln.

// Cracked Clay's integer hash.
float hashU(uint n) {
    n = (n << 13u) ^ n;
    n = n * (n * n * 15731u + 789221u) + 1376312589u;
    return float(n & 0x7fffffffu) / float(0x7fffffff);
}

// Cracked Clay's sound noise: random values joined `rate` times a second.
float noise1(float t, float rate, uint seed) {
    float x = t * rate;
    float i = floor(x), f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    uint k = uint(i) + seed * 7919u;
    return mix(hashU(k), hashU(k + 1u), f) * 2.0 - 1.0;
}

float2 sound(float t, constant Params& p) {
    // One turn of the wheel takes TAU / speed seconds: the picture's spin.
    float turns = t * p.speed / TAU;
    float phase = fract(turns);

    // The motor: a hum whose pitch climbs with the speed, with its octave.
    float f = 55.0 * (0.8 + 0.6 * p.speed);
    float hum = sin(TAU * f * t) + 0.4 * sin(TAU * f * 2.0 * t + 0.3);
    hum *= 0.12 * smoothstep(0.0, 0.3, p.speed);

    // The hands on the clay: hiss that swells where the belly passes.
    float hiss = noise1(t, 6000.0, 1u) * 0.5 + noise1(t, 1800.0, 2u) * 0.5;
    float swell = 0.4 + 0.6 * pow(0.5 + 0.5 * cos(TAU * phase), 2.0);
    hiss *= 0.07 * swell * (0.3 + 0.7 * p.belly)
          * smoothstep(0.0, 0.2, p.speed);

    // The flutes under the thumb: twelve soft knocks a turn, each a short
    // damped thump; deeper flutes knock louder.
    float slot = floor(turns * 12.0);
    float since = (turns * 12.0 - slot) * TAU / max(p.speed * 12.0, 0.05);
    float knock = sin(TAU * 140.0 * since) * exp(-since * 28.0)
                * (0.6 + 0.4 * hashU(uint(slot)));
    knock *= 0.35 * p.flutes * smoothstep(0.0, 0.2, p.speed);

    // The kiln's breath: a slow swell of low noise.
    float breath = noise1(t, 90.0, 3u) * 0.06 * (0.5 + 0.5 * sin(t * 0.4));

    float pan = 0.5 + 0.3 * sin(TAU * phase);
    float2 mixdown = float2(hum + breath)
                   + float2(1.0 - pan, pan) * (hiss * 2.0 + knock);
    float fadeIn = smoothstep(0.0, 1.5, t);
    return tanh(mixdown * 1.6) * 0.8 * fadeIn * p.volume;
}

#line 1 "rk_host"
fragment float4 rk_fragment(RKVertexOut in [[stage_in]],
                                constant Uniforms& u [[buffer(0)]],
                                constant Params& p [[buffer(1)]]) {
    return shade(in.uv, u, p);
}

struct SoundUniforms { float start; float rate; uint count; uint pad; };
kernel void rk_sound(device float2* out [[buffer(0)]],
                         constant SoundUniforms& su [[buffer(1)]],
                         constant Params& p [[buffer(2)]],
                         uint i [[thread_position_in_grid]]) {
    if (i >= su.count) return;
    out[i] = clamp(sound(su.start + float(i) / su.rate, p), -1.0, 1.0);
}