//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ReShade effect file
// visit facebook.com/MartyMcModding for news/updates
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Multilayered bloom 1.0 by Marty McFly
// Copyright © 2008-2016 Marty McFly
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include "ReShade.fxh"

uniform int BLOOM_BLEND_MODE <
	ui_type = "drag";
	ui_min = 1; ui_max = 8;
	ui_tooltip = "Bloom : Blend modes";
> = 1;

uniform float BLOOM_ECC_IN_BLACK <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Input black level, everything darker than this gets mapped to black.";
> = 0.70;

uniform float BLOOM_ECC_IN_WHITE <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Input white level, a pixel of this brightness  value is mapped to pure white.";
> = 1.00;

uniform float BLOOM_AMOUNT <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Output white level, white output gets mapped to this value.";
> = 0.40;

uniform float BLOOM_CONTRAST <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Bloom contrast, higher values increase strong bloom and decrease weak bloom.";
> = 1.00;

uniform float BLOOM_SATURATION <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Bloom saturation curve. HSV based so no saturation clipping.";
> = 1.00;

uniform float BLOOM_MULT_1024 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 1024x1024 texture.";
> = 0.00;

uniform float BLOOM_MULT_512 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 512x512 texture.";
> = 0.00;

uniform float BLOOM_MULT_256 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 256x256 texture.";
> = 1.00;

uniform float BLOOM_MULT_128 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 128x128 texture.";
> = 1.00;

uniform float BLOOM_MULT_64 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 64x64 texture.";
> = 1.00;

uniform float BLOOM_MULT_32 <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 5.00;
	ui_tooltip = "Bloom : Multiplicator of 32x32 texture.";
> = 1.00;


uniform float BLOOM_BLUR_MULT <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 16.00;
	ui_tooltip = "Bloom : Multiplicator of 32x32 texture.";
> = 1.00;



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

texture2D texResize	{Width = 1024; Height = 1024;Format = RGBA16F;};
texture2D texBloom1024	{Width = 1024; Height = 1024;Format = RGBA16F;};
texture2D texBloom512	{Width = 512;  Height = 512; Format = RGBA16F;};
texture2D texBloom256	{Width = 256;  Height = 256; Format = RGBA16F;};
texture2D texBloom128	{Width = 128;  Height = 128; Format = RGBA16F;};
texture2D texBloom64	{Width = 64;   Height = 64;  Format = RGBA16F;};
texture2D texBloom32	{Width = 32;   Height = 32;  Format = RGBA16F;};

sampler2D SamplerResize   	{ Texture = texResize; 	    };
sampler2D SamplerBloom1024     { Texture = texBloom1024;   };
sampler2D SamplerBloom512      { Texture = texBloom512;    };
sampler2D SamplerBloom256      { Texture = texBloom256;    };
sampler2D SamplerBloom128      { Texture = texBloom128;    };
sampler2D SamplerBloom64       { Texture = texBloom64;     };
sampler2D SamplerBloom32       { Texture = texBloom32;     };

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

float4	McFlyFuncBlur(sampler2D SamplerBloomX, float2 uvsrc, float srcsize, float destsize)
{
	//vogel distribution method
	float4 res = 0.0;

	float2 texelSize =  1.0 / srcsize;
	texelSize.y*=BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
	float sizefact = 1.0 - srcsize/1024.0;
	texelSize.xy *= 12;

	texelSize.xy *= BLOOM_BLUR_MULT;
	

	float GoldenAngle = 2.39996322972865332;
	float PI = 3.14159265359;
	float numSamples = lerp(128.0,12.0,sizefact);

	float2x2 GoldenAngleMatrix = float2x2(cos(GoldenAngle),-sin(GoldenAngle),sin(GoldenAngle),cos(GoldenAngle));
	float2 baseVector = float2(1.0,0.0);

	[loop]
	for(float i=0; i < numSamples; i++)
	{
		float radius = ((i+0.5) / numSamples);

		baseVector = mul(baseVector,GoldenAngleMatrix);

		float2 coord = uvsrc + radius * baseVector * texelSize;
		float weight = saturate(1.0 - sqrt(sqrt(radius)));

		res.xyz += tex2Dlod(SamplerBloomX, float4(coord.xy,0,0)).xyz * weight;
		res.w += weight;
	}

	res.xyz /= res.w + 0.00001;
	return float4(res.xyz,1.0);

}

float3 RGB2HSV(float3 RGB)
{
    	float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    	float4 p = RGB.g < RGB.b ? float4(RGB.bg, K.wz) : float4(RGB.gb, K.xy);
    	float4 q = RGB.r < p.x ? float4(p.xyw, RGB.r) : float4(RGB.r, p.yzx);

    	float d = q.x - min(q.w, q.y);
    	float e = 1.0e-10;
    	return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

float3 HSV2RGB(float3 HSV)
{
    	float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    	float3 p = abs(frac(HSV.xxx + K.xyz) * 6.0 - K.www);
    	return HSV.z * lerp(K.xxx, saturate(p - K.xxx), HSV.y); //HDR capable
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void PS_Bloom_ToDownsampled(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	float4 res = tex2D(ReShade::BackBuffer, texcoord.xy);

	//no hue shift here
	res.xyz = RGB2HSV(res.xyz);
	res.z=max(res.z-BLOOM_ECC_IN_BLACK, 0.0) / max(BLOOM_ECC_IN_WHITE-BLOOM_ECC_IN_BLACK, 0.0001);
	res.z=pow(res.z, BLOOM_CONTRAST);
	res.z *= 5.0 * BLOOM_AMOUNT;	
	//res.z=res.z*(5.0-BLOOM_ECC_OUT_BLACK) + BLOOM_ECC_OUT_BLACK;
	res.y=pow(saturate(res.y),1.0 / (BLOOM_SATURATION + 0.00001));
	res.xyz = HSV2RGB(res.xyz);
	
	bloom = res;
}

void PS_Bloom_To1024(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerResize, texcoord.xy, 1024.0, 1024.0);
}

void PS_Bloom_To512(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerResize, texcoord.xy, 1024.0, 512.0);
}

void PS_Bloom_To256(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerBloom512, texcoord.xy, 512.0, 256.0);
}

void PS_Bloom_To128(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerBloom256, texcoord.xy, 256.0, 128.0);
}

void PS_Bloom_To64(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerBloom128, texcoord.xy, 128.0, 64.0);
}

void PS_Bloom_To32(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 bloom : SV_Target0)
{
	bloom = McFlyFuncBlur(SamplerBloom64, texcoord.xy, 64.0, 32.0);
}

float4 tex2Dbicub(sampler Niggersample, float2 texcoord, float2 texsize)
{
    	float4 uv = 0.0;
    	uv.xy = texcoord * texsize;
   	float2 center  = floor(uv.xy - 0.5) + 0.5;
    	float2 dist1st = uv.xy - center;
    	float2 dist2nd = dist1st * dist1st;
    	float2 dist3rd = dist2nd * dist1st;
   	float2 weight0 =     -dist3rd + 3 * dist2nd - 3 * dist1st + 1;
    	float2 weight1 =  3 * dist3rd - 6 * dist2nd               + 4;
    	float2 weight2 = -3 * dist3rd + 3 * dist2nd + 3 * dist1st + 1;  
    	float2 weight3 =      dist3rd;
    	weight0 += weight1;
    	weight2 += weight3;
       	uv.xy  = center - 1 + weight1 / weight0;
    	uv.zw  = center + 1 + weight3 / weight2;
    	uv    /= texsize.xyxy;
    	return ( weight0.y * ( tex2D( Niggersample, uv.xy) * weight0.x + tex2D( Niggersample, uv.zy) * weight2.x) +
             	 weight2.y * ( tex2D( Niggersample, uv.xw) * weight0.x + tex2D( Niggersample, uv.zw) * weight2.x)) * 0.027777778;
}

uniform float BLOOM_FOG_START <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 1.00;
	ui_tooltip = "Bloom : Start of depth fog.";
> = 0.40;

uniform float BLOOM_FOG_END <
	ui_type = "drag";
	ui_min = 0.00; ui_max = 1.00;
	ui_tooltip = "Bloom : End of depth fog.";
> = 1.00;

uniform float BLOOM_FOG_MULT <
	ui_type = "drag";
	ui_min = -1.00; ui_max = 1.00;
	ui_tooltip = "Bloom : Mult of depth fog.";
> = 1.00;


void PS_Bloom_Combine(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 res : SV_Target0)
{

	float3 bloom = 0.0;
	float3 color = tex2D(ReShade::BackBuffer, texcoord.xy).xyz;

	bloom.xyz = BLOOM_MULT_1024 * tex2Dbicub(SamplerBloom1024, texcoord.xy, float2(1024.0,1024.0)).xyz
	          + BLOOM_MULT_512  * tex2Dbicub(SamplerBloom512,  texcoord.xy, float2(512.0,512.0)).xyz
		  + BLOOM_MULT_256  * tex2Dbicub(SamplerBloom256,  texcoord.xy, float2(256.0,256.0)).xyz
		  + BLOOM_MULT_128  * tex2Dbicub(SamplerBloom128,  texcoord.xy, float2(128.0,128.0)).xyz
		  + BLOOM_MULT_64   * tex2Dbicub(SamplerBloom64,   texcoord.xy, float2(64.0,64.0)).xyz
		  + BLOOM_MULT_32   * tex2Dbicub(SamplerBloom32,   texcoord.xy, float2(32.0,32.0)).xyz;

	bloom.xyz /= BLOOM_MULT_1024
	           + BLOOM_MULT_512
		   + BLOOM_MULT_256
		   + BLOOM_MULT_128
		   + BLOOM_MULT_64
		   + BLOOM_MULT_32
		   + 0.000001; //equalize the result

	bloom.xyz = 1-exp2(-bloom.xyz);

	switch(BLOOM_BLEND_MODE)
	{
		case 1:
		{
			color.xyz = color.xyz + bloom.xyz;
			break;
		}
		case 2:
		{
			color.xyz = 1-(1-color.xyz)*(1-bloom.xyz);
			break;
		}
		case 3:
		{
			 color.xyz = max(0.0f,max(color.xyz,lerp(color.xyz,(1.0f - (1.0f - saturate(bloom.xyz)) *(1.0f - saturate(bloom.xyz * 1.0))),1.0)));
			break;
		}
		case 4:
		{
			color.xyz = max(color.xyz, bloom.xyz);
			break;
		}
		case 5:
		{
			color.xyz += max(bloom.xyz-color.xyz,0.0);
			break;
		}
		case 6:
		{
			color.xyz = bloom.xyz;
			break;
		}
		case 7:
		{
			float lineardepth = ReShade::GetLinearizedDepth(texcoord);
			lineardepth = smoothstep(BLOOM_FOG_START,BLOOM_FOG_END, lineardepth);
			lineardepth *= BLOOM_FOG_MULT;
			color.xyz = lerp(color.xyz,bloom.xyz,lineardepth);
			break;
		}
		case 8:
		{
			color.xyz = color.xyz;
			break;
		}
		case 9:
		{
			color.xyz = lerp(color.xyz + bloom.xyz, 1-(1-color.xyz)*(1-bloom.xyz), 0.5);
			break;
		}
	}

	res.xyz = color.xyz;
	res.w = 1.0;
		
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

technique Bloom
{
	pass P0
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_ToDownsampled;
		RenderTarget  = texResize;
	}
	pass P1
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To1024;
		RenderTarget  = texBloom1024;
	}
	pass P2
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To512;
		RenderTarget  = texBloom512;
	}
	pass P3
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To256;
		RenderTarget  = texBloom256;
	}
	pass P4
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To128;
		RenderTarget  = texBloom128;
	}
	pass P5
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To64;
		RenderTarget  = texBloom64;
	}
	pass P6
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_To32;
		RenderTarget  = texBloom32;
	}
	pass Combine
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_Bloom_Combine;
	}
}