//************
// VARIABLES *
//************
cbuffer cbPerObject
{
    float4x4 m_MatrixWorldViewProj : WORLDVIEWPROJECTION;
    float4x4 m_MatrixWorld : WORLD;
    float3 m_LightDir = { 0.2f, -1.0f, 0.2f };
}

RasterizerState FrontCulling
{
    CullMode = NONE;
};

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

Texture2D m_TextureDiffuse
<
	string UIName = "Diffuse Texture";
	string ResourceName = "Chair_Dark.dds";
	string ResourceType = "2D";
>;

//**********
// STRUCTS *
//**********
struct VS_DATA
{
    float3 Position : POSITION;
    float3 Normal : NORMAL;
    float2 TexCoord : TEXCOORD;
};

struct GS_DATA
{
    float4 Position : SV_POSITION;
    float3 Normal : NORMAL;
    float2 TexCoord : TEXCOORD0;
};

//****************
// VERTEX SHADER *
//****************
VS_DATA MainVS(VS_DATA vsData)
{
	return vsData;
}

//******************
// GEOMETRY SHADER *
//******************
void CreateVertex(inout TriangleStream<GS_DATA> triStream, float3 pos, float3 normal, float2 texCoord)
{
	GS_DATA vertex = (GS_DATA)0;
	vertex.Position = mul(float4(pos, 1), m_MatrixWorldViewProj);
	vertex.Normal = mul(normal, (float3x3) m_MatrixWorld);
	vertex.TexCoord = texCoord;
	triStream.Append(vertex);
}

[maxvertexcount(6)]
void SpikeGenerator(triangle VS_DATA vertices[3], inout TriangleStream<GS_DATA> triStream)
{
	//Use these variable names
    float3 basePoint, top, left, right, spikeNormal;

	//Step 1. Calculate The basePoint
	basePoint = vertices[0].Position + vertices[1].Position + vertices[2].Position;
	basePoint /= 3;
	//Step 2. Calculate The normal of the basePoint
	float3 normal = vertices[0].Normal + vertices[1].Normal + vertices[2].Normal;
	normal /= 3;
	//Step 3. Calculate The Spike's Top Position
	top = basePoint + (8.f*normal);
	//Step 4. Calculate The Left And Right Positions
	float3 spikeDirection = (vertices[2].Position - vertices[0].Position) * 0.1f;
	left = basePoint - spikeDirection;
	right = basePoint + spikeDirection;
	//Step 5. Calculate The Normal of the spike
	spikeNormal = cross(top-left, top-right);
	//Step 6. Create The Vertices [Complete code in CreateVertex(...)]
	
	//Create Existing Geometry
    CreateVertex(triStream, vertices[0].Position, vertices[0].Normal, vertices[0].TexCoord);
    CreateVertex(triStream, vertices[1].Position, vertices[1].Normal, vertices[1].TexCoord);
    CreateVertex(triStream, vertices[2].Position, vertices[2].Normal, vertices[2].TexCoord);

	//Restart the strip so we can add another (independent) triangle!
    triStream.RestartStrip();

	//Create Spike Geometry
    CreateVertex(triStream, top, spikeNormal, float2(0, 0));
    CreateVertex(triStream, left, spikeNormal, float2(0, 0));
    CreateVertex(triStream, right, spikeNormal, float2(0, 0));
}

//***************
// PIXEL SHADER *
//***************
float4 MainPS(GS_DATA input) : SV_TARGET
{
    input.Normal = -normalize(input.Normal);
    float alpha = m_TextureDiffuse.Sample(samLinear, input.TexCoord).a;
    float3 color = m_TextureDiffuse.Sample(samLinear, input.TexCoord).rgb;
    float s = max(dot(m_LightDir, input.Normal), 0.4f);

    return float4(color * s, alpha);
}

//*************
// TECHNIQUES *
//*************
technique10 DefaultTechnique
{
    pass p0
    {
        SetRasterizerState(FrontCulling);
        SetVertexShader(CompileShader(vs_4_0, MainVS()));
        SetGeometryShader(CompileShader(gs_4_0, SpikeGenerator()));
        SetPixelShader(CompileShader(ps_4_0, MainPS()));
    }
}