//--------------------------------------------------------------------------------------
// Textures & Constant Buffer Variables
//--------------------------------------------------------------------------------------
Texture2D depthIn : register( t0 );
Texture2D posIn : register( t1 );
Texture2D normIn : register( t2 );
SamplerState samLinear : register( s0 );
SamplerState samPoint : register( s1 );

cbuffer cbChangesOnRenderTarget : register( b0 )
{
    float4 lpvMiddle;
	float4 lpvGrid; // xyz Texture Dimensions; w CellSize
	float4 camPos;
	float4 camData; //x: nearPlane, y: farPlane, z: foV
};


//--------------------------------------------------------------------------------------
// Static & Structs Variables
//--------------------------------------------------------------------------------------

static const float PI = 3.14159265f;

static const float cosLobeSHC0 = 0.886226925f; /* sqrt(pi)/2 */
static const float cosLobeSHC1 = 1.02332671f; /* sqrt(pi/3) */


//--------------------------------------------------------------------------------------

struct VS_INPUT
{
    uint pixIndex : SV_VertexID;
};

struct VS_OUTPUT {
	float4 pos : SV_POSITION;
	float3 normal : TEXCOORD0;
	float area : TEXCOORD1;
};

struct PS_INPUT {
	uint slice : SV_RenderTargetArrayIndex;
	float4 screenPos : SV_Position;
	float3 normal : TEXCOORD0;
	float area : TEXCOORD1;
};

struct PS_OUT
{
    float4 geomSH : SV_Target0;
};

//--------------------------------------------------------------------------------------
// Functions
//--------------------------------------------------------------------------------------

float4 calcSphericalHarmonicCosineLobe( float3 dir ) {
	return float4( cosLobeSHC0, -cosLobeSHC1 * dir.y, cosLobeSHC1 * dir.z, -cosLobeSHC1 * dir.x );
}

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------

VS_OUTPUT VS( VS_INPUT input ) {
	VS_OUTPUT output = (VS_OUTPUT)0;

	int2 dim;
	posIn.GetDimensions( dim.x, dim.y );
	int3 coords = int3( input.pixIndex % dim.x, input.pixIndex / dim.x, 0 );

	output.normal = normIn.Load( coords );

	float dis = depthIn.Load( coords );
	float2 a = (dis * camData.y + camData.x ) * tan(camData.z) / ( (float)dim.xy * 0.5f);
	output.area = a.x * a.y;

	float3 pos = posIn.Load( coords ).xyz - lpvGrid.w / lpvGrid.xyz * 0.5f ; //half a grid cell
	output.pos.xyz = (pos - lpvMiddle.xyz) / (lpvGrid.w * 0.5f);
	output.pos.z = (output.pos.z + 1.0f) * 0.5f;
	output.pos.w = 1.0f;
	
	return output;
}

//--------------------------------------------------------------------------------------
// Geometry Shader
//--------------------------------------------------------------------------------------

[maxvertexcount(1)]
void GS( point VS_OUTPUT input[1], inout PointStream<PS_INPUT> OutputStream ) {
	PS_INPUT output;
	
	if( length( input[0].normal ) > 0.01 ) {
		output.slice = input[0].pos.z * lpvGrid.z;
		output.screenPos = input[0].pos;

		output.normal = input[0].normal;
		output.area = input[0].area;
		
		OutputStream.Append( output );
	}
	OutputStream.RestartStrip();
}

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

PS_OUT PS( PS_INPUT input ) {
	PS_OUT output;

	float4 shc = calcSphericalHarmonicCosineLobe( input.normal );
	
	float cellSize = lpvGrid.w / lpvGrid.x;
	float areaR = saturate(input.area / (cellSize*cellSize) );

	output.geomSH = areaR * shc;

	return output;
}
