#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 height;
    float rough;
    float sun;
    float fog;
    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 · Episode 11 · Endless Terrain
//
// @episode 11 Endless Terrain
// @length 60
// @variation 30
// @variations 1,2,3,4,5,7,10,12
// @still 24.0
// @teaches Heightfield raymarching, sky, sun, distance fog
// @category Fragment
// @tags raymarching, heightfield, terrain, fractal noise, value noise, fog, sky, soft shadows, procedural sound
// @final Endless terrain | Fly low over the ridges toward the setting sun, and steer with the pointer: across to look aside, up to climb.
//
// Mountains that go on forever, flown over at dusk. The land is one function: a height for every point on the
// ground, made of fractal noise. Each pixel sends a ray out and walks it forward until it drops below the ground;
// the sun lights what it finds, the sky fills the rest, and the haze of distance joins the two.
// 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 camera flies over the land
// @param height 1.0 0.3 1.6 How tall the mountains are
// @param rough 0.5 0.0 0.8 How rough the land is: soft hills, or sharp ridges
// @param sun 0.35 0.0 1.0 How high the sun stands, from the horizon to the afternoon
// @param fog 0.5 0.0 1.0 How thick the haze of distance is
// @param heat 0.5 0.0 1.0 Slides the palette 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 FAR = 140.0;

// Episode 1's hash and value noise.
float hash21(float2 p) {
    p = fract(p * float2(123.34, 456.21));
    p += dot(p, p + 45.32);
    return fract(p.x * p.y);
}

float vnoise(float2 p) {
    float2 i = floor(p), f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    float a = hash21(i), b = hash21(i + float2(1, 0));
    float c = hash21(i + float2(0, 1)), d = hash21(i + float2(1, 1));
    return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

// Episode 3'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 land --------------------------------------------------------------------------------------------------
// A heightfield: one height for every point (x, z) on the ground. It is episode 1's fractal noise, with two changes.
// Each octave is folded into a ridge, 1 - |2n - 1|, as far as `rough` asks, so peaks come to a sharp crest. And
// each octave is turned a little against the one before, so no grid lines show. Fewer octaves give the broad shape,
// for marching; more give the detail, for lighting.

float terrain(float2 x, int octaves, float height, float rough) {
    float2 p = x * 0.06;
    float v = 0.0, a = 0.5;
    for (int i = 0; i < octaves; i++) {
        float n = vnoise(p);
        float ridge = 1.0 - abs(2.0 * n - 1.0);
        // The fine octaves are only a little ridged, or every small bump would be a crest.
        v += a * mix(n, ridge * ridge, i < 4 ? rough : rough * 0.3);
        p = rot(p, 0.64) * 2.0 + float2(3.1, 7.7);
        a *= 0.45;
    }
    // Low ground stays flat and wide, high ground climbs fast: the valleys and the peaks.
    return v * v * 18.0 * height - 1.5;
}

// The normal from the land's slope: how much the height changes a small step along x and along z.
float3 landNormal(float2 x, float e, float height, float rough) {
    float h = terrain(x, 8, height, rough);
    float hx = terrain(x + float2(e, 0.0), 8, height, rough);
    float hz = terrain(x + float2(0.0, e), 8, height, rough);
    return normalize(float3(h - hx, e, h - hz));
}

// Marching the heightfield: step forward by a share of the height above the ground, longer
// steps far away, then halve the last step back and forth to find the edge.
float marchLand(float3 ro, float3 rd, float height, float rough) {
    float d = 0.1;
    float prev = 0.1;
    for (int i = 0; i < 160; i++) {
        float3 p = ro + rd * d;
        float gap = p.y - terrain(p.xz, 5, height, rough);
        if (gap < 0.002 * d) {
            float lo = prev, hi = d;
            for (int k = 0; k < 6; k++) {
                float mid = 0.5 * (lo + hi);
                float3 q = ro + rd * mid;
                bool under = q.y < terrain(q.xz, 5, height, rough);
                if (under) hi = mid; else lo = mid;
            }
            return hi;
        }
        prev = d;
        d += max(gap * 0.4 / max(height, 1.0), 0.01 * d);
        if (d > FAR || p.y > 30.0) break;
    }
    return -1.0;
}

// A soft shadow: walk toward the sun; the closer the ray passes to the land, the darker.
float softShadow(float3 p, float3 l, float height, float rough) {
    float lit = 1.0;
    float d = 0.3;
    for (int i = 0; i < 32; i++) {
        float3 q = p + l * d;
        float gap = q.y - terrain(q.xz, 4, height, rough);
        lit = min(lit, 10.0 * gap / d);
        if (lit < 0.0 || q.y > 25.0) break;
        d += clamp(gap, 0.3, 4.0);
    }
    return smoothstep(0.0, 1.0, lit);
}

// ---- The flight ------------------------------------------------------------------------------------------------
// The camera flies along z, wandering across on two slow waves. It holds its height above the highest ground a little
// ahead and to each side, so it lifts before a ridge instead of flying into it.

float flightX(float z) {
    return 9.0 * sin(z * 0.021) + 4.0 * sin(z * 0.047 + 1.3);
}

float flightY(float z, float height, float rough) {
    float top = -10.0;
    for (int i = 0; i < 4; i++) {
        float zz = z + float(i) * 3.0;
        float x = flightX(zz);
        top = max(top, terrain(float2(x, zz), 5, height, rough));
        top = max(top, terrain(float2(x - 2.5, zz), 5, height, rough) - 1.0);
        top = max(top, terrain(float2(x + 2.5, zz), 5, height, rough) - 1.0);
    }
    return max(top, 0.0) + 2.6;
}

float travel(float t) {
    return t * 8.0;
}

// The sun: low ahead and to the right; `sun` raises it.
float3 sunDir(float sun) {
    return normalize(float3(0.6, 0.04 + 0.6 * sun, 1.0));
}

// ---- The kiln palette from episode 1 ---------------------------------------------------------------------------

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));
}

// The sky's brightness along a ray: dark overhead, bright at the horizon, brightest round the sun, with its disc.
float skyLevel(float3 rd, float3 l) {
    float toward = max(dot(rd, l), 0.0);
    float horizon = exp(-max(rd.y, 0.0) * 5.0);
    float level = 0.12 + 0.3 * horizon + 0.35 * pow(toward, 8.0) * horizon;
    level += 0.25 * pow(toward, 64.0);
    level += smoothstep(0.9996, 0.9998, toward);
    return level;
}

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;

    // Steering: the pointer's offset from the middle, zero there, as in every video. Across
    // turns the view; up climbs and looks down at the land; down looks up at the sky.
    float2 steer = u.mouse / u.resolution - 0.5;

    float z = travel(t);
    float3 ro = float3(flightX(z), flightY(z, p.height, p.rough), z);
    float climb = max(steer.y, 0.0);
    ro.y += climb * 24.0;
    // Look along the flight path a little ahead, turned by the pointer.
    float ahead = z + 6.0;
    float tip = 1.2 + climb * 5.0 + min(steer.y, 0.0) * 6.0;
    float3 target = float3(flightX(ahead), ro.y - tip, ahead);
    float3 fw = normalize(target - ro);
    float yaw = steer.x * 2.2;
    fw.xz = rot(fw.xz, -yaw);
    // Bank into the turns of the path, as a plane does.
    float bank = (flightX(z + 8.0) - flightX(z)) * 0.04 - steer.x * 0.25;
    float3 rt = normalize(cross(float3(sin(bank), cos(bank), 0.0), fw));
    float3 up = cross(fw, rt);
    float3 rd = normalize(q.x * rt + q.y * up + 1.7 * fw);

    float3 l = sunDir(p.sun);
    float sky = skyLevel(rd, l);
    float d = marchLand(ro, rd, p.height, p.rough);

    float level = sky;
    if (d > 0.0) {
        float3 pos = ro + rd * d;
        // The normal's step grows with distance, so far slopes stay smooth instead of shimmering.
        float3 n = landNormal(pos.xz, 0.002 + 0.0015 * d, p.height, p.rough);
        float shadow = softShadow(pos + n * 0.05, l, p.height, p.rough);
        float sunLight = max(dot(n, l), 0.0) * shadow;
        // Light from the sky above, more on flat ground; light bounced back from the sunlit land facing away.
        float skyLight = 0.5 + 0.5 * n.y;
        float bounce = max(dot(n, normalize(float3(-l.x, 0.0, -l.z))), 0.0);
        // Rock is darker than the flat tops where dust settles; steep faces are bare.
        float flat = smoothstep(0.6, 0.9, n.y);
        float rock = 0.7 + 0.3 * flat + 0.2 * (vnoise(pos.xz * 0.8) - 0.5);
        level = rock * (0.75 * sunLight + 0.2 * skyLight + 0.1 * bounce);

        // Distance fog: the further the land, the more of the sky's own light along that ray replaces it, by
        // exp(-distance). Toward the sun the haze is lit and glows.
        float density = 0.006 + 0.03 * p.fog;
        float through = exp(-d * density);
        float haze = 0.25 + 0.25 * pow(max(dot(rd, l), 0.0), 6.0);
        level = mix(haze, level, through);
    }

    return float4(kiln(level + (p.heat - 0.5) * 0.5), 1.0);
}

// ---- Sound -----------------------------------------------------------------------------------------------------
// Wind over the land: a rush of air that swells as the ground rises close under the camera and thins as it falls
// away. Under it a low chord holds, and it opens up, brighter, the higher the sun stands. Each time the camera
// crosses a crest, a soft bell rings, its note from how high that crest is.

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

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;
}

// How close the ground is under the flight at z: 1 skimming it, falling toward 0 as it drops away.
float closeness(float z, float height, float rough) {
    float ground = terrain(float2(flightX(z), z), 4, height, rough);
    float clearance = flightY(z, height, rough) - ground;
    return exp(-max(clearance - 2.6, 0.0) * 0.35);
}

float2 sound(float t, constant Params& p) {
    float ts = t * p.speed;
    float z = travel(ts);

    // The wind: two bands of noise in each ear, a dark one always there and a brighter one that comes with the
    // ground. Faster flight, more wind.
    float near = closeness(z, p.height, p.rough);
    float gust = 0.6 + 0.4 * noise1(t, 0.4, 9u);
    float2 low = float2(noise1(t, 180.0, 1u), noise1(t, 180.0, 2u));
    float2 high = float2(noise1(t, 900.0, 3u), noise1(t, 900.0, 4u));
    float2 wind = (low * 0.6 + high * near * 0.8) * gust;
    wind *= min(0.3 + p.speed, 1.2);

    // The chord: A2, E3 and C4. Its upper partials grow with the sun.
    float bright = 0.15 + 0.6 * p.sun;
    float2 chord = float2(0.0);
    for (int i = 0; i < 3; i++) {
        float f = i == 0 ? 110.0 : i == 1 ? 164.81 : 261.63;
        // Each ear a few cents apart, so the chord beats slowly.
        float l = sin(TAU * f * t) + bright * 0.5 * sin(TAU * 2.0 * f * t)
                + bright * 0.25 * sin(TAU * 3.0 * f * t);
        float g = f * 1.003;
        float r = sin(TAU * g * t + 1.0)
                + bright * 0.5 * sin(TAU * 2.0 * g * t + 0.4);
        float swell = 0.75 + 0.25 * sin(t * 0.3 + float(i) * 2.1);
        chord += float2(l, r) * swell;
    }
    chord *= 0.05;

    // The bells: every 6 units of flight is a stretch of land; its bell rings as the camera passes its middle, on a
    // note of A minor pentatonic picked by the height of the ground there.
    float2 bells = float2(0.0);
    float slot = floor(z / 6.0);
    for (int k = -2; k <= 0; k++) {
        float j = slot + float(k);
        float at = (j + 0.5) * 6.0;
        float since = (z - at) / (8.0 * max(p.speed, 0.05));
        if (since < 0.0) continue;
        float g = terrain(float2(flightX(at), at), 3, p.height, p.rough);
        float rank = clamp(floor(g / (2.0 * p.height) + 2.0), 0.0, 6.0);
        float semis = rank < 1.0 ? 0.0 : rank < 2.0 ? 3.0 : rank < 3.0 ? 5.0
                    : rank < 4.0 ? 7.0 : rank < 5.0 ? 10.0
                    : rank < 6.0 ? 12.0 : 15.0;
        float f = 440.0 * exp2(semis / 12.0);
        uint s = uint(int(j) + 100000);
        float pick = hashU(s);
        // Only some stretches ring, the higher ground more often: fixed for each stretch.
        float rings = step(0.55 - 0.06 * rank, pick);
        float env = since * exp(-since * 2.2) * 5.0 * rings;
        env *= 1.0 - exp(-since * 400.0);
        // Gone smoothly before the loop lets go of it.
        env *= 1.0 - smoothstep(9.0, 15.0, z - at);
        float tone = sin(TAU * f * since)
                   + 0.3 * sin(TAU * 2.76 * f * since) * exp(-since * 4.0);
        float pan = hashU(s + 7u);
        bells += float2(1.0 - pan, pan) * tone * env;
    }
    bells *= 0.12 * smoothstep(0.0, 0.1, p.speed);

    float fadeIn = smoothstep(0.0, 2.0, t);
    return tanh((wind * 0.35 + chord + bells) * 1.6) * 0.9 * 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);
}