#version 330

uniform mat4 WorldView;
uniform vec4 BaseColor;
uniform float Diffuse;
uniform vec4 Ambient;
uniform vec4 Specular;
uniform float SpecularPower;
uniform float Reflection;
uniform vec4 Emissive;
uniform bool Blinn;
uniform vec3 GlobalAmbient;
uniform vec4 BackFaceColor;
uniform int LightNum;
uniform vec4 LightPos[4];
uniform vec3 LightCol[4];
uniform vec3 LightSpc[4];
uniform vec3 ViewDir;
uniform vec3 CameraPos;
uniform int ClipPlaneNum;
uniform vec4 ClipPlane[4];
uniform int HasTextureMap;
uniform int HasAlphaMap;
uniform int HasEnvMap;
uniform float EnvMapRotate;

uniform sampler2D ColorMapSampler;
uniform sampler2D AlphaMapSampler;
uniform sampler2D EnvMapSampler;
uniform sampler2D EnvDiffuseMapSampler;

in vec3 VSWorldPos;
in vec4 VSColor;
in vec3 VSNormal;
in vec2 VSTexCoord;
#if MULTIUV >= 1
in vec2 VSTexCoord1;
#endif
#if MULTIUV >= 2
in vec2 VSTexCoord2;
#endif
#if MULTIUV >= 3
in vec2 VSTexCoord3;
#endif

out vec4 OutColor;

float PI = 3.141592653589793;
vec3 mul(vec3 v, mat3 m) { return m * v; }
float saturate(float v) { return clamp(v, 0, 1); }
vec2 saturate(vec2 v) { return clamp(v, 0, 1); }
vec3 saturate(vec3 v) { return clamp(v, 0, 1); }
vec4 saturate(vec4 v) { return clamp(v, 0, 1); }
vec3 lerp(vec3 x, vec3 y, float a) { return mix(x, y, a); }
float atan2(float y, float x) { return x == 0.0 ? sign(y)*PI/2 : atan(y, x); }

vec2 GetTexCoord(int channel)
{
#if MULTIUV >= 1
	if((channel & 0x30) == 0x10) return VSTexCoord1;
#endif
#if MULTIUV >= 2
	if((channel & 0x30) == 0x20) return VSTexCoord2;
#endif
#if MULTIUV >= 3
	if((channel & 0x30) == 0x30) return VSTexCoord3;
#endif
	return VSTexCoord;
}

vec2 dirToUV(vec3 dir)
{
	return vec2(
		atan2(dir.z, dir.x) / (PI*2),
		acos(dir.y) / PI);
}

vec2 envmapUV(vec3 dir)
{
	return dirToUV(dir) + vec2(EnvMapRotate, 0.0);
}

vec4 LINEARtoSRGB(vec4 srgbIn, bool is_srgb)
{
	if(is_srgb){
		vec3 linOut = pow(abs(srgbIn.xyz),vec3(1.0/2.2,1.0/2.2,1.0/2.2));
		return vec4(linOut,srgbIn.w);
	}else{
		return srgbIn;
	}
}

float GetSpecular(vec3 V, vec3 nv, vec3 light_dir)
{
	if(Blinn){
		vec3 Reflect = normalize(V + light_dir);
		return pow(saturate(dot(Reflect, nv)), SpecularPower);
	}else{
		vec3 Reflect = normalize(2 * dot(nv, light_dir) * nv - light_dir);
		return pow(saturate(dot(Reflect, V)), SpecularPower);
	}
}

// phong with texture
void main(void)
{
	for(int i=0; i<ClipPlaneNum; i++){
		if(dot(ClipPlane[i].xyz, VSWorldPos) + ClipPlane[i].w < 0)
			discard;
	}
	vec3 V = normalize(CameraPos - VSWorldPos);
	vec4 tex_col = LINEARtoSRGB(texture(ColorMapSampler, GetTexCoord(HasTextureMap)), (HasTextureMap&3) == 2);
	vec4 alpha_col = texture(AlphaMapSampler, GetTexCoord(HasAlphaMap));

	vec4 col = tex_col * VSColor;
	col.w *= alpha_col.w;
	if(col.w < 0.01)
		discard;

	vec3 nv = normalize(VSNormal);
	if(gl_FrontFacing){
		col.xyz *= BackFaceColor.xyz;
		nv = -nv;
	}
	vec3 dif = vec3(0,0,0);
	vec3 spc = vec3(0,0,0);
	for(int i=0; i<4 && i<LightNum; i++){
		vec3 light_dir;
		if(LightPos[i].w == 0){
			light_dir = normalize(LightPos[i].xyz - VSWorldPos); // point light
		}else{
			light_dir = LightPos[i].xyz; // directional light
		}
		dif += LightCol[i] * saturate(dot(nv, light_dir));
		spc += LightSpc[i] * GetSpecular(V, nv, light_dir);
	}
	col.xyz = col.xyz * (Emissive.xyz + Diffuse * dif) + Specular.xyz * spc;
	
	if(HasEnvMap != 0){
		col.xyz += Ambient.xyz * LINEARtoSRGB(texture(EnvDiffuseMapSampler, envmapUV(nv)), HasEnvMap == 2).xyz;
	}else{
		col.xyz += Ambient.xyz * GlobalAmbient;
	}

	if(Reflection != 0 && HasEnvMap != 0){
		vec3 refv = -normalize(reflect(V, nv));
		col.xyz = lerp(col.xyz, LINEARtoSRGB(texture(EnvMapSampler, envmapUV(refv)), HasEnvMap == 2).xyz, Reflection);
	}
	OutColor = saturate(col);
}

