
#include "/lib/fog/fog_common.glsl"
#include "/lib/fog/fogloop.glsl"
vec3 shadowSpace0()
{
    vec3 p3 = gbufferModelViewInverse[3].xyz;
    p3 = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    p3 = diagonal3(shadowProjection) * p3 + shadowProjection[3].xyz;
    return p3;
}

vec3 getVolumetricRays3(vec3 solid, vec4 translucent, inout vec3 sky, float border, vec3 fragpos, vec3 fragpos0,
                        bool iswater, float light, float skylight)
{
    float ebs = eyeBrightnessSmooth.y / (240.0);

    float dither = noise_standard(gl_FragCoord.xy);
    // Water Constants
    float bff = gbf(fragpos, far, 0);

    float estEyeDepth = clamp((14.0 - eyeBrightnessSmooth.y * (16.0 / 255.0)) * (1.0 / 14.0), 0.0, 1.0);
    estEyeDepth = estEyeDepth * estEyeDepth * estEyeDepth * 34.0;
    bool noWater = (isEyeInWater == 0 && !iswater);
    //
    vec3 absorbance_a = vec3(1.0);
    vec3 absorbance_w = vec3(1.0);
    vec3 vL_a = vec3(0.0);
    vec3 vL_w = vec3(0.0);
    //
    float density = 0;

    // project view origin into projected shadowmap space
    vec3 start = shadowSpace0();
    vec3 end = toShadowSpaceProjected(fragpos);
    vec3 refractedSunVec = refract(lightPos, -vec3(0.0, 1.0, 0.0), 0.9);

    float rayLength = length(fragpos);
    float maxZ = min(rayLength, 32.0) / (1e-8 + rayLength);

    vec3 dV = (end - start);
    vec3 dVWorld = mat3(gbufferModelViewInverse) * (fragpos - vec3(0.0));
    float SdotV = dot(sunVec, normalize(fragpos));
    vec3 ambient;
    vec3 sunColor;

#ifdef NETHER
    ambient = vec3(fogColor) * 0.5;
    sunColor = vec3(fogColor);
    fogAmount1 = 5;

#endif
#ifdef OVERWORLD

    ambient = atmosphereUp * 1;

    sunColor = sunLight * 1;

    if (isEyeInWater == 1)
    {
        dVWorld *= maxZ;
        rayLength *= maxZ;
        dV *= maxZ;
    }
    else
    {
        rayLength = bff * 1028;
        if (iswater)
        {
            rayLength = mix(max(rayLength, 16), max(rayLength, 2), rainStrength);
        }
    }
#endif
#ifdef END
    ambient = vec3(fogColor) * 0.25;

    sunColor = vec3(fogColor);
    fogAmount1 = 5;

#endif
    // apply dither
    float darknessEye = 1;
    float mie_water = phaseg(SdotV, 0.4);

    float Rayleigh = phaseRayleigh(SdotV);

    float expFactor = 11.0;
    float dY = normalize(mat3(gbufferModelViewInverse) * fragpos).y * rayLength;
    float sh0 = 1.0;

#ifdef VOLUMETRIC_LIGHTING

    AtmosphereLoop(VL_SAMPLES, expFactor, dither, rayLength, start, dV, dY, mie_water, Rayleigh, noWater, estEyeDepth,
                   refractedSunVec, ambient, sunColor, sh0, absorbance_w, vL_w, density, dVWorld);

#else

    AtmosphereNoLoop(VL_SAMPLES, expFactor, rayLength, dY, mie_water, Rayleigh, noWater, estEyeDepth, refractedSunVec,
                     ambient, sunColor, absorbance_w, vL_w);

#endif

    sh0 = clamp((sh0 + dither * 0.1 * skylight), 0, 1);
    sh0 = mix(sh0, 1, border);

    vL_a = vL_a + (vL_a - dot(vL_a, vec3(0.299, 0.587, 0.114))) * 0.1;
    vec3 transmittance;

    vec3 progressW = gbufferModelViewInverse[3].xyz + cameraPosition + dVWorld;

#ifdef OVERWORLD
    float rayLengthGround = pow(length(fragpos), 1.75) * fogAmount1;

#ifdef VL_NOISE_DENSITY

    if (isEyeInWater == 0)
        density = mix(density, fogAmount1, rainStrength);
    rayLengthGround = pow(length(fragpos), 1.75) * density * 2;

#endif

    vL_a =
        fogAtmosphereALT(mat3(gbufferModelViewInverse) * fragpos, rayLengthGround, rayLengthGround, transmittance, clamp(density * rayLengthGround * 0.000001, 0, 1)) * sh0;

#else
    float rayLengthGround = pow(length(fragpos), 2.3) + density * 666;

    float rayLengthSky = mix(rayLengthGround, 1000000, border * border);
    vL_a = fogAtmosphere2(mat3(gbufferModelViewInverse) * fragpos, rayLengthGround, rayLengthGround, transmittance);

#endif
#ifdef OVERWORLD
    vL_a = vL_a * eyeBrightnessSmooth.y / vec3(240.0);
#endif
    switch (isEyeInWater)
    {
    case 0:
        if (iswater)
        {

            vec3 transformedFragPos = mat3(gbufferModelViewInverse) * fragpos;
            float transformedFragPosLength = length(transformedFragPos);
            float np3_y = transformedFragPos.y / transformedFragPosLength;

            float estimatedDepth = distance(fragpos, fragpos0) * abs(np3_y);

            absorbance_w = min(exp(-totEpsilon * estimatedDepth), 1);

            solid *= absorbance_w;
            solid += vL_w * light;
        }
        translucent.rgb *= transmittance;
        translucent.rgb += vL_a;

        solid *= transmittance;
        solid += vL_a;

        break;
    case 1:
        absorbance_w = exp(-length(fragpos) * totEpsilon);

        solid *= absorbance_w;
        solid += vL_w;
        translucent.rgb *= absorbance_w;
        translucent.rgb += vL_w;
        sky *= absorbance_w;
        sky += vL_w;
        break;
    case 2:
    case 3:
        solid *= exp(-length(fragpos));
        solid += fogColor;
        break;
    }
#if !defined DISTANT_HORIZONS && defined SKYBLEND

    float blend_transparent = gbf(fragpos, far, 0.0);
    float blend_solid = gbf(fragpos0, far, 0.0);
    if (iswater)
        blend_solid = blend_transparent;

    solid = mix(solid, sky, blend_solid * blend_solid);
    translucent.rgb = mix(translucent.rgb, sky, blend_transparent * blend_transparent);
#endif
    vec3 color = mix(solid, translucent.rgb, translucent.a);

    return vec3(color);
}

float GetBorderFogMixFactor(in vec3 eyePlayerPos, in float far, in float z0)
{

    float eyeDist = length(eyePlayerPos);
    float borderFogFactor = smoothstep(far * 0.5, far, eyeDist);
    borderFogFactor *= borderFogFactor * borderFogFactor;
    return (eyeDist > far * 0.5) ? borderFogFactor : 0.0;
}