#ifdef GL_ES
#define LOWP lowp
#define MEDP mediump
#define HIGHP highp
precision mediump float;
#else
#define LOWP
#define MEDP
#define HIGHP
#endif

#ifdef normalFlag
varying vec3 v_normal;
#endif // normalFlag

#if defined(tangentFlag) && defined(binormalFlag)
varying mat3 v_tbn;
#endif // tangentFlag && bitangentFlag

#ifdef colorFlag
varying vec4 v_color;
#endif // colorFlag

#if defined(diffuseTextureFlag) || defined(normalTextureFlag) || defined(specularTextureFlag) || defined(emissiveTextureFlag)
#define textureFlag
#endif
#ifdef diffuseTextureFlag
varying MEDP vec2 v_diffuseUV;
uniform sampler2D u_diffuseTexture;
#endif // diffuseTextureFlag
#ifdef normalTextureFlag
varying MEDP vec2 v_normalUV;
uniform sampler2D u_normalTexture;
#endif // normalTextureFlag
#ifdef specularTextureFlag
varying MEDP vec2 v_specularUV;
uniform sampler2D u_specularTexture;
#endif // specularTextureFlag
/*
#ifdef emissiveTextureFlag
varying MEDP vec2 v_emissiveUV;
uniform sampler2D u_emissiveTexture;
#endif // emissiveTextureFlag
*/

#ifdef diffuseColorFlag
uniform vec4 u_diffuseColor;
#endif // diffuseColorFlag

#ifdef specularColorFlag
uniform vec4 u_specularColor;
#endif

#ifdef lightingFlag
varying vec3 v_lightDiffuse;

#ifdef shininessFlag
uniform float u_shininess;
#else
const float u_shininess = 20.0;
#endif // shininessFlag

#if numDirectionalLights > 0
struct DirectionalLight
{
	vec3 color;
	vec3 direction;
};
uniform DirectionalLight u_dirLights[numDirectionalLights];
#endif // numDirectionalLights

#if numPointLights > 0
struct PointLight
{
	vec3 color;
	vec3 position;
};
uniform PointLight u_pointLights[numPointLights];
#endif // numPointLights

#if	defined(ambientLightFlag) || defined(ambientCubemapFlag) || defined(sphericalHarmonicsFlag)
#define ambientFlag
varying vec3 v_ambient;
#endif // ambientFlag

#if defined(specularTextureFlag) || defined(specularColorFlag)
#define specularFlag
#endif

#endif // lightingFlag

#if (numPointLights > 0) || defined(specularFlag) || defined(fogFlag)
varying vec4 v_worldPosition;
uniform vec4 u_cameraPosition;
#endif // numPointLights > 0 || lightingFlag || fogFlag

#ifdef fogFlag
uniform vec4 u_fogColor;

#if defined(fogLinearFlag) || defined(fogSmoothFlag) || defined(fogInvSquareFlag)
uniform float u_fogNear;
uniform float u_fogFar;
#elif defined(fogExpFlag) || defined(fogExp2Flag)
uniform float u_fogDensity;
#endif

#endif // fogFlag

uniform mat4 u_projViewTrans;
uniform mat4 u_worldTrans;

vec3 linearEncode(vec3 v) { return vec3(pow(v.r, 2.2), pow(v.g, 2.2), pow(v.b, 2.2)); }
vec3 linearDecode(vec3 v) { return vec3(pow(v.r, 1.0 / 2.2), pow(v.g, 1.0 / 2.2), pow(v.b, 1.0 / 2.2)); }

void main()
{
#if defined(diffuseTextureFlag) && defined(diffuseColorFlag) && defined(colorFlag)
	vec4 diffuse = texture2D(u_diffuseTexture, v_diffuseUV) * u_diffuseColor * v_color;
#elif defined(diffuseTextureFlag) && defined(diffuseColorFlag)
	vec4 diffuse = texture2D(u_diffuseTexture, v_diffuseUV) * u_diffuseColor;
#elif defined(diffuseTextureFlag) && defined(colorFlag)
	vec4 diffuse = texture2D(u_diffuseTexture, v_diffuseUV) * v_color;
#elif defined(diffuseTextureFlag)
	vec4 diffuse = texture2D(u_diffuseTexture, v_diffuseUV);
#elif defined(diffuseColorFlag) && defined(colorFlag)
	vec4 diffuse = u_diffuseColor * v_color;
#elif defined(diffuseColorFlag)
	vec4 diffuse = u_diffuseColor;
#elif defined(colorFlag)
	vec4 diffuse = v_color;
#else
	vec4 diffuse = vec4(1.0);
#endif
	diffuse.rgb = linearEncode(diffuse.rgb);

#ifdef lightingFlag

#if defined(normalFlag) && defined(tangentFlag) && defined(binormalFlag) && defined(normalTextureFlag)
	vec3 normal = vec3(2.0 * texture2D(u_normalTexture, v_normalUV).rgb - 1.0);
	normal = normalize(v_tbn * normal);
#elif defined(normalFlag)
	vec3 normal = normalize(v_normal);
#endif // normalFlag

#ifdef ambientFlag
	vec3 accum = v_ambient;
#else // !ambientFlag
	vec3 accum = vec3(0.0);
#endif // ambientFlag

#ifdef specularFlag
	vec3 eyeVec = normalize(u_cameraPosition.xyz - v_worldPosition.xyz);
	vec3 specAccum = vec3(0.0);
#endif // specularFlag

#if defined(normalFlag) && (numDirectionalLights > 0)
	for (int i = 0; i < numDirectionalLights; ++i)
	{
		vec3 lightVec = -u_dirLights[i].direction;
		float lambert = dot(normal, lightVec);
		float phongTerm = max(lambert, 0.0);
		vec3 value = u_dirLights[i].color;
		value = linearEncode(value);
		value *= phongTerm;
		accum += value;
#ifdef specularFlag
		vec3 halfDir = normalize(lightVec + eyeVec);
		float specAngle = max(dot(halfDir, normal), 0.0);
		float specTerm = pow(specAngle, u_shininess);
		specAccum += value * specTerm;
#endif // specularFlag
	}
#endif // normalFlag && numDirectionalLights

#if defined(normalFlag) && (numPointLights > 0)
	for (int i = 0; i < numPointLights; ++i)
	{
		vec3 lightVec = u_pointLights[i].position - v_worldPosition.xyz;
		float lightDist2 = dot(lightVec, lightVec);
		vec3 lightDir = lightVec;
		lightDir *= inversesqrt(lightDist2);
		float lambert = dot(normal, lightDir);
		float phongTerm = max(lambert, 0.0);
		//float lightDist = sqrt(lightDist2);
		//float attenuation = clamp(1.0 - lightDist * lightDist / (2.0 * 2.0), 0.0, 1.0);
		//attenuation *= attenuation;
		float attenuation = 1.0 / (1.0 + lightDist2);
		vec3 value = u_pointLights[i].color;
		value = linearEncode(value);
		value *= phongTerm * attenuation;
		accum += value;
#ifdef specularFlag
		vec3 halfDir = normalize(lightDir + eyeVec);
		float specAngle = max(dot(halfDir, normal), 0.0);
		float specTerm = pow(specAngle, u_shininess);
		specAccum += value * specTerm;
#endif // specularFlag
	}
#endif

	vec3 fragment;
#ifdef specularFlag
#ifdef specularTextureFlag
	vec3 specularColorTex = texture2D(u_specularTexture, v_specularUV).rgb;
	specularColorTex = linearEncode(specularColorTex);
	specAccum *= specularColorTex;
#endif // specularTextureFlag
#ifdef specularColorFlag
	vec3 specularColor = u_specularColor.rgb;
	specularColor = linearEncode(specularColor);
	specAccum *= specularColor;
#endif // specularColorFlag
	fragment = diffuse.rgb * accum + specAccum;
#else // !specularFlag
	fragment = diffuse.rgb * accum;
#endif // specularFlag
#else // !lightingFlag
	fragment = diffuse.rgb;
#endif // lightingFlag

#ifdef fogOriginalFlag
	vec3 fogVec = u_cameraPosition.xyz - v_worldPosition.xyz;
	float fogDist = dot(fogVec, fogVec);
	float fog = min(fogDist * u_cameraPosition.w, 1.0);
#elif defined(fogDistanceFlag)
	float fogDist = length(v_worldPosition.xyz - u_cameraPosition.xyz);
#elif defined(fogDepthFlag)
	float fogDist = gl_FragCoord.z / gl_FragCoord.w;
#endif // fogOriginalFlag || fogDistanceFlag || fogDepthFlag

#ifdef fogLinearFlag
	// fog = saturate(linearstep(near, far, x))
	float fog = clamp((fogDist - u_fogNear) / (u_fogFar - u_fogNear), 0.0, 1.0);
#elif defined(fogSmoothFlag)
	// fog = smoothstep(saturate(linearstep(near, far, x)))
	float fog = clamp((fogDist - u_fogNear) / (u_fogFar - u_fogNear), 0.0, 1.0);
	fog = fog * fog * (3.0 - 2.0 * fog);
	//fog = fog * fog * fog * (fog * (6.0 * fog - 15.0) + 10.0);
#elif defined(fogInvSquareFlag)
	// fog = isqstep(saturate(linearstep(near, far, x)))
	float fog = clamp((fogDist - u_fogNear) / (u_fogFar - u_fogNear), 0.0, 1.0);
	fog = 1.0 - fog;
	fog = 1.0 - fog * fog;
#elif defined(fogExpFlag)
	// todo: can precompute some stuff in uniforms
	float fog = max(1.0 - exp(-u_fogDensity * fogDist), 0.0);
#elif defined(fogExp2Flag)
	// todo: can precompute some stuff in uniforms
	float dz = u_fogDensity * fogDist;
	float fog = max(1.0 - exp(-dz * dz), 0.0);
#endif // fogLinearFlag || fogSmoothFlag || fogInvSquareFlag || fogExpFlag || fogExp2Flag

#ifdef fogFlag
	vec3 fogColor = u_fogColor.rgb;
	fogColor = linearEncode(fogColor);
	fragment = mix(fragment, fogColor, fog);
#endif // fogFlag

	gl_FragColor = vec4(linearDecode(fragment), diffuse.a);
}
