varying vec3 position;
varying vec3 n, e;

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

uniform sampler2D diffuseMap;

uniform sampler2D normalMap;
uniform bool haveNormalMap;

uniform sampler2D lightMap;
uniform bool haveLightMap;

uniform sampler2DShadow shadowMap1;
uniform sampler2DShadow shadowMap2;
uniform sampler2DShadow shadowMap3;
uniform float shadowMapSize;
uniform bool useShadowMap;

mat3 cotangentFrame(vec3 N, vec3 p, vec2 uv)
{
    vec3 dp1 = dFdx(p);
    vec3 dp2 = dFdy(p);
    vec2 duv1 = dFdx(uv);
    vec2 duv2 = dFdy(uv);
    vec3 dp2perp = cross(dp2, N);
    vec3 dp1perp = cross(N, dp1);
    vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
    vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
    float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
    return mat3(T * invmax, B * invmax, N);
}

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

float pcf(sampler2DShadow depths, 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(depths, coord, vec2(x, y));
    }
	s /= radius * radius * 4.0;
    return s;
}

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);
}

void main()
{
    vec3 N = normalize(n);
    vec3 E = normalize(e);
    vec3 L = gl_LightSource[0].position.xyz;
    
    vec2 texCoords0 = gl_TexCoord[0].st;
    vec2 texCoords1 = gl_TexCoord[1].st;
    
    mat3 TBN = cotangentFrame(N, position, texCoords0);
    
    if (haveNormalMap)
    {
        vec3 tN = normalize(texture2D(normalMap, texCoords0).rgb * 2.0 - 1.0);
        N = normalize(TBN * tN);
    }

    vec4 diffuseTex = texture2D(diffuseMap, texCoords0);
    vec4 lightTex = haveLightMap? texture2D(lightMap, texCoords1) : vec4(1.0, 1.0, 1.0, 1.0);
    
    float s1 = 1.0;
    
    if (useShadowMap)
    {
        s1 = pcf(shadowMap1, shadowCoord1, 3.0);
        float s2 = pcf(shadowMap2, shadowCoord2, 2.0);
        float s3 = pcf(shadowMap3, shadowCoord3, 2.0);
        
        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);
    }
    float diffuse = clamp(dot(N, L), 0.0, 1.0);
    
    vec3 H = normalize(L + E);
    float NH = dot(N, H);
    float specular = pow(max(NH, 0.0), gl_FrontMaterial.shininess);
    
    const vec3 shadowColor = vec3(0.3, 0.5, 0.7);
    const float shadowBrightness = 0.7;
    
    vec3 shading = mix(lightTex.rgb * shadowColor * shadowBrightness, lightTex.rgb + gl_FrontMaterial.specular * specular, s1 * diffuse);
    vec3 normalColor = diffuseTex.rgb * shading;

    vec4 objColor = vec4(normalColor, diffuseTex.a);

    float fogDistance = gl_FragCoord.z / gl_FragCoord.w;
    float fogFactor = clamp((gl_Fog.end - fogDistance) / (gl_Fog.end - gl_Fog.start), 0.8, 1.0);
        
    gl_FragColor = mix(gl_Fog.color, objColor, fogFactor);
    gl_FragColor.a = diffuseTex.a;
}
