varying vec3 position;
varying vec3 normal;

varying vec4 shadowCoord1;
varying vec4 shadowCoord2;
varying vec4 shadowCoord3;

uniform sampler2D diffuseTexture;

uniform sampler2DShadow shadowMap1;
uniform sampler2DShadow shadowMap2;
uniform sampler2DShadow shadowMap3;

uniform vec2 shadowMapSize; 

uniform bool usePCF;
uniform bool drawCascades;

float shadowLookup(sampler2DShadow shadowMap, vec4 coord, vec2 offset)
{
    vec2 texelSize = 1.0 / shadowMapSize;
    vec2 v = offset * texelSize * coord.w;
    return shadow2DProj(shadowMap, coord + vec4(v.x, v.y, 0.0, 0.0)).r;
}

float weight(vec4 tc)
{
    vec2 proj = vec2(tc.x / tc.w, tc.y / tc.w);
    proj = (1.0 - abs(proj * 2.0 - 1.0)) * 8.0;
    proj = clamp(proj, 0.0, 1.0);
    return min(proj.x, proj.y);
}

float pcf(sampler2DShadow shadowMap, vec4 coord, float radius)
{
    float s = 0.0;
    float x, y;
    for (y = -radius ; y < radius ; y += 1.0)
    for (x = -radius ; x < radius ; x += 1.0)
    {
        s += shadowLookup(shadowMap, coord, vec2(x, y));
    }
    s /= radius * radius * 4.0;
    return s;
}

const vec3 c_white = vec3(1.0, 1.0, 1.0);
const vec3 c_red   = vec3(1.0, 0.0, 0.0);
const vec3 c_green = vec3(0.0, 1.0, 0.0);
const vec3 c_blue  = vec3(0.0, 0.0, 1.0);

void main()
{
    vec3 tex = texture2D(diffuseTexture, gl_TexCoord[0].st).rgb;
    
    float s1 = 1.0;
    float s2 = 1.0;
    float s3 = 1.0;

    if (usePCF)
    {
        s1 = pcf(shadowMap1, shadowCoord1, 3.0);
        s2 = pcf(shadowMap2, shadowCoord2, 2.0);
        s3 = pcf(shadowMap3, shadowCoord3, 2.0);
    }
    else
    {
        s1 = shadow2DProj(shadowMap1, shadowCoord1).r;
        s2 = shadow2DProj(shadowMap2, shadowCoord2).r;
        s3 = shadow2DProj(shadowMap3, shadowCoord3).r;
    }

    float w1 = weight(shadowCoord1);
    float w2 = weight(shadowCoord2);
    float w3 = weight(shadowCoord3);
    
    s3 = mix(1.0, s3, w3);
    s2 = mix(s3, s2, w2);
    s1 = mix(s2, s1, w1);
    
    // Cascade visualization
    vec3 c3 = mix(c_white, c_blue, w3);
    vec3 c2 = mix(c3, c_green, w2);
    vec3 c1 = mix(c2, c_red, w1);
    
    vec3 N = normalize(normal);
    vec3 L = gl_LightSource[0].position.xyz;
    float diffuse = clamp(dot(N, L), 0.0, 1.0);
    
    float linearDepth = abs(position.z);
    float fogFactor = clamp((gl_Fog.end - linearDepth) / (gl_Fog.end - gl_Fog.start), 0.0, 1.0);
    
    const vec3 ambientColor = vec3(0.4, 0.4, 0.5);
    
    vec3 color = drawCascades? c1 * s1 : mix(gl_Fog.color.rgb, mix(tex * ambientColor, tex, diffuse * s1), fogFactor);
    
    gl_FragColor = vec4(color, 1.0);
}
