// @name Golden Crystal Bloom
// @group Placement
// @param uOffsetX "Offset X" 0.0 -1.5 1.5 0.01
// @param uOffsetY "Offset Y" 0.0 -1.0 1.0 0.01
// @param uScale "Overall Scale" 1.25 0.2 4.0 0.01
// @param uScaleX "Scale X" 1.0 0.25 4.0 0.01
// @param uScaleY "Scale Y" 1.0 0.25 4.0 0.01
// @group Center Cutout
// @param uCenterHole "Center Hole On / Off" 0.0 0.0 1.0 1.0
// @param uHoleRadius "Hole Radius" 0.18 0.0 0.7 0.005
// @param uHoleSoftness "Hole Edge Softness" 0.035 0.0 0.3 0.002
// @param uHoleCurve "Softness Curve" 1.0 0.25 4.0 0.05
// @group Composition
// @param uPetals "Petal Count" 8.0 4.0 16.0 1.0
// @param uLayers "Bloom Layers" 6.0 2.0 10.0 1.0
// @param uPetalLength "Petal Length" 0.58 0.2 1.2 0.01
// @param uPetalWidth "Petal Width" 0.21 0.05 0.5 0.01
// @param uCenterSize "Jewel Center" 0.16 0.03 0.45 0.01
// @param uSymmetry "Cut Symmetry" 6.0 3.0 14.0 1.0
// @group Choreography
// @param uRotation "Rotation Speed" 0.055 -0.6 0.6 0.005
// @param uUnfurl "Petal Unfurl" 0.34 0.0 1.0 0.01
// @param uBreathing "Bloom Breathing" 0.12 0.0 0.6 0.01
// @param uLayerTwist "Layer Twist" 0.38 -1.5 1.5 0.01
// @param uRipple "Silk Ripple" 0.18 0.0 1.0 0.01
// @group Materials
// @param uGold "Gold Amount" 0.78 0.0 1.0 0.01
// @param uCrystal "Crystal Amount" 0.74 0.0 1.0 0.01
// @param uDispersion "Spectral Dispersion" 0.38 0.0 1.5 0.01
// @param uFacetSharpness "Facet Sharpness" 1.25 0.2 3.0 0.01
// @param uSpecular "Jewelry Highlights" 1.45 0.0 4.0 0.05
// @param uBloom "Optical Bloom" 0.82 0.0 2.5 0.01
// @group Color Grade
// @param uGoldHue "Gold Hue" 0.105 0.04 0.18 0.001
// @param uCrystalHue "Crystal Hue" 0.56 0.0 1.0 0.01
// @param uSaturation "Saturation" 0.72 0.0 1.0 0.01
// @param uExposure "Exposure" 1.18 0.25 2.5 0.01
// @param uBlackLevel "Black Level" 0.035 0.0 0.25 0.005
// @param uVignette "Cinematic Vignette" 0.48 0.0 1.0 0.01
// @group Audio
// @param uAudioReact "Audio Reactivity" 0.58 0.0 2.0 0.01
// @param uBassUnfurl "Bass Unfurl" 0.32 0.0 1.5 0.01
// @param uTrebleGlint "Treble Glint" 0.78 0.0 2.0 0.01

#define PI 3.14159265359

mat2 rot(float a) {
    float s = sin(a), c = cos(a);
    return mat2(c, -s, s, c);
}

float hash21(vec2 p) {
    p = fract(p * vec2(123.34, 456.21));
    p += dot(p, p + 45.32);
    return fract(p.x * p.y);
}

vec3 hsv2rgb(vec3 c) {
    vec3 p = abs(fract(c.xxx + vec3(0.0, 0.6666667, 0.3333333)) * 6.0 - 3.0);
    return c.z * mix(vec3(1.0), clamp(p - 1.0, 0.0, 1.0), c.y);
}

vec2 polarFold(vec2 p, float sectors) {
    float a = atan(p.y, p.x);
    float r = length(p);
    float span = 2.0 * PI / max(sectors, 1.0);
    a = abs(mod(a + span * 0.5, span) - span * 0.5);
    return vec2(cos(a), sin(a)) * r;
}

float petalSDF(vec2 p, float len, float width) {
    p.x -= len * 0.48;
    p.x /= max(len, 0.01);
    p.y /= max(width, 0.01);
    float taper = 1.0 - clamp(p.x + 0.48, 0.0, 1.0) * 0.58;
    return length(vec2(p.x, p.y / max(taper, 0.2))) - 0.50;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 screen = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec2 placedScreen = screen - vec2(uOffsetX, uOffsetY);
    float t = iTime;
    float bass = iBass * uAudioReact;
    float treble = iTreble * uAudioReact;
    float breathe = 1.0 + sin(t * 0.72) * uBreathing * 0.10 + bass * uBassUnfurl * 0.08;
    // Apply placement before rotation so offsets pan the bloom instead of
    // changing its rotation pivot.
    vec2 uv = rot(t * uRotation) * placedScreen;
    uv = uv * uScale / (vec2(uScaleX, uScaleY) * breathe);
    float layerCount = max(2.0, floor(uLayers + 0.5));
    float petals = max(4.0, floor(uPetals + 0.5));
    vec3 gold = hsv2rgb(vec3(uGoldHue, 0.76, 1.0));
    vec3 paleGold = hsv2rgb(vec3(uGoldHue + 0.025, 0.30, 1.0));
    vec3 col = vec3(uBlackLevel) * mix(vec3(0.45, 0.28, 0.14), vec3(0.15, 0.18, 0.28), uCrystal);
    float softBloom = 0.0;

    for (int i = 0; i < 10; i++) {
        float fi = float(i);
        if (fi >= layerCount) break;
        float depth = fi / max(layerCount - 1.0, 1.0);
        vec2 q = rot(fi * uLayerTwist * 0.21 + sin(t * 0.33 + fi) * uUnfurl * 0.08) * uv;
        q = polarFold(q, petals + mod(fi, 2.0));
        float radius = 0.11 + depth * 0.075;
        q.x -= radius;
        q.y += sin(q.x * 9.0 - t * 0.7 + fi) * uRipple * 0.025;
        float len = uPetalLength * mix(0.42, 1.0, depth);
        float width = uPetalWidth * mix(0.62, 1.0, depth);
        float d = petalSDF(q, len, width);
        float aa = 0.006 + depth * 0.002;
        float inside = smoothstep(aa, -aa, d);
        float edge = exp(-abs(d) / (0.012 + 0.018 / max(uFacetSharpness, 0.1)));
        float axis = abs(q.y) / max(width, 0.01);
        float facets = pow(abs(sin(atan(q.y, q.x) * uSymmetry + q.x * 17.0 - fi)), 3.0 + uFacetSharpness * 3.0) * inside;
        float highlight = pow(max(0.0, 1.0 - axis), 12.0) * inside;
        float hue = fract(uCrystalHue + q.x * uDispersion * 0.25 + fi * 0.027);
        vec3 crystal = hsv2rgb(vec3(hue, uSaturation * 0.55, 1.0));
        vec3 spectrum = 0.5 + 0.5 * cos(vec3(0.0, 2.1, 4.2) + q.x * 18.0 * uDispersion + fi);
        vec3 material = mix(crystal, gold, uGold * (0.32 + depth * 0.52));
        material = mix(material, spectrum, facets * uDispersion * 0.48 * uCrystal);
        float shade = mix(0.10, 0.52, depth) + facets * 0.34 + highlight * 0.72;
        col += material * inside * shade / (1.0 + fi * 0.38);
        col += mix(paleGold, spectrum, uCrystal * 0.45) * edge * uSpecular * 0.17 / (1.0 + fi * 0.22);
        softBloom += edge / (1.0 + fi * 0.45);
    }

    float r = length(uv);
    float centerD = abs(uv.x) + abs(uv.y) - uCenterSize;
    float center = smoothstep(0.018, -0.018, centerD);
    float centerEdge = exp(-abs(centerD) / 0.012);
    float crown = pow(abs(cos(atan(uv.y, uv.x) * uSymmetry)), 10.0) * center;
    vec3 prism = 0.5 + 0.5 * cos(vec3(0.0, 2.1, 4.2) + atan(uv.y, uv.x) * 4.0 + r * 24.0 * uDispersion);
    col += mix(gold, prism, uCrystal * uDispersion * 0.5) * center * (0.28 + crown * 0.85);
    col += paleGold * centerEdge * uSpecular * 0.42;
    float glitter = pow(hash21(floor(uv * 110.0) + floor(t * 8.0)), 42.0) * (softBloom + centerEdge);
    col += paleGold * glitter * uTrebleGlint * (0.5 + treble * 2.5);
    col += mix(gold, prism, uCrystal) * softBloom * uBloom * 0.035;
    col *= 1.0 - uVignette * smoothstep(0.18, 0.88, length(screen));
    col *= uExposure * (1.0 + bass * 0.10);
    col = col / (1.0 + col * 0.68);
    col = pow(max(col, 0.0), vec3(0.88, 0.91, 0.95));

    // The cutout is measured in placed screen space rather than the bloom's
    // anisotropic object space. It therefore follows Offset X/Y while staying
    // perfectly circular even when Scale X and Scale Y differ.
    float holeAlpha = 1.0;
    if (uCenterHole > 0.5) {
        float feather = max(uHoleSoftness, 0.0001);
        float innerRadius = max(uHoleRadius - feather * 0.5, 0.0);
        float outerRadius = max(uHoleRadius + feather * 0.5, innerRadius + 0.0001);
        float featheredEdge = smoothstep(innerRadius, outerRadius, length(placedScreen));
        holeAlpha = pow(clamp(featheredEdge, 0.0, 1.0), max(uHoleCurve, 0.01));
    }

    fragColor = vec4(col, holeAlpha);
}
