const vec2 shadowOffsets[6] = vec2[6](
    vec2(0.5303, 0.5303),
    vec2(-0.6250, 0.0),
    vec2(0.3536, -0.3536),
    vec2(0.0, 0.3750),
    vec2(-0.1768, -0.1768),
    vec2(0.1250, 0.0));
#include "/lib/shadows/shadow_misc.glsl"

float shadowFunc(
    float diffuseSun,
    float sssAmount,
    vec3 p3,
    float noise,
    vec2 lightmap,
    vec3 fragpos,
    inout vec3 sAlbedo,
    vec3 albedo,
    vec3 normals,
    inout vec3 colored,
    float entity)
{
    // === Shadow Bias ===
    // === Basic slope bias
    float ndotl = dot(normals, lightPos);
    float slope = clamp(1.0 - ndotl, 0.0, 1.0);

    // Early exit if there is effectively no lightmap contribution
    float eyeBright = pow(clamp(eyeBrightnessSmooth.y / 240.0, 0.0, 1.0), 0.01);

    float skylimit = mix(lightmap.y, 1.0, eyeBright);

    if (skylimit < 0.001)
        return 0.0;

    // === Sample shadow distortion strength
    float distort = calcDistortMainDH(abs(p3.xy));

    // === Compensate for nonlinear warping
    float distortComp = 1.0 + distort * 1.5; // distortion-aware scaling

    // === Final bias (tunable)
    float bias = max(0.003, 0.05 * slope * distortComp);

    // === Offset along normal (NOT lightDir, avoids acne on vertical blocks)
    p3 += normals * bias;

    // === Shadow Map Projection ===
    vec3 ProjShadowPos = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    ProjShadowPos = diagonal3(shadowProjection) * ProjShadowPos + shadowProjection[3].xyz;

    // === Shadow Distortion ===
    float distortFac = calcDistort(ProjShadowPos.xy);
    ProjShadowPos.xy *= distortFac;
    ProjShadowPos.z -= bias * 0.005; // fine-tune depending on PCF strength

    // === Subsurface Light Enhancement ===
    if (sssAmount > 0.0)
    {
        // How much light comes from the "behind" side of the block
        float backLit = max(dot(-lightPos, normals), 0.0);

        // How much light comes from the side (not top-down)
        float sideLit = 1.0 - abs(dot(normals, lightPos)); // max at 90°

        // Mix both with adjustable weights
        float sssBoost = mix(backLit, sideLit, 0.5) * sssAmount;

        // Blend it in if it helps
        diffuseSun = max(diffuseSun, sssBoost);
    }

    // === Shadow Bounds Check ===
    vec2 bounds = vec2(1.0) - (1.5 / shadowMapResolution);
    bool inBounds = all(lessThan(abs(ProjShadowPos.xy), bounds)) &&
                    (abs(ProjShadowPos.z) < 6.0) &&
                    (diffuseSun > 0.001);

    float shading = 1.0;

    if (inBounds)
    {
        // Normalize for shadow sampling
        ProjShadowPos = ProjShadowPos * vec3(0.5, 0.5, 1.0 / 12.0) + vec3(0.5);
        shading = 0.0;

#if SHADOW_FILTERING == 1
        float angle = noise * 6.2831853; // 2 * PI
        float ca = cos(angle), sa = sin(angle);
        mat2 rot = mat2(ca, -sa, sa, ca) * 0.001;

        // Unrolled 6-tap filter with rotation
        for (int i = 0; i < 6; ++i)
        {
            vec3 offset = vec3(rot * shadowOffsets[i], 0.0);
            shading += texture(shadow, ProjShadowPos + offset);
        }

        shading *= 1.0 / 6.0;
#else
        shading = texture(shadow, ProjShadowPos);
#endif

        // Blend day/night shadows
        shading = mix(1.0, shading, nightblendShadows);
    }

#if defined EXTENDED_SHADOWS || defined VOXY
    float distanceMeters = length(p3);
    float SSS = 0.0;
    float blendEdge = 16.0;
    float mixamount = 1.0 - clamp((min(shadowDistance, farorg) - distanceMeters) / blendEdge, 0.0, 1.0);
    #ifdef VOXY
    float vdepth = (texture(vxDepthTexOpaque, texcoord, 0).x);
    vec3 A = toScreenSpace_og(vec3(texcoord, vdepth));
    vec3 p32 = toWorldSpace(A);
    float eyeDist = max(length(p32.xz), abs(p32.y)) / far;
    bool check2 = (eyeDist < (1 / far) * (farorg - 16));

    bool isvanilla = (vdepth >= 1.0);
    if (check2 || !isvanilla)
        mixamount = 1;

    #endif

    vec2 shading2 = rayTraceShadowAlt(diffuseSun, worldToView(lightPos), fragpos, noise, (sssAmount > 0.0));
    shading = mix(shading, shading2.x, mixamount);
    if (sssAmount > 0.0)
    {
        float SSS2 = computeSSS(sssAmount, albedo, p3, lightPos, shading2.y, min(diffuseSun, shading)) * (lightmap.y * lightmap.y * lightmap.y * lightmap.y);

        SSS = SSS2;
    }
    float rainFactor = -0.9 * rainStrength + 1.0;
    float invSSS = -sssAmount + 1.0;

#endif
    // === Final Light Application ===
    float shadowed = min(diffuseSun, shading);
#if defined EXTENDED_SHADOWS || defined VOXY

    shadowed = mix(shadowed * invSSS, SSS, sssAmount);
#endif

    shadowed *= lightmap.y;
    shadowed = mix(0.0, shadowed, shadowblend);
    shadowed *= lightmap.y * (1.0 - rainStrength * 0.9);

    return shadowed;
}
