#ifndef INCLUDE_ENDER_MIST
    #define INCLUDE_ENDER_MIST

    // Renders atmosphere for The End:
    //  - ENDER_MIST: drifting void mist with faint glowing dust particles at
    //    roughly player altitude, so it reads as "kabut & partikel".
    //  - ENDER_CLOUD_CEILING: a dense, mostly-flat fog bank high above the
    //    islands. Deliberately NOT styled like puffy overworld clouds - it's
    //    meant to look like a thick ceiling of fog.
    // Both layers are lit up by GetEnderLightningFlash() so that stray light
    // "petir" flashes read consistently in the fog, the ceiling and the sky.

    #ifdef ENDER_LIGHTNING
        #include "/lib/atmospherics/enderLightning.glsl"
    #endif

    vec4 GetEnderAtmosphere(vec3 nPlayerPos, float lViewPos, float lViewPos1, float dither) {
        vec3 translucentTrack = vec3(1.0);
        float mistDensity = 0.0;
        float ceilingDensity = 0.0;
        float particleGlow = 0.0;

        float maxDist = min(renderDistance, 512.0);
        int sampleCount = int(maxDist / 8.0 + 0.001);

        vec3 traceAdd = nPlayerPos * maxDist / sampleCount;
        vec3 tracePos = cameraPosition;
        tracePos += traceAdd * dither;

        for (int i = 0; i < sampleCount; i++) {
            tracePos += traceAdd;

            vec3 tracedPlayerPos = tracePos - cameraPosition;
            float lTracePos = length(tracedPlayerPos);
            if (lTracePos > lViewPos1) break;

            float distFade = sqrt1(max0(1.0 - lTracePos / maxDist));

            #ifdef ENDER_MIST
                {
                    vec3 wind = vec3(frameTimeCounter * 0.0015, frameTimeCounter * 0.0006, frameTimeCounter * 0.0015);
                    vec3 tp = tracePos * 0.0012;
                    tp += Noise3D(tp - wind) * 0.03;

                    float altFactor = 1.0 - min1(abs(tracePos.y - ENDER_MIST_ALT) / 90.0);

                    float mistSample = pow2(pow2(Noise3D(tp + wind)));
                    mistSample *= altFactor * distFade;
                    mistDensity += mistSample;

                    // Sparse bright motes drifting inside the mist
                    float particleN = Noise3D(tp * 6.0 - wind * 3.0);
                    float particleSample = pow(max0(particleN - 0.965) * 28.5, 3.0);
                    particleSample *= altFactor * distFade;
                    particleGlow += particleSample;
                }
            #endif

            #ifdef ENDER_CLOUD_CEILING
                {
                    vec3 windC = vec3(frameTimeCounter * 0.0004, 0.0, frameTimeCounter * 0.0004);
                    vec3 tpC = tracePos * 0.0004;
                    tpC += Noise3D(tpC - windC) * 0.015;

                    float altFactorC = 1.0 - min1(abs(tracePos.y - ENDER_CLOUD_CEILING_ALT) / 60.0);

                    float ceilingSample = Noise3D(tpC + windC) * 0.6 + Noise3D(tpC * 2.3 - windC * 1.5) * 0.4;
                    ceilingSample = pow2(clamp01(ceilingSample * 1.3 - 0.15));
                    ceilingSample *= altFactorC * distFade;
                    ceilingDensity += ceilingSample;
                }
            #endif

            if (lTracePos > lViewPos) translucentTrack *= 0.985;
        }

        #ifdef ENDER_LIGHTNING
            float flash = GetEnderLightningFlash() * ENDER_LIGHTNING_I;
        #else
            float flash = 0.0;
        #endif

        mistDensity = min1(mistDensity * ENDER_MIST_I);
        ceilingDensity = min1(ceilingDensity * ENDER_CLOUD_CEILING_I);
        particleGlow = clamp(particleGlow, 0.0, 1.0);

        vec3 mistColor = endSkyColor * 1.4 + vec3(0.015, 0.010, 0.030);
        vec3 ceilingColor = endSkyColor * 0.85;
        vec3 flashTint = vec3(0.90, 0.93, 1.00);

        mistColor = mix(mistColor, flashTint, flash * 0.55);
        ceilingColor = mix(ceilingColor, flashTint, flash * 0.85);

        float totalDensity = min1(mistDensity + ceilingDensity);
        float ceilingShare = clamp(ceilingDensity / max(totalDensity, 0.0001), 0.0, 1.0);
        vec3 fogTint = mix(mistColor, ceilingColor, ceilingShare);

        vec3 particleColor = vec3(0.85, 0.78, 1.00) + flashTint * (flash * 0.5);
        fogTint = mix(fogTint, particleColor, particleGlow);

        vec4 enderAtmo = vec4(fogTint * translucentTrack, totalDensity);
        enderAtmo.rgb *= 1.0 - maxBlindnessDarkness;

        if (any(isnan(enderAtmo))) enderAtmo = vec4(0.0);

        return enderAtmo;
    }

#endif //INCLUDE_ENDER_MIST
