//Depth of Field values, shader by PetkaGtA, thank you :]
//////////////////////////////////-----------------------------------------------------------------]

//DOF

//constants, don't change


float PI = 3.14159265;
float2 texel = {0.0009765625,0.00130208333333333333333333333333};

//your screen resolution

float2 screenRes = {1920,1080};

//DOF params

float focalDepth = 20.5;
float focalLength = 10.5;
float fstop = 20.5;
bool manualdof = false; 				//manual dof calculation
float ndofstart = 1.0; 					//near dof blur start
float ndofdist = 2.0; 					//near dof blur falloff distance
float fdofstart = 1.0; 					//far dof blur start
float fdofdist = 3.0; 					//far dof blur falloff distance
bool autofocus = true;
float2 focus = float2(0.5,0.5);
float CoC = 0.300;					//Circle of confusion http://en.wikipedia.org/wiki/Circle_of_confusion
bool noise = false; 					//DOF noise, adds grain
float namount = 0.0000; 				//grain amount
float DOFdownsample = 9;
float maxblur = 3.0; //Power of DOF blur
static const int samples = 26; 				//more samples =better quality but also less fps
static const int rings = 1; 				//ring count, DO NOT GO BEYOND 2 OR GAME CRASH!!!
float threshold = 0.9;					//threshold for gain
float gain = 1.0;					//brightens blurred areas to make bokeh effect visible
float bias = 0.0;
float fringe = 1.0;
float znear = 2.0; 					//camera clipping start (blur start)
float zfar = 2000.0; 					//camera clipping end (distance beyond which blur doesn't get bigger anymore)
bool pentagon = false; 					//use pentagon as bokeh shape? Bugged!
float feather = 0.0; 					//pentagon shape feather

//vignetting, darkens screen borders
bool vignetting = true;
float vignout = 25.31; 					//vignetting outer border
float vignin = 5.30; 					//vignetting inner border
float MartyMcFly = 25.30; 				//f-stops till vignette fades


#include "mta-helper.fx"
matrix gProjectionMainScene : PROJECTION_MAIN_SCENE;
//--------------------------------------------------------------------------------------
// Textures
//--------------------------------------------------------------------------------------
texture sTex0 :TEX0;
texture2D texDepth: DEPTHBUFFER;
//--------------------------------------------------------------------------------------
// Sampler Inputs
//--------------------------------------------------------------------------------------

sampler2D InputSampler = sampler_state
{
    Texture = (sTex0);
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = POINT;
    AddressU   = Clamp;
	AddressV   = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
	
};

sampler2D SamplerDepth = sampler_state
{
	Texture   = (texDepth);
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

struct VSInput
{
    float3 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
};

struct PSInput
{
    float4 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
};
 
PSInput VertexShaderFunction(VSInput VS)
{
    PSInput PS = (PSInput)0;
    PS.Position = mul(float4(VS.Position, 1), gWorldViewProjection);
    PS.TexCoord = VS.TexCoord;
    return PS;
}

float FetchDepthBufferValue( float2 uv )
{
    float4 texel = tex2D(SamplerDepth, uv);
#if IS_DEPTHBUFFER_RAWZ
    float3 rawval = floor(255.0 * texel.arg + 0.5);
    float3 valueScaler = float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5);
    return dot(rawval, valueScaler / 255.0);
#else
    return texel.r;
#endif
}

float preLinearize(float posZ)
{
    return gProjectionMainScene[3][2] / (posZ - gProjectionMainScene[2][2]);
}

float penta(float2 coords) //pentagonal shape
{
	float scale = float(rings) - 1.3;
	float4  HS0 = float4( 1.0,         0.0,         0.0,  1.0);
	float4  HS1 = float4( 0.309016994, 0.951056516, 0.0,  1.0);
	float4  HS2 = float4(-0.809016994, 0.587785252, 0.0,  1.0);
	float4  HS3 = float4(-0.809016994,-0.587785252, 0.0,  1.0);
	float4  HS4 = float4( 0.309016994,-0.951056516, 0.0,  1.0);
	float4  HS5 = float4( 0.0        ,0.0         , 1.0,  1.0);
	
	float4  one = float4( 1,1,1,1 );
	
	float4 P = float4(coords,float2(scale, scale)); 
	
	float4 dist = float4(0,0,0,0);
	float inorout = -4.0;
	
	dist.x = dot( P, HS0 ); //by Marty McFly
	dist.y = dot( P, HS1 );
	dist.z = dot( P, HS2 );
	dist.w = dot( P, HS3 );
	
	dist = smoothstep( -feather, feather, dist );
	
	inorout += dot( dist, one );
	
	dist.x = dot( P, HS4 );
	dist.y = HS5.w - abs( P.z );
	
	dist = smoothstep( -feather, feather, dist );
	inorout += dist.x;
	
	return saturate( inorout );
}


float linearize(float depth)
{
	return -zfar * znear / (depth * (zfar - znear) - zfar);
}

float2 rand(float2 coord) //generating noise/pattern texture for dithering
{
	float noiseX = ((frac(1.0-coord.x*(screenRes.x/2.0))*0.25)+(frac(coord.y*(screenRes.y/2.0))*0.75))*2.0-1.0;
	float noiseY = ((frac(1.0-coord.x*(screenRes.x/2.0))*0.75)+(frac(coord.y*(screenRes.y/2.0))*0.25))*2.0-1.0;
	
	return float2(noiseX,noiseY);
}

float4 colorDof(float2 coords,float blur) //processing the sample
{
	float4 colDF = float4(0,0,0,1);
	
	colDF.x = tex2D(InputSampler,coords + float2(0.0,1.0)*texel*fringe*blur).x;
	colDF.y = tex2D(InputSampler,coords + float2(-0.866,-0.5)*texel*fringe*blur).y;
	colDF.z = tex2D(InputSampler,coords + float2(0.866,-0.5)*texel*fringe*blur).z; //by Marty McFly
	
	float3 lumcoeff = float3(0.299,0.587,0.114);
	float lum = dot(colDF.xyz,lumcoeff);
	float thresh = max((lum-threshold)*gain, 0.0);
	float3 nullcol = float3(0,0,0);
	colDF.xyz +=lerp(nullcol,colDF.xyz,thresh*blur);
	return colDF;
}

float vignette(float2 coord)
{
	float dist = distance(coord, float2(0.5,0.5));
	dist = smoothstep(vignout+(fstop/MartyMcFly), vignin+(fstop/MartyMcFly), dist);
	return saturate(dist);
}

float4 PixelShaderFunction(PSInput PS) : COLOR0
{




	float depth = linearize(preLinearize(FetchDepthBufferValue(PS.TexCoord.xy)));
	
	float fDepth = focalDepth;
	
	if (autofocus)
	{
		fDepth = linearize(preLinearize(FetchDepthBufferValue(PS.TexCoord.xy)));
	}
	
	float blur = 0.0;
	if (manualdof)
	{    
		float a = depth-fDepth; //focal plane
		float b = (a-fdofstart)/fdofdist; //far DoF
		float c = (-a-ndofstart)/ndofdist; //near Dof
		blur = (a>0.0)?b:c;
	}
	else
	{
		float f = focalLength; //focal length in mm
		float d = fDepth*1000.0; //focal plane in mm
		float o = depth*1000.0; //depth in mm
		
		float a = (o*f)/(o-f); 
		float b = (d*f)/(d-f); 
		float c = (d-f)/(d*fstop*CoC); 
		
		blur = abs(a-b)*c;
	}
	blur = saturate(blur);
	float2 noise = rand(PS.TexCoord.xy)*namount*blur;
	
	float w = (1.0/screenRes.x)*blur*maxblur+noise.x;
	float h = (1.0/screenRes.y)*blur*maxblur+noise.y;
	
	float4 col = float4(0,0,0,1);
	
	if(blur < 0.05) //some optimization thingy
	{
		col = tex2D(InputSampler, PS.TexCoord.xy);
	}
	else
	{
	col = tex2D(InputSampler, PS.TexCoord.xy);
	float s = 1.0;
	int ringsamples;
	for (int i = 1; i <= rings; i += 1)
	{
		ringsamples = i * samples;
		for (int j = 0 ; j < ringsamples ; j += 1)
		{
			float step = PI*2.0 / ringsamples;
			float pw = cos(j*step)*i;
			float ph = sin(j*step)*i;
			float p = 1.0;
			if (pentagon)
			{ 
				p = penta(float2(pw,ph));
			}
			col.xyz += colorDof(PS.TexCoord.xy + float2(pw*w,ph*h),blur).xyz;  
			s += 1.0*lerp(1.0,i/rings,bias)*p;
		}
	}
	col = col/s; //divide by sample count
	}
	
	if (vignetting==true)
	{
		col *= vignette(PS.TexCoord.xy);
	}
	
	return col;
	//return float4(depth,depth,depth,1);
}

technique DoFBokeh
{	
	pass P0
	{
		VertexShader = compile vs_3_0 VertexShaderFunction();
		PixelShader  = compile ps_3_0 PixelShaderFunction();
		CullMode=NONE;
	}
	
}