////////////////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////////////////

#include "../../../types.txt"

////////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////////////////////////

static const float CMB_SMP_TABLE[CMB_MAX_SAMPLES + 1] =
{
	1.0 / 1.0,
	1.0 / 1.0,
	1.0 / 2.0,
	1.0 / 3.0,
	1.0 / 4.0,
	1.0 / 5.0,
	1.0 / 6.0,
	1.0 / 7.0,
	1.0 / 8.0,
	1.0 / 9.0,
	1.0 / 10.0,
	1.0 / 11.0,
	1.0 / 12.0,
	1.0 / 13.0,
	1.0 / 14.0,
	1.0 / 15.0,
	1.0 / 16.0,
#if ( CMB_MAX_SAMPLES >= 24 )
	1.0 / 17.0,
	1.0 / 18.0,
	1.0 / 19.0,
	1.0 / 20.0,
	1.0 / 21.0,
	1.0 / 22.0,
	1.0 / 23.0,
	1.0 / 24.0,
#endif //CMB_MAX_SAMPLES
#if ( CMB_MAX_SAMPLES >= 32 )
	1.0 / 25.0,
	1.0 / 26.0,
	1.0 / 27.0,
	1.0 / 28.0,
	1.0 / 29.0,
	1.0 / 30.0,
	1.0 / 31.0,
	1.0 / 32.0,
#endif //CMB_MAX_SAMPLES
#if ( CMB_MAX_SAMPLES >= 64 )
	1.0 / 33.0,
	1.0 / 34.0,
	1.0 / 35.0,
	1.0 / 36.0,
	1.0 / 37.0,
	1.0 / 38.0,
	1.0 / 39.0,
	1.0 / 40.0,
	1.0 / 41.0,
	1.0 / 42.0,
	1.0 / 43.0,
	1.0 / 44.0,
	1.0 / 45.0,
	1.0 / 46.0,
	1.0 / 47.0,
	1.0 / 48.0,
	1.0 / 49.0,
	1.0 / 50.0,
	1.0 / 51.0,
	1.0 / 52.0,
	1.0 / 53.0,
	1.0 / 54.0,
	1.0 / 55.0,
	1.0 / 56.0,
	1.0 / 57.0,
	1.0 / 58.0,
	1.0 / 59.0,
	1.0 / 60.0,
	1.0 / 61.0,
	1.0 / 62.0,
	1.0 / 63.0,
	1.0 / 64.0,
#endif //CMB_MAX_SAMPLES
};

#define CMB_MIN_DEPTH 0.0001

static const float CMB_INV_MAX_SAMPLES_F = 1.0 / CMB_MAX_SAMPLES;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Structures
////////////////////////////////////////////////////////////////////////////////////////////////////

struct PS_INPUT
{
#if MIX_SM_HIGH
	float4 pos : SV_POSITION;
#endif //MIX_SM_HIGH
	float2 tex : TEXCOORD0;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Grobal values
////////////////////////////////////////////////////////////////////////////////////////////////////

#if MIX_SM_HIGH

	cbuffer cbInput : register( b0 )
	{
		float4x4 g_InvViewProjMat;
		float4x4 g_PreViewProjMat;
		float4   g_Params0;
		float4   g_Params1;
	};

	Texture2D    g_ColorTexture : register( t0 );
	SamplerState g_ColorSampler : register( s0 );
	Texture2D    g_DepthTexture : register( t1 );
	SamplerState g_DepthSampler : register( s1 );

#else //MIX_SM_HIGH

	float4x4 g_InvViewProjMat : register(  c0 );
	float4x4 g_PreViewProjMat : register(  c4 );
	float4   g_Params0        : register(  c8 );
	float4   g_Params1        : register(  c9 );

	sampler g_ColorSampler : register( s0 );
	sampler g_DepthSampler : register( s1 );

#endif //MIX_SM_HIGH

////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros
////////////////////////////////////////////////////////////////////////////////////////////////////

#if MIX_SM_HIGH
	#define TEX_COLOR( uv ) g_ColorTexture.Sample( g_ColorSampler, uv )
	#define TEX_DEPTH( uv ) g_DepthTexture.Sample( g_DepthSampler, uv )
#else //MIX_SM_HIGH
	#define TEX_COLOR( uv ) tex2D( g_ColorSampler, uv )
	#define TEX_DEPTH( uv ) tex2D( g_DepthSampler, uv )
#endif //MIX_SM_HIGH

#define CMB_N2 g_Params0.x // 2.0 * NearZ
#define CMB_FPN g_Params0.y // FarZ + NearZ
#define CMB_INV_M_FMN g_Params0.z // 1.0 / -( FarZ - NearZ )

#define CMB_SCALE g_Params1.x // 0.5 * scale
#define CMB_INTENSITY g_Params1.y // intensity
#define CMB_INV_VOLUME g_Params1.z // 1.0 / volume
#define CMB_DIST g_Params1.w // dist / farZ ( 0.0001 )

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main function
////////////////////////////////////////////////////////////////////////////////////////////////////

float4 main( PS_INPUT input ) : MSV_TARGET0
{
	float linDepth;
	float expDepth;
	float4 curWorldPos;
	float4 preProjPos;
	float2 velocity;
	float volume;
	float preDepth;
	float curDepth;
	int smpCount;
	float2 smpTex;
	float4 total;
	float4 output;
	
	////////////////////////////////////////////////////////////////////////////////////////////////////

	// n  = near
	// f  = far
	// ld = linearDepth
	// ed = exponentialDepth
	//
	// ld = ( 2 * n ) / ( ( f + n ) - ed * ( f - n ) );
	//
	//                     |
	//
	// ( ( f + n ) - ed * ( f - n ) ) = ( 2 * n ) / ld;
	// ed = ( ( ( 2 * n ) / ld ) - ( f + n ) ) / -( f - n )

	linDepth = TEX_DEPTH( input.tex ).r;

	if( linDepth > CMB_MIN_DEPTH )
	{
		expDepth = ( ( CMB_N2 / linDepth ) - CMB_FPN ) * CMB_INV_M_FMN;
		expDepth = clamp( expDepth, 0.0, 1.0 );
//		depth = ( ( ( 2.0 * n ) / depth ) - ( f + n ) ) / -( f - n );
	}
	else
	{
		expDepth = 0.0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	
	curWorldPos = float4( ( input.tex.x * 2.0 - 1.0 ), ( ( 1.0 - input.tex.y ) * 2.0 - 1.0 ), expDepth, 1.0 );
	curWorldPos = mul( curWorldPos, g_InvViewProjMat );
	curWorldPos = curWorldPos / curWorldPos.w;

	preProjPos = mul( curWorldPos, g_PreViewProjMat );
	preProjPos = preProjPos / preProjPos.w;
	preProjPos.xy = float2( preProjPos.x * 0.5 + 0.5, 1.0 - ( preProjPos.y * 0.5 + 0.5 ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	velocity = ( input.tex - preProjPos.xy );

	volume = min( length( velocity ) * CMB_INV_VOLUME, 1.0 );

	velocity = velocity * CMB_SCALE;

	////////////////////////////////////////////////////////////////////////////////////////////////////

	preDepth = linDepth;

	velocity *= CMB_INV_MAX_SAMPLES_F;

	smpCount = 1;
	smpTex = input.tex + velocity;

	output = TEX_COLOR( input.tex );
	total = 0.0f;

	[unroll(CMB_MAX_SAMPLES)]
	for( int i = 1; i < CMB_MAX_SAMPLES; i++ )
	{
		curDepth = TEX_DEPTH( smpTex ).r;

		if( ( curDepth - preDepth ) <= CMB_DIST )
		{
			total += TEX_COLOR( smpTex );
			preDepth = curDepth;
			smpCount++;
		}

		smpTex += velocity;
	}

	output = lerp( output, ( output + total ) * CMB_SMP_TABLE[smpCount], CMB_INTENSITY * volume );

	return output;
}
