#version 330
/*
  Copyright(C) 2015-2022, tetraface Inc. All rights reserved.
  
  This is a modified implementation of Disney's BRDF shader.
  The original code is here:
    https://github.com/wdas/brdf
*/
/*
# Copyright Disney Enterprises, Inc.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License
# and the following modification to it: Section 6 Trademarks.
# deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the
# trade names, trademarks, service marks, or product names of the
# Licensor and its affiliates, except as required for reproducing
# the content of the NOTICE file.
#
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
*/

#define float3 vec3
#define float4 vec4
#define lerp mix
#define saturate(_x) clamp(_x,0.0,1.0)
vec3 mul(vec3 v, mat3 m) { return m * v; }

//uniform float presence; //Alpha
uniform float Diffuse;
uniform float4 BaseColor; //diffuseColor
struct SPECULAR {
	float4 Specular; //FaceColor
	float4 EdgeColor;
	float Roughness;
	float Anisotropic;
	int ModelType;
	int FresnelMode;
	float FresnelExponent;
	float4 RefractionIndex;
	float4 ExtinctionCoeff;
};
uniform SPECULAR Specular;
struct GROW {
	float Gain;
	float4 Color;
};
uniform GROW Glow;
struct GLASS {
	float ReflectionGain;
};
uniform GLASS Glass;

uniform bool HasBumpMap;
uniform vec3 NormalMapFlip;
uniform int LightNum;
uniform vec4 LightPos[4];
uniform vec3 LightCol[4];
uniform vec3 LightSpc[4];
uniform vec4 BackFaceColor;
uniform vec3 GlobalAmbient;
uniform vec3 ViewDir;
uniform int ClipPlaneNum;
uniform vec4 ClipPlane[4];

uniform sampler2D ColorMapSampler;
//uniform sampler2D AlphaMapSampler;
uniform sampler2D BumpMapSampler;

in vec3 VSWorldPos;
in vec4 VSColor;
in vec3 VSNormal;
in vec2 VSTexCoord;
in vec3 VSTangent;
in vec3 VSBinormal;

out vec4 OutColor;


const float PI = 3.14159265358979323846;

float sqr(float x) { return x*x; }

float SchlickFresnel(float u)
{
    float m = saturate(1-u);
    float m2 = m*m;
    return m2*m2*m; // pow(m,5)
}

float GTR1(float NdotH, float a)
{
    if (a >= 1) return 1/PI;
	else{
		float a2 = a*a;
		float t = 1 + (a2 - 1)*NdotH*NdotH;
		return (a2 - 1) / (PI*log(a2)*t);
	}
}

float GTR2(float NdotH, float a)
{
    float a2 = a*a;
    float t = 1 + (a2-1)*NdotH*NdotH;
    return a2 / (PI * t*t);
}

float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
	float d = PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH );
    return (d > 0) ? 1 / d : 0;
}

float smithG_GGX(float Ndotv, float alphaG)
{
	float a = alphaG*alphaG;
	float b = Ndotv*Ndotv;
	return 1 / (saturate(Ndotv) + sqrt(a + b - a*b));
}

float3 BRDF(float3 L, float3 V, float3 N, float3 X, float3 Y, float3 base_color, float shadow)
{
    float NdotL = dot(N,L);
    float NdotV = dot(N,V);
    if (NdotL <= 0){
		return float3(0,0,0);
	}else{
		float3 H = normalize(L+V);
		float NdotH = dot(N,H);
		float LdotH = dot(L,H);

		float3 Cdlin = base_color * Diffuse;
		//float Cdlum = .3*Cdlin[0] + .6*Cdlin[1]  + .1*Cdlin[2]; // luminance approx.

		//float3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : float3(1,1,1); // normalize lum. to isolate hue+sat
		//float3 Cspec0 = lerp(Specular.Specular*0.08*lerp(float3(1,1,1), Ctint, specularTint), Cdlin, reflectionGain);
		//float3 Csheen = lerp(float3(1,1,1), Ctint, sheenTint);

		// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
		// and lerp in diffuse retro-reflection based on roughness
		float FL = SchlickFresnel(NdotL);
		float FV = SchlickFresnel(NdotV);
		float Fd90 = 0.5 + 2 * LdotH*LdotH * Specular.Roughness;
		float Fd = lerp(1, Fd90, FL) * lerp(1, Fd90, FV);

		// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
		// 1.25 scale is used to (roughly) preserve albedo
		// Fss90 used to "flatten" retroreflection based on roughness
		/*float Fss90 = LdotH*LdotH*roughness;
		float Fss = lerp(1, Fss90, FL) * lerp(1, Fss90, FV);
		float ss = (NdotL + NdotV > 0) ? 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5) : 0;*/

		// specular
		float3 specular;
		if(Specular.FresnelMode == 0){
			// Artistic mode (Not exact but approximated)
			float3 Cspec0 = Specular.Specular.xyz*0.08;
			float F0 = Specular.FresnelExponent;
			float alpha = Specular.Roughness*Specular.Roughness;
			float alphaSqr = alpha * alpha;
			float denom = NdotH * NdotH * (alphaSqr - 1.0) + 1.0;
			float D = alphaSqr / (3.141592653589793 * denom * denom);
			float F = F0 + (1.0 - F0) * pow(1.0 - saturate(LdotH), 5.0);
			float k = 0.5 * alpha;
			float ggx = NdotL * D * F / ((NdotL*(1.0-k)+k) * (saturate(NdotV)*(1.0-k)+k));
			specular = ggx * lerp(Cspec0, float3(0,0,0), Specular.Roughness);
		}else{
			// Physical mode
			float3 Cspec0 = Specular.EdgeColor.xyz*0.08;
			float aspect = sqrt(1-Specular.Anisotropic*.9);
			float ax = max(.001, sqr(Specular.Roughness)/aspect);
			float ay = max(.001, sqr(Specular.Roughness)*aspect);
			float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
			float FH = SchlickFresnel(LdotH);
			float3 Fs = lerp(Cspec0, float3(1,1,1), FH);
			float roughg = sqr(Specular.Roughness*.5+.5);
			float Gs = smithG_GGX(NdotL, roughg) * smithG_GGX(NdotV, roughg);
			specular = Gs*Fs*Ds;
		}

		// sheen
		//float3 Fsheen = FH * sheen * Csheen;

		// clearcoat (ior = 1.5 -> F0 = 0.04)
		//float Dr = GTR1(NdotH, lerp(.1,.001,clearcoatGloss));
		//float Fr = lerp(.04, 1, FH);
		//float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);

		float3 diffuse = (1/PI) * /*lerp*/(Fd*Cdlin/*, ss*subsurfaceColor.xyz, subsurface*/) * shadow;
		float3 result = (diffuse/*+Fsheen*/)*(1-Glass.ReflectionGain) + specular/* + .25*clearcoat*Gr*Fr*Dr*/;
		return result * NdotL;
	}
}


// Pixel shader
void main(void)
{
	for(int i=0; i<ClipPlaneNum; i++){
		if(dot(ClipPlane[i].xyz, VSWorldPos) + ClipPlane[i].w < 0)
			discard;
	}
	vec4 base_color = texture(ColorMapSampler, VSTexCoord) * VSColor;
	if(base_color.w < 0.01)
		discard;
	
	vec3 nrm_v, tan_v, bin_v;
	if(HasBumpMap){
		float3 normal_col = texture(BumpMapSampler, VSTexCoord).xyz * 2.0 - 1.0;
		normal_col *= NormalMapFlip;
		normalize(normal_col);

		mat3 mtxn = mat3(VSTangent, VSBinormal, VSNormal);
		nrm_v = normal_col * mtxn;
		mat3 mtxt = mat3(VSBinormal, VSNormal, VSTangent);
		tan_v = normal_col * mtxt;
		mat3 mtxb = mat3(VSNormal, VSTangent, VSBinormal);
		bin_v = normal_col * mtxb;
	}else{
		nrm_v = VSNormal;
		tan_v = VSTangent;
		bin_v = VSBinormal;
	}
	if(gl_FrontFacing){
		base_color.xyz *= BackFaceColor.xyz;
		nrm_v = -nrm_v;
		tan_v = -tan_v;
		bin_v = -bin_v;
	}

	float3 b = float3(0,0,0);
	for(int i=0; i<4 && i<LightNum; i++){
		float3 light_dir;
		if(LightPos[i].w == 0){
			light_dir = normalize(LightPos[i].xyz - VSWorldPos.xyz); // point light
		}else{
			light_dir = LightPos[i].xyz; // directional light
		}
		b += BRDF(light_dir, ViewDir, nrm_v, tan_v, bin_v, base_color.xyz, 1);
	}

	vec4 col;
	col.xyz = pow(saturate(b * PI + Glow.Gain*Glow.Color.xyz), vec3(1.0/2.2));
	col.w = base_color.w;
	OutColor = saturate(col);
}

