

#include "/lib/ssr/ssr_common.glsl"

vec3 RT_SIMPLE(vec3 dir, vec3 position, float roughness, float dither, float quality)
{
    vec3 rtPos = vec3(1.1);

    vec3 farthestClipPos = toClipSpace3(position + dir * 64);

    if (all(greaterThan(farthestClipPos.xy, vec2(0.0))) && all(lessThan(farthestClipPos.xy, vec2(1.0))))
    {
        float sampledDepth = texture(depthtex0, farthestClipPos.xy).r;
        float currentDepth = (farthestClipPos.z * far);

        if (sampledDepth < 1.0 && currentDepth <= (sampledDepth * far) + 0.1)
        {
            rtPos = farthestClipPos;
        }
    }

    if (roughness > 0.3)
        return rtPos;

    vec3 clipPosition = toClipSpace3(position);
    vec3 direction = normalize(toClipSpace3(position + dir) - clipPosition);
    vec3 maxLengths = (step(0.0, direction) - clipPosition) / direction;

    float mult = min(maxLengths.x, min(maxLengths.y, maxLengths.z));

    vec3 stepv = direction * mult / quality;
    vec3 spos = clipPosition + stepv * dither;

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

        float depthSample = texelFetch2D(depthtex0, ivec2(spos.xy / texelSize), 0).r;

        if (depthSample < spos.z)
        {
            float dist = abs(depthSample - spos.z) / spos.z;
            if (dist <= 0.005)

                rtPos = vec3(spos.xy, depthSample);
            break;
        }
        spos += stepv;
    }

    return rtPos;
}

// GGX distribution function G1
float g(float NdotL, float roughness)
{
    float k = (roughness * roughness) * 0.5;
    return NdotL / (NdotL * (1.0 - k) + k);
}

const bool colortex0MipmapEnabled = true;
const bool colortex6MipmapEnabled = true;

#include "/lib/specular.glsl"

vec4 calculateSpecularLighting_simplified(
    vec3 normal,
    vec3 np3,
    vec3 albedo,
    float shading,
    float skylight,
    float roughness,
    vec3 f0,
    vec3 fragpos,
    vec3 scene, bool hand)
{

    // Compute metal index from f0.y
    int metalidx = int((f0.y * 255));
    bool isMetal = (metalidx > 229);
    f0 = CalculateF0(f0, isMetal, metalidx, albedo);

    float noise;
    if (!hand)
    {
        noise = blueNoise(gl_FragCoord.xy);
    }
    else
    {
        noise = 0.5;
    }
    if (!isMetal)
    {
        noise = mix(noise, 0.5, luma(f0));
    }
    //  roughness = 0.2;
    vec3 specTerm = shading * GGX2(normal, -np3, lightPos, roughness, f0) * pow(skylight, 3.0);

    // Indirect specular calculation
    vec3 indirectSpecular = vec3(0.0);
    vec3 fresnelDiffuse = vec3(0.0);

    // Coordinate basis for sampling
    mat3 basis = CoordBase(normal);
    vec3 normSpaceView = normalize(-np3 * basis);

    float lod = mix(3, 8.0, roughness);
    vec3 reflectionColorScene = scene;

    // Sample  VNDF
    // vec3 H = SampleBeckmannVNDF(normSpaceView, roughness, blueNoise4(gl_FragCoord.xy).xy);
    vec3 H = SampleGGXVNDF2(normSpaceView, roughness, noise);
    vec3 Ln = reflect(-normSpaceView, H);
    vec3 L = basis * Ln;

    // Fresnel term
    vec3 F = f0 + (1.0 - f0) * pow(clamp(1.0 + dot(-Ln, H), 0.0, 1.0), 5.0);

    // Geometry term
    float NoL = clamp(dot(normal, L), 0.0, 1.0);
    float G1 = g(NoL, roughness);

    // Ray contribution
    vec3 rayContrib = F * G1;

    float rayQuality = (clamp(32 * sqrt(luma(rayContrib)), 1.0, REFLECTION_SAMPLES_MAX));
    vec4 sky = skyFromTexLod(L, colortex6, lod);
    sky.rgb += sky.a * sunLight;

    // float bounceAmount = step(0.0, L.y) + clamp(-L.y * 0.1 + 0.1, 0.0, 1.0);
    float bounceAmount = clamp(L.y * 0.5 + 0.5, 0.0, 1.0) * sqrt(skylight);

    bounceAmount *= (!isMetal && roughness > 0.5) ? 0.5 : 1.0;
    vec3 reflectionColor = mix(reflectionColorScene, sky.rgb * bounceAmount, pow(skylight, 3.0));

    if (!hand && luma(rayContrib) > 0.02 && (rayQuality > 5.0))
    {

        vec3 rtPos = RT_SIMPLE(mat3(gbufferModelView) * L, fragpos, roughness, noise, rayQuality);

        if (rtPos.z < 1.0)
        {
            reflectionColor = textureLod(colortex0, rtPos.xy, lod).rgb;
        }
    }

    indirectSpecular += reflectionColor * rayContrib;
    fresnelDiffuse += rayContrib;

    vec3 a = (indirectSpecular) + (specTerm * sunLight);
    vec3 b = (isMetal ? vec3(0.0) : (vec3(1.0) - fresnelDiffuse));
#ifdef SSR_ACCUMULATION
    vec4 finalout = encodeTwoVec3(a, b);
#else
    vec3 effectiveAlbedo = (metalidx < 229) ? vec3(1.0) : albedo;
    vec4 finalout = vec4((a + b * scene) * effectiveAlbedo, 1.0);

#endif

    return finalout;
}

vec4 computeReflections(float lightmapY, float smoothness, vec3 f0, vec3 normal, vec3 fragpos, float materialsX, vec3 scene, bool hand)
{

    // f0 = vec3(0.0);
    vec3 p3 = toWorldSpace(fragpos);

    vec4 reflections = vec4(0);

    float roughness = unpackRoughness(smoothness);
    float shading = texture(colortex1, texcoord).a;
    vec3 albedo = texture(colortex1, texcoord).rgb;
    reflections = calculateSpecularLighting_simplified(normal, normalize(p3), albedo, shading, lightmapY, roughness, f0, fragpos, scene, hand);

    return vec4(reflections);
}
