//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
 * @file basic.fx
 */
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// CONSTANTS
float4x4 c_mtx_world;          // World
float4x4 c_mtx_view_proj;      // View * Projection matrix

float4   c_colorize;           // Color used to colorize a vertex.

float4x4 c_mtx_light_proj;     // transforms from view space to light/projection space.
float3   c_eye;                // Eye position in world space.

float    c_spec_pow;           // Power value for specular lights.
float4   c_light_ambient[3];   // light ambient color.
float4   c_light_diffuse[3];   // light diffuse color.
float4   c_light_specular[3];  // light specular color.
float3   c_light_dir[3];       // light direction vector.

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Textures 
texture c_tex;          // diffuse texture
texture c_normal_tex;   // normal map texture

sampler s_tex =
sampler_state
{
    Texture = <c_tex>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
};

sampler s_normal_tex =
sampler_state
{
    Texture = <c_normal_tex>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Vertex shader input structure
struct VS_IN_UNLIT
{
    float4 pos     : POSITION;   // position
    float4 color   : COLOR0;     // color
    float2 tex     : TEXCOORD0;  // texture coordinates
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Vertex shader input structure
struct VS_IN_LIT
{
    float4 pos     : POSITION;   // position
    float3 normal  : NORMAL;     // normal
    float2 tex     : TEXCOORD0;  // texture coordinates
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Vertex shader input structure
struct VS_IN_NMAP
{
    float4 pos     : POSITION;   // position
    float3 normal  : NORMAL;     // normal
    float2 tex     : TEXCOORD0;  // texture coordinates
    float3 tangent : TANGENT;    // tangent
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Vertex shader output structure
struct VS_OUT_LIT
{
    float4 pos     : POSITION;   // position
    float4 diffuse : COLOR0;     // diffuse color
    float2 tex     : TEXCOORD0;  // texture coordinates
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Vertex shader output structure
struct VS_OUT_NMAP
{
    float4 pos     : POSITION;   // position
    float4 light   : COLOR0;     // light vector
    float4 diffuse : COLOR1;     // diffuse color
    float2 tex     : TEXCOORD0;  // texture coordinates
#if defined(SPECULAR)
    float4 eye     : TEXCOORD2;  // eye vector
#endif
};


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LightDiffuse
float4 LightDiffuse( float3 vec_dir, float3 normal : NORMAL,
                     float4 color )
{
    return float4(color.rgb * saturate(dot(normal, vec_dir)), 1.0F);
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LightSpecular
float4 LightSpecular( float3 vec_dir, float3 normal : NORMAL,
                      float3 vec_eye, float4 color )
{
    float3 vec_half = normalize(vec_dir + vec_eye);
    float intensity = pow(saturate(dot(normal, vec_half)), c_spec_pow);

    return float4(color.rgb * intensity, 0.0F);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VS_OUT_LIT VS_Unlit( in VS_IN_UNLIT vs_in )
{
    VS_OUT_LIT vs_out;

    float4x4 mtx_wvp = mul(c_mtx_world, c_mtx_view_proj);
    vs_out.pos = mul(float4(vs_in.pos.xyz, 1.0F), mtx_wvp);
    vs_out.diffuse = vs_in.color * c_colorize;
    vs_out.tex = vs_in.tex;

    return vs_out;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VS_OUT_LIT VS_Lit( in VS_IN_LIT vs_in )
{
    VS_OUT_LIT vs_out;

    float4x4 mtx_wvp = mul(c_mtx_world, c_mtx_view_proj);
    vs_out.pos = mul(float4(vs_in.pos.xyz, 1.0F), mtx_wvp);
	float3 normal = normalize(mul( vs_in.normal, c_mtx_world ));
    vs_out.diffuse = LightDiffuse(c_light_dir[0], normal, c_light_diffuse[0]);
    vs_out.diffuse += LightDiffuse(c_light_dir[1], normal, c_light_diffuse[1]);
//    vs_out.diffuse += LightDiffuse(c_light_dir[2], normal, c_light_diffuse[2]);
    vs_out.tex = vs_in.tex;

    vs_out.diffuse += c_light_ambient[0] + c_light_ambient[1];

    return vs_out;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VS_OUT_NMAP VS_NormalMap( in VS_IN_NMAP vs_in )
{
    VS_OUT_NMAP vs_out;

    float4x4 mtx_wvp = mul(c_mtx_world, c_mtx_view_proj);
    vs_out.pos = mul(float4(vs_in.pos.xyz, 1.0F), mtx_wvp);

	float3 normal = normalize(mul( vs_in.normal, c_mtx_world ));
	float3 tangent = normalize(mul( vs_in.tangent, c_mtx_world ));
	float3 binormal = cross(normal, tangent);
	float3x3 mtx_TBN = float3x3(tangent, binormal, normal);

#if defined(SPECULAR)
	float4 world_pos = mul(float4(vs_in.pos.xyz, 1.0F), c_mtx_world);

    // Eye vector for specular lighting
	float3 vec_eye = normalize(c_eye - world_pos.xyz);
    vs_out.eye.xyz = mul(mtx_TBN, vec_eye);
	vs_out.eye.w = 1.0F;
#endif

    vs_out.light.xyz = mul(mtx_TBN, c_light_dir[0]);
	vs_out.light.w = 1.0F;

    vs_out.diffuse = LightDiffuse(c_light_dir[1], normal, c_light_diffuse[1]);
//    vs_out.diffuse += LightDiffuse(c_light_dir[2], normal, c_light_diffuse[2]);
    vs_out.tex = vs_in.tex;

    vs_out.diffuse += c_light_ambient[0] + c_light_ambient[1];// +

    return vs_out;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Pixel Shader
float4 PS_Lit( in VS_OUT_LIT ps_in, uniform bool b_tex, uniform bool b_decal ) : COLOR
{
	float4 color;

    if (b_tex)
    {
		float4 tex_color;
        tex_color = tex2D(s_tex, ps_in.tex);
        
        if (b_decal)
        {
            // Decal blending
            color.rgb = ( (ps_in.diffuse.rgb * ( 1 - tex_color.a )) + (tex_color.rgb * tex_color.a) );
            color.a = ps_in.diffuse.a;
        }//if
        else
        {
            color = ps_in.diffuse * tex_color;
        }//else
    }//if
    else
    {
        color = ps_in.diffuse;
    }//else

    return color;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Pixel Shader
float4 PS_NormalMap( in VS_OUT_NMAP ps_in ) : COLOR
{
	float4 tex_color = tex2D(s_tex, ps_in.tex);
	float3 normal = 2.0F * tex2D(s_normal_tex, ps_in.tex).xyz - 1.0F;
	float3 light = 2.0F * ps_in.light.xyz - 1.0F;
//	float3 light1 = 2.0F * ps_in.light1.xyz - 1.0F;

	float4 color;
    float4 diffuse = ps_in.diffuse +
                     LightDiffuse(light, normal, c_light_diffuse[0]);

#if defined(SPECULAR)
	float3 vec_eye = 2.0F * ps_in.eye.xyz - 1.0F;
    float4 specular = LightSpecular(light, normal, vec_eye, c_light_specular[0]);
#endif


#if defined(SPECULAR)
    color = diffuse * tex_color + specular;
#else
    color = diffuse * tex_color;
#endif

    return color;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TECHNIQUES

technique Unlit
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Unlit( );
        PixelShader  = compile ps_2_0 PS_Lit( false, false );
    }
}

technique UnlitTex
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Unlit( );
        PixelShader  = compile ps_2_0 PS_Lit( true, false );
    }
}

technique UnlitTexDecal
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Unlit( );
        PixelShader  = compile ps_2_0 PS_Lit( true, true );
    }
}

technique Lit
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Lit( );
        PixelShader  = compile ps_2_0 PS_Lit( false, false );
    }
}

technique LitTex
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Lit( );
        PixelShader  = compile ps_2_0 PS_Lit( true, false );
    }
}

technique LitTexDecal
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_Lit( );
        PixelShader  = compile ps_2_0 PS_Lit( true, true );
    }
}

technique NormalMap
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS_NormalMap( );
        PixelShader  = compile ps_2_0 PS_NormalMap( );
    }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
