/*
====================================================================
  SXGG: Realis — Shader ultra-realistica per Minecraft
  Autore : Samax.GG
  File   : lib/shadows.glsl
  Modulo : 2 — Soft Shadows (PCF) + ombre colorate

  Design:
  - Distorsione della shadow map: più risoluzione vicino alla camera
    (la stessa funzione DEVE essere usata in shadow.vsh)
  - PCF a rotazione casuale per penombra morbida senza pattern
  - Penombra crescente con la distanza dall'occlusore (contact-hardening
    approssimato dal bias della distanza)
  - shadowcolor0 per le ombre colorate (vetro, acqua)

  Richiede uniform: shadowtex0, shadowtex1, shadowcolor0,
                    shadowModelView, shadowProjection
====================================================================
*/

#ifndef SHADOWS_GLSL
#define SHADOWS_GLSL

const int   shadowMapResolution = 2048;   // [1024 2048 4096]
const float shadowDistance      = 128.0;  // [96.0 128.0 192.0 256.0]
const float SHADOW_BIAS = 0.0009;

// --- Distorsione: concentra i texel della shadow map vicino al player ---
vec2 distortShadow(vec2 pos) {
    float factor = length(pos) * 0.9 + 0.1;
    return pos / factor;
}

vec3 distortShadowPos(vec3 shadowPos) {
    return vec3(distortShadow(shadowPos.xy), shadowPos.z * 0.2);
}

// --- Rotazione casuale per i tap PCF (elimina il banding) ---
mat2 randomRotation(vec2 screenCoord) {
    float angle = ign(screenCoord) * TAU;
    float c = cos(angle), s = sin(angle);
    return mat2(c, -s, s, c);
}

// Poisson disk 16 tap
const vec2 POISSON[16] = vec2[16](
    vec2(-0.94201624, -0.39906216), vec2( 0.94558609, -0.76890725),
    vec2(-0.09418410, -0.92938870), vec2( 0.34495938,  0.29387760),
    vec2(-0.91588581,  0.45771432), vec2(-0.81544232, -0.87912464),
    vec2(-0.38277543,  0.27676845), vec2( 0.97484398,  0.75648379),
    vec2( 0.44323325, -0.97511554), vec2( 0.53742981, -0.47373420),
    vec2(-0.26496911, -0.41893023), vec2( 0.79197514,  0.19090188),
    vec2(-0.24188840,  0.99706507), vec2(-0.81409955,  0.91437590),
    vec2( 0.19984126,  0.78641367), vec2( 0.14383161, -0.14100790)
);

/*
  Ombra morbida PCF con supporto colori.
  shadowPos : posizione già in shadow clip space [0,1], distorta
  Ritorna rgb: 1 = luce piena, 0 = ombra piena, colorato se filtrata
  da vetro/acqua.
*/
vec3 softShadow(vec3 shadowPos, vec2 screenCoord, float NdotL) {
    // Bias in base all'angolo: superfici radenti richiedono più bias
    float bias = SHADOW_BIAS * (2.0 - NdotL) * (2048.0 / float(shadowMapResolution));
    float depthCompare = shadowPos.z - bias;

    mat2 rot = randomRotation(screenCoord);
    float radius = SHADOW_SOFTNESS * 1.2 / float(shadowMapResolution);

    vec3 shadow = vec3(0.0);

    for (int i = 0; i < SHADOW_SAMPLES; i++) {
        // Indice ciclico sul disco Poisson (supporta SHADOW_SAMPLES > 16:
        // la rotazione casuale per-pixel decorre i tap ripetuti)
        vec2 offset = rot * POISSON[int(mod(float(i), 16.0))] * radius;
        vec2 tap    = shadowPos.xy + offset;

        // shadowtex0: tutto | shadowtex1: solo opachi
        float solid = step(depthCompare, texture2D(shadowtex1, tap).r);

        #ifdef COLORED_SHADOWS
            float all = step(depthCompare, texture2D(shadowtex0, tap).r);
            if (all < solid) {
                // Bloccato solo da trasparenti: tinta dal loro colore
                vec4 caster = texture2D(shadowcolor0, tap);
                shadow += solid * mix(vec3(1.0), caster.rgb, caster.a * 0.85);
            } else {
                shadow += vec3(solid);
            }
        #else
            shadow += vec3(solid);
        #endif
    }

    return shadow / float(SHADOW_SAMPLES);
}

#endif // SHADOWS_GLSL
