precision mediump float;

uniform sampler2D Texture;
uniform sampler2D uShadowMap;
uniform vec3 uLightDir;

varying vec2 texCoord;
varying vec4 fragPosLightSpace;
varying vec3 fragNormal;

float computeShadow(vec4 lightSpacePos, vec3 normal)
{
	vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
	projCoords = projCoords * 0.5 + 0.5;

	if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
		projCoords.y < 0.0 || projCoords.y > 1.0 ||
		projCoords.z > 1.0)
	{
		return 0.0;
	}

	float currentDepth = projCoords.z;

	float bias = 0.0004;
	if (dot(normal, normal) > 0.001)
	{
		float cosTheta = dot(normalize(normal), -normalize(uLightDir));
		bias = max(0.002 * (1.0 - cosTheta), 0.0002);
	}

	float shadow = 0.0;
	float texelSize = 1.0 / 2048.0;
	for (int x = -1; x <= 1; x++)
	{
		for (int y = -1; y <= 1; y++)
		{
			float pcfDepth = texture2D(uShadowMap, projCoords.xy + vec2(float(x), float(y)) * texelSize).r;
			shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
		}
	}
	shadow /= 9.0;

	return shadow;
}

void main()
{
	vec4 color = texture2D(Texture, texCoord);

	float ambient = 0.4;
	float diffuseStrength = 0.6;

	float diffuse = 1.0;
	vec3 n = fragNormal;
	if (dot(n, n) > 0.001)
	{
		n = normalize(n);
		diffuse = max(dot(n, -normalize(uLightDir)), 0.0);
	}

	float shadow = computeShadow(fragPosLightSpace, fragNormal);

	float lighting = ambient + diffuseStrength * diffuse * (1.0 - shadow);
	color.rgb *= lighting;

	gl_FragColor = color;
}
