#include "PBR.fx"
#include "CurveWorld.fx"

float4x4 gWorld : WORLD;
float4x4 gWorldViewProj : WORLDVIEWPROJECTION;
float4x4 gMatrixViewInverse : VIEWINVERSE;
float3 gLightDirection = float3(-0.577f, -0.577f, 0.577f);

Texture2D gDiffuseTexture;
Texture2D gDiffuseMask;
Texture2D gNormalMap;
Texture2D gOcclusionRoughMetal;

TextureCube gCubeEnvironment;

float3 gColorNeon1;
float3 gColorNeon2;
float3 gColorNeon3;

float gFresnelHardness = 1;
float gFresnelPower = 5;
float gFresnelMultiplier = 10;

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap; // or Mirror or Clamp or Border
    AddressV = Wrap; // or Mirror or Clamp or Border
};

RasterizerState Solid
{
    FillMode = SOLID;
    CullMode = FRONT;
};

struct VS_INPUT
{
    float3 pos : POSITION;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float2 texCoord : TEXCOORD;
};
struct VS_OUTPUT
{
    float4 pos : SV_POSITION;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float4 worldPosition : COLOR0;
    float2 texCoord : TEXCOORD;
};

DepthStencilState EnableDepth
{
    DepthEnable = TRUE;
    DepthWriteMask = ALL;
};

RasterizerState NoCulling
{
    CullMode = FRONT;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VS(VS_INPUT input)
{

    VS_OUTPUT output;
    output.pos = CurveWorld(input.pos, gMatrixViewInverse, gWorld, gWorldViewProj);
    output.worldPosition = mul(float4(input.pos, 1.0), gWorld);
    output.normal = normalize(mul(input.normal, (float3x3) gWorld));
    output.tangent = normalize(mul(input.tangent, (float3x3) gWorld));
    output.texCoord = input.texCoord;
    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------

float3 lerp3(float3 color1, float3 color2, float t)
{
    float lerpR = lerp(color1.r, color2.r, t);
    float lerpG = lerp(color1.g, color2.g, t);
    float lerpB = lerp(color1.b, color2.b, t);
    return float3(lerpR, lerpG, lerpB);
}

float3 CalculateNormal(float3 tangent, float3 normal, float2 texCoord)
{
    float3 newNormal = normal;
	
    float3 binormal = cross(tangent, normal);
    binormal *= -1;
	
    float3x3 tangentMatrix;
    tangentMatrix._m00_m01_m02 = tangent;
    tangentMatrix._m10_m11_m12 = binormal;
    tangentMatrix._m20_m21_m22 = normal;
	
    float3 sampledNormal = (gNormalMap.Sample(samLinear, texCoord) * 2) - 1;
	
    return normalize(mul(sampledNormal, tangentMatrix));
}

float3 CalculateEnvironment(float3 viewDirection, float3 normal, float2 texCoord)
{	
    float3 reflected = normalize(reflect(viewDirection, normal));
	
    float roughness = gOcclusionRoughMetal.Sample(samLinear, texCoord).g;
    float metal = gOcclusionRoughMetal.Sample(samLinear, texCoord).b;
    
    float width, height, mipLevels;
    gCubeEnvironment.GetDimensions(0, width, height, mipLevels);
    
    // 9 lods are generated so use 9
    float3 reflectionCol = gCubeEnvironment.SampleLevel(samLinear, reflected, roughness * mipLevels);
	
    return reflectionCol * (1 - roughness) * metal;
}

//FRESNEL FALLOFF FUNCTION
float3 CalculateFresnelFalloff(float3 normal, float3 viewDirection, float3 environmentColor)
{
    float fresnelPrePow = 1 - saturate(abs(dot(normal, viewDirection)));
    float fresnel = pow(fresnelPrePow, gFresnelPower) * gFresnelMultiplier;
	
    float fresnelMaskPrewPow = 1 - saturate(dot(float3(0, -1, 0), normal));
    float fresnelMask = pow(fresnelMaskPrewPow, gFresnelHardness);
	
    return fresnel * fresnelMask * environmentColor;
}

float4 PS(VS_OUTPUT input) : SV_TARGET
{
    float4 diffuseColor = gDiffuseTexture.Sample(samLinear, input.texCoord);
    float4 maskColor = gDiffuseMask.Sample(samLinear, input.texCoord);
    float3 color_rgb = diffuseColor.rgb;
    float color_a = diffuseColor.a;
	
    float3 viewDirection = normalize(input.worldPosition.xyz - gMatrixViewInverse[3].xyz);
    //float3 normal = CalculateNormal(input.tangent, input.normal, input.texCoord);
    float3 normal = CalculateNormal(input.tangent, input.normal, input.texCoord, gNormalMap);
    float3 environment = CalculateEnvironment(viewDirection, normal, input.texCoord);
    environment = CalculateFresnelFalloff(normal, viewDirection, environment);
    
	//HalfLambert Diffuse :)
    float diffuseStrength = dot(normal, -gLightDirection);
    diffuseStrength = diffuseStrength * 0.5 + 0.5;
    diffuseStrength = saturate(diffuseStrength);
    color_rgb = color_rgb * diffuseStrength + environment;
    
    color_rgb = lerp3(color_rgb, gColorNeon1, maskColor.r);
    color_rgb = lerp3(color_rgb, gColorNeon2, maskColor.g);
    color_rgb = lerp3(color_rgb, gColorNeon3, maskColor.b);

    return float4(color_rgb, color_a);
}

//--------------------------------------------------------------------------------------
// Technique
//--------------------------------------------------------------------------------------
technique11 Default
{
    pass P0
    {
        SetRasterizerState(Solid);
        SetDepthStencilState(EnableDepth, 0);

        SetVertexShader(CompileShader(vs_4_0, VS()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_4_0, PS()));
    }
}

