#version 330
/*
  Copyright(C) 2018-2023, tetraface Inc. All rights reserved.
*/
/*
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/
*/

uniform mat4 WorldView;
uniform mat4 WorldViewProj;
uniform mat4 ModelMatrix;
uniform int ColorSpace;
uniform int DoubleSided;
uniform vec4 BaseColor;
uniform float Metallic;
uniform float Roughness;
uniform int AlphaMode;
uniform float AlphaCutOff;
uniform int TransparentWithZWrite;
uniform int RenderQueueOffsetNumber;
uniform vec4 ShadeColorFactor;
uniform float ShadingShiftFactor;
uniform float ShadingToonyFactor;
uniform float GiEqualizationFactor;
uniform vec4 Emissive;
uniform float RimLightingMixFactor;
uniform vec4 ParametricRimColorFactor;
uniform float ParametricRimFresnelPowerFactor;
uniform float ParametricRimLiftFactor;
uniform int OutlineWidthMode;
uniform float OutlineWidthFactor;
uniform vec4 OutlineColorFactor;
uniform float OutlineLightingMixFactor;
uniform float UvAnimationScrollXSpeedFactor;
uniform float UvAnimationScrollYSpeedFactor;
uniform float UvAnimationRotationSpeedFactor;

uniform int HasBumpMap;
uniform vec3 NormalMapFlip;
uniform float NormalScale;
uniform int HasEmissiveMap;
uniform int HasShadeMultiplyMap;
uniform int HasShadingShiftMap;
uniform float ShadingShiftScale;
uniform int HasMatcapMap;
uniform vec4 MatcapFactor;
uniform int HasRimMultiplyMap;
uniform int HasOutlineWidthMultiplyMap;
uniform int HasUvAnimationMaskMap;

uniform int LightNum;
uniform vec4 LightPos[4];
uniform vec3 LightCol[4];
uniform vec3 LightSpc[4];
uniform vec4 BackFaceColor;
//uniform vec3 GlobalAmbient;
uniform int AlphaBlendMode;
uniform int HasTextureMap;
uniform vec3 ViewDir;
uniform vec3 CameraPos;
uniform int ClipPlaneNum;
uniform vec4 ClipPlane[4];

uniform sampler2D ColorMapSampler;
//uniform sampler2D AlphaMapSampler;
uniform sampler2D BumpMapSampler;
uniform sampler2D EmissiveMapSampler;
uniform sampler2D ShadeMultiplyMapSampler;
uniform sampler2D ShadingShiftMapSampler;
uniform sampler2D MatcapMapSampler;
uniform sampler2D RimMultiplyMapSampler;
uniform sampler2D OutlineWidthMultiplyMapSampler;
uniform sampler2D UvAnimationMaskMapSampler;


in vec4 GSWorldPos;
in vec4 GSColor;
in vec3 GSNormal;
in vec2 GSTexCoord;
#if USE_TANGENT
in vec3 GSTangent;
in vec3 GSBinormal;
#endif
#if MULTIUV >= 1
in vec2 GSTexCoord1;
#endif
#if MULTIUV >= 2
in vec2 GSTexCoord2;
#endif
#if MULTIUV >= 3
in vec2 GSTexCoord3;
#endif

out vec4 OutColor;

const float PI = 3.141592653589793;

#define float2 vec2
#define float3 vec3
#define float4 vec4
float lerp(float a, float b, float t) { return mix(a,b,t); }
vec3 lerp(vec3 a, vec3 b, float t) { return mix(a,b,t); }
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); }
float atan2(in float y, in float x) { return x == 0.0 ? sign(y)*PI/2 : atan(y, x); }

float SRGBtoLINEAR(float c)
{
    return (c < 0.04045) ? c*0.0773993808 : pow(c*0.9478672986+0.0521327014, 2.4);
}

vec3 SRGBtoLINEAR(vec3 srgbIn)
{
	return vec3(
		SRGBtoLINEAR(srgbIn.x),
		SRGBtoLINEAR(srgbIn.y),
		SRGBtoLINEAR(srgbIn.z));
}

vec4 SRGBtoLINEAR(vec4 srgbIn)
{
	vec3 linOut = SRGBtoLINEAR(srgbIn.xyz);
	return vec4(linOut,srgbIn.w);
}

vec3 SRGBtoLINEAR(vec3 srgbIn, bool is_srgb)
{
	if(is_srgb)
		return SRGBtoLINEAR(srgbIn);
	else
		return srgbIn;
}

vec4 SRGBtoLINEAR(vec4 srgbIn, bool is_srgb)
{
	if(is_srgb){
		vec3 linOut = SRGBtoLINEAR(srgbIn.xyz);
		return float4(linOut,srgbIn.w);
	}else{
		return srgbIn;
	}
}

float LINEARtoSRGB(float c)
{
    return (c < 0.0031308) ? c * 12.92 : 1.055 * pow(abs(c), 0.41666) - 0.055;
}

vec3 LINEARtoSRGB(vec3 srgbIn)
{
	return vec3(
		LINEARtoSRGB(srgbIn.x),
		LINEARtoSRGB(srgbIn.y),
		LINEARtoSRGB(srgbIn.z));
}

vec4 LINEARtoSRGB(vec4 srgbIn)
{
	vec3 linOut = LINEARtoSRGB(srgbIn.xyz);
	return vec4(linOut,srgbIn.w);
}

vec3 LINEARtoSRGB(vec3 srgbIn, bool is_srgb)
{
	if(is_srgb)
		return LINEARtoSRGB(srgbIn);
	else
		return srgbIn;
}

vec4 LINEARtoSRGB(vec4 srgbIn, bool is_srgb)
{
	if(is_srgb)
		return LINEARtoSRGB(srgbIn);
	else
		return srgbIn;
}

vec4 ColorCorrect(vec4 col, int type)
{
	if(ColorSpace != 0) // VRM 1.0
		return SRGBtoLINEAR(col, (type&3) == 1);
	else // VRM 0.x
		return LINEARtoSRGB(col, (type&3) == 2);
}

vec2 GetTexCoord(int channel)
{
#if MULTIUV >= 1
	if((channel & 0x30) == 0x10) return GSTexCoord1;
#endif
#if MULTIUV >= 2
	if((channel & 0x30) == 0x20) return GSTexCoord2;
#endif
#if MULTIUV >= 3
	if((channel & 0x30) == 0x30) return GSTexCoord3;
#endif
	return GSTexCoord;
}

float linearstep(float a, float b, float t)
{
	return saturate( ( t - a ) / ( b - a ) );
}


void main(void)
{
	for(int i=0; i<ClipPlaneNum; i++){
		if(dot(ClipPlane[i].xyz, GSWorldPos.xyz) + ClipPlane[i].w < 0)
			discard;
	}
	
	if(GSWorldPos.w == 0 && gl_FrontFacing)
		discard;

	vec3 V = normalize(CameraPos - GSWorldPos.xyz);
	vec4 base_color = GSColor;
	base_color *= ColorCorrect(texture(ColorMapSampler, GetTexCoord(HasTextureMap)), HasTextureMap);
	if(AlphaMode < 2)
		base_color.w = 1;
	else if(AlphaMode == 2){
		if(base_color.w < AlphaCutOff)
			discard;
		base_color.w = 1;
	}else if(base_color.w < 0.01)
		discard;

	vec3 N;
#if USE_TANGENT
	if(HasBumpMap != 0){
		vec3 bump_col = texture(BumpMapSampler, GetTexCoord(HasBumpMap)).xyz * 2 - 1;
		bump_col *= float3(NormalScale, NormalScale, 1.0);
		bump_col *= NormalMapFlip;
		mat3 mtx = mat3(GSTangent, GSBinormal, GSNormal);
		N = normalize(mul(bump_col, mtx));
	}else
#endif
	{
		N = normalize(GSNormal);
	}
	if(gl_FrontFacing && GSWorldPos.w != 0){
		if(BackFaceColor.w == 0 && DoubleSided == 0)
			discard;
		base_color.xyz *= BackFaceColor.xyz;
		N = -N;
	}

	float3 color = float3(0,0,0);
	for(int i=0; i<1 && i<LightNum; i++){
		float3 L;
		if(LightPos[i].w == 0){
			L = normalize(LightPos[i].xyz - GSWorldPos.xyz); // point light
		}else{
			L = LightPos[i].xyz; // directional light
		}
		float3 litcol = LightCol[i];

		float shading = dot(N, L);
		shading += ShadingShiftFactor;
		if(HasShadingShiftMap != 0)
			shading += ColorCorrect(texture(ShadingShiftMapSampler, GetTexCoord(HasShadingShiftMap)), HasShadingShiftMap).r * ShadingShiftScale;
		shading = linearstep(-1.0 + ShadingToonyFactor, 1.0 - ShadingToonyFactor, shading);

		float3 shadeColorTerm = ShadeColorFactor.xyz;
		if(HasShadeMultiplyMap != 0)
			shadeColorTerm *= ColorCorrect(texture(ShadeMultiplyMapSampler, GetTexCoord(HasShadeMultiplyMap)), HasShadeMultiplyMap).xyz;

		color = lerp(shadeColorTerm, base_color.xyz, shading);
		color = color * litcol;
		
		// Rim lighting
		{
			float3 rim = float3(0,0,0);
			if(HasMatcapMap != 0){
				float3 x = normalize( float3( V.z, 0.0, -V.x ) );
				float3 y = -cross( V, x );
				float2 matcapUv = float2( dot( x, N ), dot( y, N ) ) * 0.495 + 0.5;
				rim = MatcapFactor.xyz * ColorCorrect(texture(MatcapMapSampler, matcapUv), HasMatcapMap).xyz;
			}

			float epsilon = 0.00001;
			float parametricRim = saturate( 1.0 - dot( N, V ) + ParametricRimLiftFactor );
			parametricRim = pow( parametricRim, max( ParametricRimFresnelPowerFactor, epsilon ) );
			rim += parametricRim * ParametricRimColorFactor.xyz;

			if(HasRimMultiplyMap != 0)
				rim *= ColorCorrect(texture(RimMultiplyMapSampler, GetTexCoord(HasRimMultiplyMap)), HasRimMultiplyMap).xyz;

			rim *= lerp( float3(1,1,1), litcol, RimLightingMixFactor);

			// color already has the direct lighting factor + global illumination factor
			color += rim;
		}
	}

	vec3 emissive = Emissive.xyz;
	if(HasEmissiveMap != 0)
		emissive *= ColorCorrect(texture(EmissiveMapSampler, GetTexCoord(HasEmissiveMap)), HasEmissiveMap).xyz;
	color += emissive;

	if(GSWorldPos.w == 0 && OutlineWidthMode != 0){
		vec3 outline = OutlineColorFactor.xyz;
		color.xyz = lerp(outline, color.xyz, OutlineLightingMixFactor);
	}

	vec4 col;
	col.xyz = saturate(color);
	col.w = saturate(base_color.w);
	OutColor = LINEARtoSRGB(col, ColorSpace != 0);
}

