//--------------------------------------------------------------------------------------
// Textures & Constant Buffer Variables
//--------------------------------------------------------------------------------------
Texture3D lightVolumeRed :	register( t0 );
Texture3D lightVolumeGreen :	register( t1 );
Texture3D lightVolumeBlue :	register( t2 );
Texture3D geometryVolume :	register( t3 );

SamplerState samLinear :register( s0 );
SamplerState samPoint :	register( s1 );
SamplerState samPointZeroBorder : register( s2 );

cbuffer cbChangeOnRenderTarget: register( b0 )
{
    float4 lpvMiddle;
	float4 lpvGrid;
};

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

static const int3 neighbourSample[6] = {
	int3( 1, 0, 0),
	int3(-1, 0, 0),
	int3( 0, 1, 0),
	int3( 0,-1, 0),
	int3( 0, 0, 1),
	int3( 0, 0,-1)
};

static const float3 neighbourDir[6] = {
	float3( 1.0f, 0.0f, 0.0f),
	float3(-1.0f, 0.0f, 0.0f),
	float3( 0.0f, 1.0f, 0.0f),
	float3( 0.0f,-1.0f, 0.0f),
	float3( 0.0f, 0.0f, 1.0f),
	float3( 0.0f, 0.0f,-1.0f)
};

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

static const float directFaceSubtendedSolidAngle = 0.03188428; // 0.4006696846f / 4Pi;
static const float sideFaceSubtendedSolidAngle = 0.03369559; // 0.4234413544f / 4Pi;


//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float2 Pos : POSITION;
    float2 Tex : TEXCOORD0;
	uint slice : SV_InstanceID;
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float3 Tex : TEXCOORD0;
	uint slice : TEXCOORD1;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Tex : TEXCOORD0;
	uint slice : SV_RenderTargetArrayIndex;
};

struct PS_OUTPUT
{
    float4 redSH : SV_Target0;
	float4 greenSH : SV_Target1;
	float4 blueSH : SV_Target2;
	float4 redSHaccum : SV_Target3;
	float4 greenSHaccum : SV_Target4;
	float4 blueSHaccum : SV_Target5;
};

struct SHc
{
	float4 redSH;
	float4 greenSH;
	float4 blueSH;
};

//--------------------------------------------------------------------------------------
// 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;
    output.Pos.xy = input.Pos;
	output.Pos.zw = float2(0.0f,1.0f);
    output.Tex.xy = input.Tex;
	output.Tex.z = (float)input.slice / lpvGrid.z;
	output.slice = input.slice;
    
    return output;
}

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

[maxvertexcount(3)]
void GS( triangle VS_OUTPUT input[3], inout TriangleStream<PS_INPUT> OutputStream )
{
	PS_INPUT output;
	
	output.Pos = input[0].Pos;
	output.Tex = input[0].Tex;
	output.slice = input[0].slice;
	
	OutputStream.Append( output );

	output.Pos = input[1].Pos;
	output.Tex = input[1].Tex;
	output.slice = input[1].slice;
	
	OutputStream.Append( output );

	output.Pos = input[2].Pos;
	output.Tex = input[2].Tex;
	output.slice = input[2].slice;
	
	OutputStream.Append( output );

	OutputStream.RestartStrip();
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
PS_OUTPUT PS( PS_INPUT input)
{
	PS_OUTPUT output = (PS_OUTPUT) 0;

	SHc prop = (SHc) 0;
	
	/*float4 occCoeffs[8];
	//sample all occCoeffs
	for( int i = 0 ; */

	for( int i = 0 ; i < 6 ; i++ ) {
		
		SHc nb;
		nb.redSH = lightVolumeRed.Sample(samPointZeroBorder, input.Tex, neighbourSample[i]);
		nb.greenSH = lightVolumeGreen.Sample(samPointZeroBorder, input.Tex, neighbourSample[i]);
		nb.blueSH = lightVolumeBlue.Sample(samPointZeroBorder, input.Tex, neighbourSample[i]);
		
		float transmittanceValue = 1.0; // no occlusion

		float occludedDirectFaceContribution = transmittanceValue * directFaceSubtendedSolidAngle;

		float4 mainDirectionCosineLobeSH = calcSphericalHarmonicCosineLobe( neighbourDir[i] );
		float4 mainDirectionSH = calcSphericalHarmonicCosineLobe( neighbourDir[i] );
		/*prop.redSH += occludedDirectFaceContribution * dot( nb.redSH, mainDirectionSH ) * mainDirectionCosineLobeSH;
		prop.greenSH += occludedDirectFaceContribution * dot( nb.greenSH, mainDirectionSH ) * mainDirectionCosineLobeSH;
		prop.blueSH += occludedDirectFaceContribution * dot( nb.blueSH, mainDirectionSH ) * mainDirectionCosineLobeSH;*/
		prop.redSH += occludedDirectFaceContribution * dot( nb.redSH, mainDirectionSH ) * mainDirectionCosineLobeSH;
		prop.greenSH += occludedDirectFaceContribution * dot( nb.greenSH, mainDirectionSH ) * mainDirectionCosineLobeSH;
		prop.blueSH += occludedDirectFaceContribution * dot( nb.blueSH, mainDirectionSH ) * mainDirectionCosineLobeSH;

	}

	output.redSHaccum = output.redSH = prop.redSH;
	output.greenSHaccum = output.greenSH = prop.greenSH;
	output.blueSHaccum = output.blueSH = prop.blueSH;

	return output;
}
