#version 120
const int nLightCount = 2;
const float pi = 3.14159265358;

uniform sampler2D diffuse, normalAOlights, shield, masks;
uniform samplerCube skybox;
uniform vec4 ownerColor;
uniform vec3 glowColor;
uniform float thrust;
uniform float camDist;
uniform float time, nodeScale;

float teamNumber = 5;

//varying vec4 shadowCoord[nLightCount];
varying vec3 forward, right;
varying vec3 normal, binormal, tangent;
varying vec3 npos;
varying vec2 uv, uv2, uv3;
varying vec4 vertPaint;

vec3 light[nLightCount];
float dist[nLightCount];
/*
//Skybox sampling call
vec2 skyboxSample(vec3 dir) {
	float x = dot(dir, forward);
	float z = dot(dir, right);
	float y = dot(dir, cross(forward,right));
	
	return vec2(atan(-x,z) / 6.28318530718, (y + 1.0) * 0.5);
}
*/
//Engine heat color call
float mixRange(float x, float low, float hi) {
	return clamp((x - low) / (hi - low), 0.0, 1.0);
}

vec3 blackBody(float temp) {
	vec3 c;
	c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
	c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
	if(temp < 6600.0)
		c.g = mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0));
	else
		c.g = mix(0.976, 0.75, mixRange(temp, 6600.0, 29800.0));
	return c;
}

float D_GGX(float HdotN, float Roughness)
{
    float m = Roughness * Roughness;
    float m2 = m * m;
    float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
    float D = m2 /( pi * denominator * denominator);
    return D;
}

// Schlick GGX approximation
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
{
    float k = Roughness * Roughness * 0.5f;
    float G1V = NdotV * (1.0 - k) + k;
    float G1L = NdotL * (1.0 - k) + k;
    return 0.25f / (G1V * G1L);
}

vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
{
	return substance + (1.0 - substance) * pow(1.0 - clamp((dot(l, h)), 0.0, 1.0), 5.0);
}

vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
{
	return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
}

float RoughToSPow(float fRoughness)
{
   return (2.0 / (fRoughness * fRoughness)) - 2.0;
}

const float k0 = 0.00098f, k1 = 0.9921f;
const float g_fMaxT = (exp2(-10.0 / sqrt((2.0 / (0.0014f * 0.0014f)) - 2.0)) - 0.00098f) / 0.9921f;

float GetSpecPowToMip(float fSpecPow, int nMips)
{
   float fSmulMaxT = (exp2( -10.0 / sqrt(fSpecPow)) - k0) / k1;

   return float(nMips - 1) * (1.0 - clamp(fSmulMaxT / g_fMaxT, 0.0, 1.0 ));
}

void main() {
	vec4 diffuse = texture2D(diffuse, uv.xy);
	vec4 normAO = texture2D(normalAOlights, uv.xy);
	vec3 maskSamp = texture2D(masks, uv.xy).rgb;
	float paintSamp = texture2D(masks, uv2.xy).w;
	float flagSamp = texture2D(masks, uv2.xy + vec2((1.0/32.0) * teamNumber, 0.0)).w;
	vec3 vertMasksPrimary = vertPaint.rgb; // .a unused
	//float texSamp7 = texture2D(shield, uv2.xy * 10);
	//float texSamp8 = texture2D(shield, uv2.xy - time * 80.0);
	float texSamp5 = texture2D(shield, uv2.xy - 8.0 * 24.0).r;
	float texSamp6 = texture2D(shield, uv2.xy + time * 24.0).g;
	
	// Normal map setup
	vec3 normMap = normAO.xyz * 2.0 - vec3(1.0);
	
	// Vertex masks setup
	vec3 vertMasksSecondary = (clamp(vertMasksPrimary, 0.5, 1.0) - 0.5) * 2.0;	//  shield r, windows g, engine b
	vertMasksPrimary = clamp(((1.0 - vertMasksPrimary) - 0.5)* 2.0, 0.0, 1.0); // flags g, warp b, transparency on r

	// Texture masks setup	
	float paintMask = (flagSamp * vertMasksPrimary.g + paintSamp * (1.0-vertMasksPrimary.g)) * maskSamp.b;
	float plateMask = maskSamp.b * (1.0-paintMask);	
	float roughnessTweakMask = plateMask * (1.0-paintMask);
	
	float lightsOne = (clamp(1.0 - normAO.w, 0.5, 1.0) - 0.5) * 2.0; 
	float lightsTwo = (clamp(normAO.w, 0.5, 1.0) - 0.5) * 2.0;
	
	// Emissive lights setup
	vec3 lights = vec3(0.0);
	
	float lightMask = 1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b; // filter for no-special lights

	// Primary and secondary lights are added - should be selectable, as owner and inverted owner is not always nice
	lights += vec3(lightsOne * lightMask * ownerColor.rgb * 2.0);
	lights += vec3(lightsTwo * lightMask * (1.0 - ownerColor.rgb) * 2.0);

	// Warp drive is added - currently no models contains the mask.
	float engineGlow = texSamp5 * texSamp6;
//	lights += vec3(2.0, 0.0, 2.0) * (1.0 + engineGlow) * lightsTwo * vertMasksPrimary.b;
	
	// Windows are added
	lights += glowColor * vertMasksSecondary.g * lightsOne;
	
	// Engine animation is created
	engineGlow = lightsOne * (/*lightsOne * 2.0 + */engineGlow * 2.0) * vertMasksSecondary.b;	
	
	// Gives nice intensity and falloff transitions.
	lights = pow(lights + (lightsOne + lightsTwo) * 1.0, vec3(1.5)) * (1.0 - vertMasksPrimary.r);

	// Transparency setup
	float transparency = min(clamp((clamp(diffuse.w,0.25, 0.5) - 0.25), 0.0, 0.25) * 4 + 0.25, 1.0-vertMasksPrimary.r);
	
	// Transparency and ao extractionsetup
	float ao = (1.0-((1.0-transparency) * 0.5 + diffuse.w))*2.0;
		
	// Engine setup
	float thrustIntensity = pow(thrust * 1.0, 2.0) * engineGlow;
	lights += (blackBody(1000.0 + 5000.0 * thrustIntensity) * thrustIntensity);

	
// Player color setup - move the PBR values to external source!
//	float playerMetal = 0.01;												//PBR gold
//	vec3 playerMetalCol = vec3(1.0, 0.77, 0.33); 							//PBR gold 
//	float playerMetal = 0.25;												//PBR bronze
//	vec3 playerMetalCol = vec3(0.99, 0.64, 0.34); 							//PBR bronze 
//	float playerMetal = 0.13;												//PBR brass
//	vec3 playerMetalCol = vec3(0.98, 0.87, 0.41); 							//PBR brass 
//	float playerMetal = 0.35;												//PBR Copper
//	vec3 playerMetalCol = vec3(0.98, 0.60, 0.52); 							//PBR Copper
//	float playerMetal = 0.10;												//PBR Silver
//	vec3 playerMetalCol = vec3(0.97, 0.96, 0.91); 							//PBR Silver
//	float playerMetal = 0.01;												//PBR Chrome
//	vec3 playerMetalCol = vec3(0.91, 0.95, 0.97); 							//PBR Chrome
//	float playerMetal = 0.45;												//PBR Titanium
//	vec3 playerMetalCol = vec3(0.54, 0.49, 0.46); 							//PBR Titanium
//	float playerMetal = 0.15;												//PBR Bright Steel
//	vec3 playerMetalCol = vec3(0.84, 0.85, 0.90); 							//PBR Bright Steel
//	float playerMetal = 0.75;												//PBR Dull Steel
//	vec3 playerMetalCol = vec3(0.56, 0.57, 0.58); 							//PBR Dull Steel
//	float playerMetal = 0.25;												//PBR Dark Steel
//	vec3 playerMetalCol = vec3(0.48, 0.45, 0.42) ; 							//PBR Dark Steel
//	float playerMetal = 0.27;												//PBR Gallium
//	vec3 playerMetalCol = vec3(0.88, 0.93, 0.56); 							//PBR Gallium
//	float playerMetal = 0.35;												//PBR Molybdenum
//	vec3 playerMetalCol = vec3(0.16, 0.22, 0.15); 							//PBR Molybdenum
//	float playerMetal = 0.33;												//PBR Niobium
//	vec3 playerMetalCol = vec3(0.38, 0.34, 0.62); 							//PBR Niobium
//	float playerMetal = 0.35;												//PBR Vanadium
//	vec3 playerMetalCol = vec3(0.08, 0.10, 0.09); 							//PBR Vanadium
//	float playerMetal = 0.25;												//PBR Adamantium
//	vec3 playerMetalCol = vec3(0.26, 0.18, 0.25); 							//PBR Adamantium
//	float playerMetal = 0.07;												//PBR Tritinium
//	vec3 playerMetalCol = vec3(0.54, 0.49, 0.62); 							//PBR Tritinium
//	float playerMetal = 0.03;												//PBR Zentronium
//	vec3 playerMetalCol = vec3(0.96, 0.48, 0.43); 							//PBR Zentronium
//	float playerMetal = 0.16;												//PBR Xintinium
//	vec3 playerMetalCol = vec3(0.48, 0.96, 0.96); 							//PBR Xintinium
//	float playerMetal = 0.15;												//PBR Duranium
//	vec3 playerMetalCol = vec3(0.94, 0.24, 0.02); 							//PBR Duranium
//	float playerMetal = 1.0;												//PBR Carbon Fibre 	Complimentary
//	vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.25;					//PBR Carbon Fibre 	Complimentary
//	float playerMetal = 0.9;												//PBR Ceramic 		Complimentary
//	vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.64 + vec3(0.32);		//PBR Ceramic 		Complimentary
//	float playerMetal = 1.0;												//PBR Paint 		Complimentary
//	vec3 playerMetalCol = vec3(1.0 - ownerColor);							//PBR Paint			Complimentary
	float playerMetal = 0.15;												//PBR Metal 		Complimentary
	vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.5 + vec3(0.5);			//PBR Metal			Complimentary

	vec4 playerPlateProfile = vec4(playerMetalCol, playerMetal);

	// PBR mixer
	playerMetal = clamp(playerMetal, 0.001, 1.0) * plateMask;
	vec3 paintColor = ownerColor.rgb * (1.0-paintMask);
	float metalness = clamp(maskSamp.g * max(0.5, 1.0-paintMask), 0.0, 1.0);
	
	// Roughness setup
	float orgRoughness = maskSamp.r * (1.0-plateMask) + plateMask * (plateMask * playerPlateProfile.w);
	

	// Albedo setup
	vec3 albedo = mix(diffuse.rgb * plateMask * playerMetalCol, ownerColor.rgb * diffuse.rgb, paintMask);
	albedo = mix(diffuse.rgb, albedo, maskSamp.b);

	// Shield setup.	
	lights *= (1.0 - vertMasksSecondary.r);
	//vertMasksSecondary.r * (1.0 - abs(texSamp5 * texSamp6 * 2.0 - 1.0)) * vec3(0.33, 0.99, 2.0);
	//	transparency *= vertMasksSecondary.r * (1.0 - abs(texSamp5 * texSamp6 * 2.0 - 1.0)) * 0.66;
	transparency *= clamp(1.0 - vertMasksSecondary.r  * 0.5, 0.0, 1.0);
	transparency += (texSamp5 * texSamp6);// * vertMasksSecondary.r;
	lights += transparency * vec3(0.33, 0.99, 2.0) * vertMasksSecondary.r;
	//Adjust all other PBR textures accordingly
	ao *= 1.0 - vertMasksSecondary.r;
	orgRoughness *= 1.0 - vertMasksSecondary.r;
	metalness *= 1.0 - vertMasksSecondary.r;
	albedo *= 1.0-vertMasksSecondary.r;
	normMap = normalize(mix(normMap, vec3(0.0,0.0,1.0), vertMasksSecondary.r));
	
	// Normal setup
	vec3 n = normalize(normal) * normMap.z;
	n += normalize(binormal) * normMap.y;
	n += normalize(tangent) * normMap.x;
	n = normalize(n);
	vec3 v = normalize(npos);
	vec3 r = normalize(reflect(-v, n));

	// Substance setup and Albedo adjust
	vec3 substance = clamp((0.04 - 0.04 * metalness) + albedo * metalness,0.0, 1.0);
	albedo -= substance;

	// Light model specials setup
	float NdotV = clamp(dot(n, v), 0.0, 1.0);
	float roughnessV = (orgRoughness + 1.0)/2.0; // roughness remapping

	if(nLightCount > 0) {
		const int i = 0;
		light[i] = gl_LightSource[i].position.xyz + npos;
		dist[i] = length(light[i]);
		light[i] = light[i] / dist[i];
	}
	
	if(nLightCount > 1) {
		const int i = 1;
		light[i] = gl_LightSource[i].position.xyz + npos;
		dist[i] = length(light[i]);
		light[i] = light[i] / dist[i];
	}
	
	//Zoom-out highlight setup
	float highlight = 0.0;
	if(camDist > 1.0)
		highlight += smoothstep(0.0, 500.0, camDist / sqrt(nodeScale)) * 0.2;
	vec3 color = vec3(highlight);

	// Ambient reflections
	float fakeLysSpecularPower=RoughToSPow(orgRoughness);
	float lysMipMap = GetSpecPowToMip(fakeLysSpecularPower,8);
	color += textureCube(skybox, r, lysMipMap).rgb * Fresnel2(substance,NdotV,orgRoughness);
	
// use this if you want a cheaper reflection cubemap calculation. If so, remember to remove the GetSpecPowToMip() and RoughToSPow()
// 	color += textureCube(skybox, r,8.0 - (8.0*(1.0-orgRoughness))).rgb * reflectionFresnel;   	

	// Ambient light
	color += textureCube(skybox, n, 7.0).rgb * albedo * (1.0 - Fresnel2(substance,NdotV,orgRoughness));

	if(nLightCount > 0) {
		//light
		const int i = 0;
//		float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
//		float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
		// fakes disc lights up close to a star.
		float falloff = 1.0 / (0.25 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
		float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
		
		float intensity = falloff * NdotL;

		vec3 L=light[i];
		L = normalize(L);
	
		vec3 VplusL = L + v; 
		vec3 halfVec = normalize(VplusL);

		float HdotN = clamp(dot(halfVec,n), 0.0, 0.98);
	
		vec3 F = Fresnel(substance, L, halfVec);
		float D = D_GGX(HdotN, roughnessV);
		float V = V_SchlickforGGX(roughnessV, NdotV, NdotL);
	
		color += ao * ((D * V * F) + (1.0 - F) * NdotL * albedo) * gl_LightSource[i].diffuse.rgb * intensity;
		
	}

	if(nLightCount > 1) {
		//light
		const int i = 1;
//		float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
//		float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
		// fakes disc lights up close to a star.
		float falloff = 1.0 / (0.25 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
		float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);

		float intensity = falloff * NdotL;

		vec3 L=light[i];
		L = normalize(L);
	
		vec3 VplusL = L + v;
		vec3 halfVec = normalize(VplusL);

		float HdotN = clamp(dot(halfVec,n), 0.0, 1.0);
	
		vec3 F = Fresnel(substance, L, halfVec);
		float D = D_GGX(HdotN, roughnessV);
		float V = V_SchlickforGGX(roughnessV, NdotV, NdotL);
	
		color += ao * ((D * V * F) + (1.0 - F) * NdotL * albedo) * gl_LightSource[i].diffuse.rgb * intensity;
	}
	gl_FragColor.rgb = color + lights;
	gl_FragColor.a = transparency + 1.0;
}
