//+++++++++++++++++++++++++++++++++++
//ENB Bloom from GTAIV by Boris Vorontsov
//Modified by iCE La GlacE
//Contains Anamorphic lens flare by Matso
//Bloom modified by Fabriciuz
//Using lenz/offsets code from Skyrim
//
//not finished
//+++++++++++++++++++++++++++++++++++


//global variable externally set
float4	BloomParameters; //BloomRadius1, BloomRadius2, BloomBlueShiftAmount, BloomContrast
float4	Timer;
// Lensflareintensity
float4	LenzParameters = 1.50; // Lens flare intensity
float4	TempParameters;
float4	ScreenSize;
float4 PrePass = 0.12; // Pre-pass bloom intensity
float4 Pass1 = 0.1; // First pass intensity
float4 Pass2 = 0.1; //  Second pass intensity
//Anamorphic setting
float fLuminance = 1; //Higher it is, less affect the effect is by luminance					
float fBlur = 1000.0;							
float fIntensity = 0.33; // Anamorphic intensity


//x=generic timer in range 0..1, period of 16777216 ms (4.6 hours)
//w=frame time elapsed (in seconds)



//quad
struct VS_OUTPUT_POST
{
	float4 vpos  : POSITION;
	float2 txcoord0 : TEXCOORD0;
};
struct VS_INPUT_POST
{
	float3 pos  : POSITION;
	float2 txcoord0 : TEXCOORD0;
};



texture2D texBloom1;
texture2D texBloom2;
texture2D texBloom3;
texture2D texBloom4;
texture2D texBloom5;

sampler2D SamplerBloom1 = sampler_state
{
    Texture   = <texBloom1>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerBloom2 = sampler_state
{
    Texture   = <texBloom2>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerBloom3 = sampler_state
{
    Texture   = <texBloom3>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerBloom4 = sampler_state
{
    Texture   = <texBloom4>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerBloom5 = sampler_state
{
    Texture   = <texBloom5>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};



//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VS_OUTPUT_POST VS_Bloom(VS_INPUT_POST IN)
{
	VS_OUTPUT_POST OUT;

	OUT.vpos=float4(IN.pos.x,IN.pos.y,IN.pos.z,1.0);

	OUT.txcoord0.xy=IN.txcoord0.xy+TempParameters.xy;//1.0/(bloomtexsize*2.0)

	return OUT;
}



//zero pass HQ, input texture is fullscreen
//SamplerBloom1 - fullscreen texture
float4 PS_BloomPrePass(VS_OUTPUT_POST In) : COLOR
{
	float4 bloomuv;

	float4 bloom=tex2D(SamplerBloom1, In.txcoord0);
	const float2 offset[4]=
	{
		float2(0.25, 0.25),
		float2(0.25, -0.25),
		float2(-0.25, 0.25),
		float2(-0.25, -0.25)
	};
	float2 screenfact=1.0;
	screenfact.y*=ScreenSize.z;
	screenfact.xy*=TempParameters.z*1;
	float4 srcbloom=bloom;
	for (int i=0; i<4; i++)
	{
		bloomuv.xy=offset[i];
		bloomuv.xy=(bloomuv.xy*screenfact.xy)+In.txcoord0.xy;
		float4 tempbloom=tex2D(SamplerBloom1, bloomuv.xy);
		bloom.xyz+=tempbloom.xyz;
	}
	bloom*=PrePass;//0.125;

	bloom.w=1.0;
	return bloom;
}



//first and second passes draw to every texture
//twice, after computations of these two passes,
//result is set as input to next cycle

//first pass
//SamplerBloom1 is result of prepass or second pass from cycle
float4 PS_BloomTexture1(VS_OUTPUT_POST In) : COLOR
{
	float4 bloomuv;

	float4 bloom=tex2D(SamplerBloom1, In.txcoord0);
	const float2 offset[8]=
	{
		float2(1.0, 1.0),
		float2(1.0, -1.0),
		float2(-1.0, 1.0),
		float2(-1.0, -1.0),
		float2(0.0, 1.0),
		float2(0.0, -1.0),
		float2(1.0, 0.0),
		float2(-1.0, 0.0)
	};
	float2 screenfact=1.0;
	screenfact.y*=ScreenSize.z;
	screenfact.xy/=ScreenSize.x;
	float4 srcbloom=bloom;
	float step=(TempParameters.w-0.5);//*0.5;
	screenfact.xy*=step;

	float4 bloomadd=bloom;
	for (int i=0; i<8; i++)
	{
		bloomuv.xy=offset[i]*BloomParameters.x;
		bloomuv.xy=(bloomuv.xy*screenfact.xy)+In.txcoord0.xy;//-(1.0/256.0);//-(1.0/512.0);
		//v1
//		bloomadd+=tex2D(SamplerBloom1, bloomuv.xy);
		//v2
		float4 tempbloom=tex2D(SamplerBloom1, bloomuv.xy);
		bloomadd+=tempbloom;
//		float fgr=dot(bloom.xyz, 0.333);
		bloom.xyz=max(bloom.xyz, tempbloom.xyz*0.99);
	}
	//v1
	bloomadd*=Pass1;
	//v0
	bloom.xyz=lerp(bloomadd.xyz, bloom.xyz, BloomParameters.w);


	//float3 violet=float3(0.78, 0.5, 1.0);
	//float3 violet=float3(0.6, 0.4, 1.0);//v2
	float3 violet=float3(0.6, 0.4, 1.0);//v3

	//this applies when white
	//float gray=0.104*dot(srcbloom.xyz, 0.333);//max(srcbloom.x, max(srcbloom.y, srcbloom.z));
	//this applies on dark and when contrast
	float ttt=dot(bloom.xyz, 0.333)-dot(srcbloom.xyz, 0.333);
	ttt=max(ttt, 0.0);
	float gray=BloomParameters.z*ttt;//max(srcbloom.x, max(srcbloom.y, srcbloom.z));
	float mixfact=(gray/(1.0+gray));
	mixfact*=1.0-saturate((TempParameters.w-1.0)*0.2);
	violet.xy+=saturate((TempParameters.w-1.0)*0.3);
	violet.xy=saturate(violet.xy);
	bloom.xyz*=lerp(1.0, violet.xyz, mixfact);

	bloom.w=1.0;
	return bloom;
}


//second pass
//SamplerBloom1 is result of first pass
float4 PS_BloomTexture2(VS_OUTPUT_POST In) : COLOR
{
	float4 bloomuv;

	float4 bloom=tex2D(SamplerBloom1, In.txcoord0);
	const float2 offset[8]=
	{
		float2(1.0, 1.0),
		float2(1.0, -1.0),
		float2(-1.0, 1.0),
		float2(-1.0, -1.0),
		float2(0.0, 1.0),
		float2(0.0, -1.0),
		float2(1.0, 0.0),
		float2(-1.0, 0.0)
	};
	float2 screenfact=1.0;
	screenfact.y*=ScreenSize.z;
	screenfact.xy/=ScreenSize.x;
	float4 srcbloom=bloom;

	float step=(TempParameters.w-0.5)*1;//v3
	screenfact.xy*=step;
	float4 rotvec=0.0;
	//sincos(0.19635, rotvec.x, rotvec.y);
	sincos(0.3927, rotvec.x, rotvec.y);
	for (int i=0; i<8; i++)
	{
		bloomuv.xy=offset[i];
		bloomuv.xy=reflect(bloomuv.xy, rotvec.xy);
		bloomuv.xy*=BloomParameters.y;
		//separate code is much faster without constant table operations
		bloomuv.xy=(bloomuv.xy*screenfact.xy)+In.txcoord0.xy;//-(1.0/256.0);//-(1.0/512.0);
		bloom+=tex2D(SamplerBloom1, bloomuv.xy);
	}
	bloom*=Pass2;//0.125;

	bloom.w=1.0;
	return bloom;
}



//last pass, mix several bloom textures
//SamplerBloom5 is the result of prepass
float4 PS_BloomPostPass(VS_OUTPUT_POST In) : COLOR
{
	float4 bloom;
/*
	//v1
	bloom =tex2D(SamplerBloom1, In.txcoord0);
	bloom+=tex2D(SamplerBloom2, In.txcoord0);
	bloom+=tex2D(SamplerBloom3, In.txcoord0);
	bloom+=tex2D(SamplerBloom4, In.txcoord0);
	bloom*=0.25;
*/
	//v2
	float4 bloom1=tex2D(SamplerBloom1, In.txcoord0);
	float4 bloom2=tex2D(SamplerBloom2, In.txcoord0);
	float4 bloom3=tex2D(SamplerBloom3, In.txcoord0);
	float4 bloom4=tex2D(SamplerBloom4, In.txcoord0);
	float4 bloom5=tex2D(SamplerBloom5, In.txcoord0);
	bloom=max(bloom1, bloom2);
	bloom=max(bloom, bloom3);
	bloom=max(bloom, bloom4);
	bloom=max(bloom, bloom5);

	float3 lenz=0;
	float2 lenzuv=0.0;



	const float3 offset[15]=
	{
		float3(0.3, 0.01, 4),
		float3(0.7, 0.25, 25),
		float3(0.3, 0.25, 15),
// Full
		float3(1, 1.0, 5),
// Arriere
		float3(-0.15, 20, 1),
		float3(-0.3, 20, 1),
// Avant
		float3(0.5, 0.1, 1),
		float3(0.01, 10, 1),
		float3(20, 0.25, 1),
		float3(1000, 5, 100),
//Plein ecran passe inverse
		float3(0.5, -0.5, 2),
//Rond bleu du debut
		float3(2, 2, -5),
//2eme partie de la passe FS
		float3(-5, 0.2, 0.2),
//Derniere passe
		float3(0.15, 0.5, 20),
		float3(0.4, 1, 10)
	};

	const float3 factors[15]=
	{
		float3(0.5, 0.5, 0),
		float3(0, 0.5, 0),
		float3(0, 0, 0.5),
// Full
		float3(0.2, 0.25, 0),
// Arriere
		float3(0.15, 0, 0.0),
		float3(0, 0.0, 0.15),
// Avant
		float3(0.2, 0.2, 0.05),
		float3(0.25, 0.25, 0.25),
		float3(1, 1, 1),
		float3(0, 0.25, 1),
//Plein ecran
		float3(0, 0,0.25),
//Rond bleu au debut
		float3(0, 0, 1),
//Derniere passe avec passe full combine
		float3(2, 2, 2),
		float3(1, 1, 0.25),
		float3(0, 0, 0)

	};

//lenzuv.xy=0.5-lenzuv.xy;
//distfact=0.5-lenzuv.xy-0.5;

	if (LenzParameters.x>0.00001)
	{
		for (int i=0; i<15; i++)
		{
			float2 distfact=(In.txcoord0.xy-0.5);
			lenzuv.xy=offset[i].x*distfact;
			lenzuv.xy*=pow(2.0*length(float2(distfact.x*ScreenSize.z,distfact.y)), offset[i].y);
			lenzuv.xy*=offset[i].z;
			//lenzuv.xy=0.5-lenzuv.xy;//v1
			lenzuv.xy=In.txcoord0.xy-lenzuv.xy;//v2
			float3 templenz=tex2D(SamplerBloom2, lenzuv.xy);
			templenz=templenz*factors[i];
			distfact=(lenzuv.xy-0.5);
			distfact*=2.0;
			templenz*=saturate(1.0-dot(distfact,distfact));//limit by uv 0..1
	//		templenz=factors[i] * (1.0-dot(distfact,distfact));
			float maxlenz=max(templenz.x, max(templenz.y, templenz.z));
/*			float3 tempnor=(templenz.xyz/maxlenz);
			tempnor=pow(tempnor, tempF1.z);
			templenz.xyz=tempnor.xyz*maxlenz;
*/
			float tempnor=(maxlenz/(1.0+maxlenz));
			tempnor=pow(tempnor, LenzParameters.y);
			templenz.xyz*=tempnor;

	//		templenz*=maxlenz*maxlenz;
			lenz+=templenz;
	//		lenz.xyz=max(lenz.xyz, templenz.xyz*0.99);
		}
		lenz.xyz*=0.25*LenzParameters.x;

		bloom.xyz+=lenz.xyz;
//		bloom.w=dot(lenz.xyz, 0.333);
		bloom.w=max(lenz.xyz, max(lenz.y, lenz.z));
	}
	return bloom;
}

float3 BrightPass(float2 tex)
{
	float3 c = tex2D(SamplerBloom1, tex).rgb;
	//c*=(1-pow(2.7, -c *10));
  	 float3 bC = max(c - float3(fLuminance, fLuminance, fLuminance), 0.0);
   	 float bright = dot(bC, 1.3);
   	 bright = smoothstep(0.0f, 0.5f, bright);
   	 return lerp(0.0, c, bright);
}
float3 AnamorphicSample(int axis, float2 tex, float blur)
{
	float2 oritex=tex;
	tex = 2.0 * tex - 1.0;
	blur*=pow(tex-oritex,2);
	if (!axis) tex.x /= -blur;
	else tex.y /= -blur;
	tex = 0.5 * tex + 0.5;
	return BrightPass(tex);
}

float4 PS_ProcessPass_Anamorphic(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{
	float4 res;
	float2 coord = IN.txcoord0.y;
	float3 anamFlare = AnamorphicSample(0, coord.xy, fBlur) * float3(0.31, 0.21, 0.70);
	res.rgb = anamFlare * fIntensity;
	res.a = 1.0;
	return res;
}

technique BloomPrePass
{
    pass p0
    {
	VertexShader = compile vs_3_0 VS_Bloom();
	PixelShader  = compile ps_3_0 PS_BloomPrePass();

	COLORWRITEENABLE=ALPHA|RED|GREEN|BLUE;
	CullMode=NONE;
	AlphaBlendEnable=FALSE;
	AlphaTestEnable=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
	}
}

technique BloomTexture1
{
    pass p0
    {
	VertexShader = compile vs_3_0 VS_Bloom();
	PixelShader  = compile ps_3_0 PS_BloomTexture1();

	COLORWRITEENABLE=ALPHA|RED|GREEN|BLUE;
	CullMode=NONE;
	AlphaBlendEnable=FALSE;
	AlphaTestEnable=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
	}
}


technique BloomTexture2
{
    pass p0
    {
	VertexShader = compile vs_3_0 VS_Bloom();
	PixelShader  = compile ps_3_0 PS_BloomTexture2();

	COLORWRITEENABLE=ALPHA|RED|GREEN|BLUE;
	CullMode=NONE;
	AlphaBlendEnable=FALSE;
	AlphaTestEnable=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
	}
}

technique BloomPostPass
{
	 pass p0
	{
		VertexShader = compile vs_3_0 VS_Bloom();
		PixelShader  = compile ps_3_0 PS_BloomPostPass();
		
	COLORWRITEENABLE=ALPHA|RED|GREEN|BLUE;
	CullMode=NONE;
	AlphaBlendEnable=FALSE;
	AlphaTestEnable=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;

	}

	pass p1
	{
			AlphaBlendEnable = true;
			SrcBlend = One;
			DestBlend = One;

		VertexShader = compile vs_3_0 VS_Bloom();
		PixelShader  = compile ps_3_0 PS_ProcessPass_Anamorphic();

	}
}


