/*
  End Void Particles
  Screen-space luminous particles that drift upward from the void,
  like embers or chorus spores. Drawn in the sky pass for depth-infinite feel,
  but also faintly visible against terrain via additive blend in fog.
*/

#ifdef END

vec3 EndVoidParticles(vec3 viewPos, float dither) {
    vec3 nViewPos = normalize(viewPos);

    // World-space direction for stable particle placement
    vec4 worldPos = gbufferModelViewInverse * vec4(nViewPos, 0.0);
    vec3 wDir = normalize(worldPos.xyz);

    float VoU = dot(nViewPos, upVec);

    // Particles mostly visible looking downward/horizontal, fade looking straight up
    float verticalFade = smoothstep(0.6, -0.3, VoU);

    vec3 particles = vec3(0.0);

    // Three layers at different depths and scales for parallax
    for (int layer = 0; layer < 3; layer++) {
        float layerScale = 80.0 + float(layer) * 60.0;
        float layerSpeed = (0.4 + float(layer) * 0.15) * END_PARTICLE_SPEED;
        float layerBright = 1.0 - float(layer) * 0.25;

        // Project world direction onto a plane at this layer's depth
        vec2 planeCoord = wDir.xz / (abs(wDir.y) + 0.1) * layerScale;

        // Drift upward over time
        planeCoord.y -= frametime * layerSpeed;
        // Slight horizontal sway
        planeCoord.x += sin(frametime * 0.3 + float(layer) * 2.1) * 2.0;

        // Grid-based particle placement
        vec2 cell = floor(planeCoord);
        vec2 frac_pos = fract(planeCoord);

        // Hash for this cell to get particle position within cell
        float h1 = fract(sin(dot(cell, vec2(127.1, 311.7))) * 43758.5453);
        float h2 = fract(sin(dot(cell, vec2(269.5, 183.3))) * 43758.5453);
        float h3 = fract(sin(dot(cell, vec2(419.2, 371.9))) * 43758.5453);

        vec2 particlePos = vec2(h1, h2);

        // Distance from fragment to particle center
        float dist = length(frac_pos - particlePos);

        // Sharp glowing dot with soft falloff
        float glow = exp(-dist * dist * 80.0) * 0.8;
        // Outer halo
        glow += exp(-dist * dist * 12.0) * 0.15;

        // Per-particle twinkle
        float twinkle = 0.5 + 0.5 * sin(frametime * (1.5 + h3 * 3.0) + h1 * 6.28);

        // Particle color — mix between purple core and cyan edge
        vec3 pCol = mix(endRiftCol, endCol.rgb * 1.5, h3 * 0.6);
        pCol = mix(pCol, vec3(0.8, 0.6, 1.0), 0.3);

        // Only some cells have particles (sparsity)
        float exists = step(0.7 - END_PARTICLE_DENSITY * 0.3, h3);

        particles += pCol * glow * twinkle * layerBright * exists;
    }

    particles *= verticalFade * END_PARTICLE_BRIGHTNESS;

    return particles * 0.015;
}

#endif
