
uniform sampler2DShadow shadow;

uniform ivec2 eyeBrightnessSmooth;

uniform float darknessInv;
uniform float darknessEye;
uniform float blindness;
uniform float darknessFactor;
uniform float normWT;
uniform mat4 shadowModelView;
uniform mat4 shadowProjection;

#include "/lib/shadow_param.glsl"

float phaseRayleigh(float cosTheta)
{
    return cosTheta * 0.18 + 0.51;
}

float phaseg(float x, float g)
{
    float gg = g * g;
    return (gg * -0.25 + 0.25) * pow(-2.0 * (g * x) + (gg + 1.0), -1.5) / PI;
}

vec3 calculateAbsorbance(vec3 scatterCoeff, float attenuationDepth)
{
    return clamp(exp(-scatterCoeff * attenuationDepth), 0.0, 1.0);
}
float phaseRayleigh0(float cosTheta)
{
    const vec2 mul_add = vec2(0.1, 0.28) / acos(-1.0);
    return cosTheta * mul_add.x + mul_add.y; // optimized version from [Elek09], divided by 4 pi for energy conservation
}
float phaseRayleigh1(float cosTheta)
{
    const vec2 mul_add = vec2(0.1, 0.28) / acos(-1.0);
    return cosTheta * mul_add.x + mul_add.y; // optimized version from [Elek09], divided by 4 pi for energy conservation
}

#define diagonal3(m) vec3((m)[0].x, (m)[1].y, m[2].z)

vec3 toShadowSpaceProjected(vec3 p3)
{
    p3 = mat3(gbufferModelViewInverse) * p3 + gbufferModelViewInverse[3].xyz;
    p3 = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    p3 = diagonal3(shadowProjection) * p3 + shadowProjection[3].xyz;

    return p3;
}

#include "/lib/sky_function.glsl"

const vec3 totEpsilon = vec3(0.69, 0.19, 0.14);

const vec3 dirtScatterCoef = 0.1 * vec3(0.2, 0.6, 0.8);

float safety = 0.00000001;

vec3 water_coefficientRayleigh = vec3(1.0 * 1e-6, 2.5 * 1e-6, 3.5 * 1e-6);

vec3 fog_coefficientRayleigh = vec3(5.8 * 1e-6, 1.35 * 1e-5, 3.31 * 1e-5);
vec3 fog_coefficientMie = vec3(0.0000030);
uniform float fogStart;
uniform float fogEnd;

float linear_fog_fade(float vertexDistance, float fogStart, float fogEnd)
{
    if (vertexDistance <= fogStart)
    {
        return 1.0;
    }
    else if (vertexDistance >= fogEnd)
    {
        return 0.0;
    }

    return smoothstep(fogEnd, fogStart, vertexDistance);
}

float gbf(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;
}

float waterCaustics(vec3 wPos, vec3 lightSource)
{

    vec2 pos = wPos.xz - (lightSource.xz / lightSource.y * wPos.y);
    float caustic = 0.0;
    mat2 rotationMatrix = mat2(vec2(-0.737367, -0.675493), vec2(0.675493, -0.737367));

    const vec2 wave_size[4] = vec2[](vec2(64.0), vec2(32.0, 16.0), vec2(16.0, 32.0), vec2(48.0));

    for (int i = 0; i < 4; i++)
    {

        float small_wave = texture2D(colortex7, mod(rotationMatrix * pos / wave_size[i], 1.0)).x;

        caustic += max(1.0 - sin(1.0 - pow(0.5 + sin(small_wave * 3.0) * 0.5, 25.0)), 0);
    }
    return (caustic / 4) * 0.5;
}

float fogAmount1 = mix(0.1, 5, normWT) + rainStrength;

float hash(vec3 p)
{
    p = fract(p * 0.3183099 + 0.1) * 17.0;
    return fract(dot(p, p.yzx + vec3(1.0)));
}

float customMix(vec3 i, vec3 f)
{
    float n000 = hash(i + vec3(0.0, 0.0, 0.0));
    float n100 = hash(i + vec3(1.0, 0.0, 0.0));
    float n010 = hash(i + vec3(0.0, 1.0, 0.0));
    float n110 = hash(i + vec3(1.0, 1.0, 0.0));
    float n001 = hash(i + vec3(0.0, 0.0, 1.0));
    float n101 = hash(i + vec3(1.0, 0.0, 1.0));
    float n011 = hash(i + vec3(0.0, 1.0, 1.0));
    float n111 = hash(i + vec3(1.0, 1.0, 1.0));

    float nx00 = mix(n000, n100, f.x);
    float nx10 = mix(n010, n110, f.x);

    float nx01 = mix(n001, n101, f.x);
    float nx11 = mix(n011, n111, f.x);

    float nxy0 = mix(nx00, nx10, f.y);
    float nxy1 = mix(nx01, nx11, f.y);

    return mix(nxy0, nxy1, f.z);
}

float noisem(vec3 x)
{
    vec3 i = floor(x);
    vec3 f = fract(x);
    f = f * f * (3.0 - 2.0 * f);

    return customMix(i, f);
}

float cloud_density0(in vec3 x)
{
    // Calculate uniform coverage
    float unifCov = exp2(-max(x.y - 60.0, 0.0) / 32.0);
    unifCov = pow(unifCov, 4) * 32.0 * fogAmount1;
    if (unifCov <= 0.01)
        return 0.0;

    // Scale coordinates
    x *= vec3(12.0, 24.0, 12.0);

    // Initialize noise parameters
    float result = 0.0;
    float w = 1.0;
    float w_sum = 0.0;
    vec3 x_oct = x / 256.0;

    // Accumulate noise
    for (int i = 0; i < 4; i++)
    {
        result += w * noisem(x_oct + 0.02 * frameTimeCounter);
        x_oct *= 2.0;
        w_sum += w;
        w *= 0.6;
        if (result <= 0.1)
            return 0.0;
    }

    // Normalize result
    result /= w_sum;

    // Apply bias and adjust result
    float bias_noise = max(0.0, 0.5 - 0.1 * exp(-(x.y - 400.0) / 8192.0));
    float bias = 0.7 * (1 - pow(bias_noise, 1.5));
    result = max(0.0, (result - bias) / (1.0 - bias));

    // Final adjustment and return
    return pow(result, 0.5) * unifCov;
}
float cloud_density(in vec3 x)
{
    // Calculate uniform coverage
    float unifCov = exp2(-max(x.y - 60.0, 0.0) / 32.0);
    unifCov = pow(unifCov, 4) * 32.0 * fogAmount1;
    if (unifCov <= 0.05)
        return 0.0;

    // Scale coordinates
    x *= vec3(12.0, 24.0, 12.0);

    // Initialize noise parameters
    float result = 0.0;
    float w = 1.0;
    float w_sum = 0.0;
    vec3 x_oct = x / 256.0;

    // Accumulate noise
    for (int i = 0; i < 4; i++)
    {
        result += w * noisem(x_oct + 0.02 * frameTimeCounter);
        if (result <= 0.1)
            return 1.0;
        x_oct *= 2.0;
        w_sum += w;
        w *= 0.7;
    }

    // Normalize result
    result /= w_sum;

    // Apply bias and adjust result
    float bias_noise = max(0.0, 0.5 - 0.1 * exp(-(x.y - 400.0) / 8192.0));
    float bias = 0.7 * (1 - pow(bias_noise, 1.5));
    result = max(0.0, (result - bias) / (1.0 - bias));

    // Final adjustment and return
    return pow(result, 0.5) * unifCov;
}

float cloudVol(in vec3 pos)
{
    float unifCov = exp2(-max(pos.y - 60.0, 0.0) / 32.0);
    unifCov = pow(unifCov, 4) * 32.0 * fogAmount1;
    if (unifCov <= 1)
        return 0.0;

    // Precompute some values
    vec3 scaledPos = pos * vec3(1.0, 0.0625, 1.0) + frameTimeCounter * vec3(1, 0.0, 1);
    vec3 samplePos = vec3(scaledPos.x, scaledPos.y * 0.5, scaledPos.z);

    // Get the integer and fractional parts
    vec3 p = floor(samplePos);
    vec3 f = fract(samplePos);

    // Smooth interpolation
    f = f * f * (3.0 - 2.0 * f);

    // Compute texture coordinates
    vec2 uv = p.xz + f.xz + p.y;
    vec2 coord = uv / 512.0;

    // Sample noise texture
    vec2 noiseSample = texture2D(noisetex, coord).yx;
    float noise = mix(noiseSample.x, noiseSample.y, f.y);

    // Apply fog amount and return
    return pow(noise, PI) * 10 * unifCov;
}

#include "/lib/fog/fognoloop.glsl"
