//Billboard geometry shader - generates a 2D quad based on a point representing the top left of the quad and calculates output and view positions of light sources

cbuffer MatrixBuffer : register(b0)
{
	//Includes view and projection matrices of lights
	matrix worldMatrix;
	matrix viewMatrix;
	matrix projectionMatrix;
	matrix sunViewMatrix;
	matrix sunProjectionMatrix;
	matrix spotViewMatrix;
	matrix spotProjectionMatrix;
	matrix hillViews[6];
	matrix hillProjections[6];
};

cbuffer CameraBuffer : register(b1)
{
	float4 cameraPosition;	//Position of the camera - needed for billboarding effect
};

cbuffer PositionsBuffer : register(b2)
{
	float4 positions[4];	//Positions of the vertices
};

cbuffer QuadDataBuffer
{
	//Precalculated texture coordinates of the quad
	static float2 texCoords[4] =
	{
		float2(0.0f, 0.0f),
		float2(0.0f, 1.0f),
		float2(1.0f, 0.0f),
		float2(1.0f, 1.0f)
	};
};

struct InputType
{
	float4 position : POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
	float4 sunViewPos : TEXCOORD1;
	float4 spotViewPos : TEXCOORD2;
	float3 worldPosition : TEXCOORD3;
	float4 hillViewPositions[6] : TEXCOORD4;
};

struct OutputType
{
	float4 position : SV_POSITION;
	float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
	float4 sunViewPos : TEXCOORD1;
	float4 spotViewPos : TEXCOORD2;
	float3 worldPosition : TEXCOORD3;
	float4 hillViewPositions[6] : TEXCOORD4;
};

//Quad has maximum vertex count of 4
[maxvertexcount(4)]
void main(point InputType input[1], inout TriangleStream<OutputType> triStream)
{
	OutputType output;

	//Calculate the camera's look at vector between it and the quad
	float3 lookAt = cameraPosition.xyz - mul(input[0].position, worldMatrix).xyz;
	lookAt.y = 0.0f;
	lookAt = normalize(lookAt);

	//Calculate a tangent from the look at and up vectors
	float3 tangent = normalize(cross(lookAt, float3(0.0f, 1.0f, 0.0f)));

	for (int i = 0; i < 4; i++)
	{
		//Calculate the homogenous point position based on the passed in position and the tangent
		float3 pointPos = (tangent * (positions[i].x)) + float3(0.0f, positions[i].y, 0.0f);
		float4 homogeneousPointPos = input[0].position + float4(pointPos, 1.0f);
		homogeneousPointPos.w = 1.0f;

		//Now calculate the output position and world coordinates
		output.position = mul(homogeneousPointPos, worldMatrix);
		output.worldPosition = mul(output.position, worldMatrix);
		output.position = mul(output.position, viewMatrix);
		output.position = mul(output.position, projectionMatrix);

		//Set the precalculated texture coordinate and normal
		output.tex = texCoords[i];
		output.normal = float3(0.0, 1.0f, 0.0f);

		//Calculate the position of the sun
		output.sunViewPos = mul(homogeneousPointPos, worldMatrix);
		output.sunViewPos = mul(output.sunViewPos, sunViewMatrix);
		output.sunViewPos = mul(output.sunViewPos, sunProjectionMatrix);

		//Calculate the position of the spot light
		output.spotViewPos = mul(homogeneousPointPos, worldMatrix);
		output.spotViewPos = mul(output.spotViewPos, spotViewMatrix);
		output.spotViewPos = mul(output.spotViewPos, spotProjectionMatrix);

		//Calculate the positions of the hill point light in each of the directions
		for (int i = 0; i < 6; i++)
		{
			output.hillViewPositions[i] = mul(homogeneousPointPos, worldMatrix);
			output.hillViewPositions[i] = mul(output.hillViewPositions[i], hillViews[i]);
			output.hillViewPositions[i] = mul(output.hillViewPositions[i], hillProjections[i]);
		}

		//Now append the vertex to the output
		triStream.Append(output);

	}

	//All vertices are made, restart the strip
	triStream.RestartStrip();

}