/**
* Maded by Ganossa () ported by IDDQD
*/

uniform bool mfDebug <
ui_type = "slider";
ui_items = "Off\0On";
ui_tooltip = "Activates debug mode of MF";
> = false;

uniform float mfFocusStrength <
ui_type = "slider";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "The intensity with which the camera will follow motion";
> = 1.0;

uniform float mfZoomStrength <
ui_type = "slider";
ui_min = 0.0; ui_max = 1.0;
ui_tooltip = "The intensity of camera zoom to objects in motion";
> = 0.60;

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

#include "ReShade.fxh"

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

#define Ganossa_MF_BUFFERX BUFFER_WIDTH/2
#define Ganossa_MF_BUFFERY BUFFER_HEIGHT/2

#define Ganossa_MF_BUFFERXHalf Ganossa_MF_BUFFERX/2
#define Ganossa_MF_BUFFERYHalf Ganossa_MF_BUFFERY/2

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

texture2D Ganossa_MF_NormTex { Width = Ganossa_MF_BUFFERX; Height = Ganossa_MF_BUFFERY; Format = RGBA8; };
texture2D Ganossa_MF_PrevTex { Width = Ganossa_MF_BUFFERX; Height = Ganossa_MF_BUFFERY; Format = RGBA8; };
texture2D Ganossa_MF_QuadFullTex { Width = Ganossa_MF_BUFFERX; Height = Ganossa_MF_BUFFERY; Format = RGBA8; };
texture2D Ganossa_MF_QuadFullPrevTex { Width = Ganossa_MF_BUFFERX; Height = Ganossa_MF_BUFFERY; Format = RGBA8; };
texture2D Ganossa_MF_QuadTex { Width = Ganossa_MF_BUFFERX; Height = Ganossa_MF_BUFFERY; Format = RGBA16F; };

sampler2D Ganossa_MF_NormColor { Texture = Ganossa_MF_NormTex; };
sampler2D Ganossa_MF_PrevColor { Texture = Ganossa_MF_PrevTex; };
sampler2D Ganossa_MF_QuadFullColor { Texture = Ganossa_MF_QuadFullTex; };
sampler2D Ganossa_MF_QuadFullPrevColor { Texture = Ganossa_MF_QuadFullPrevTex; };
sampler2D Ganossa_MF_QuadColor { Texture = Ganossa_MF_QuadTex; };

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

#define xSprint BUFFER_WIDTH/192f
#define ySprint BUFFER_HEIGHT/108f

void PS_MotionFocusNorm(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 normR : SV_Target0)
{
normR = tex2D(ReShade::BackBuffer, texcoord);
}

void PS_MotionFocusQuadFull(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 quadFullR : SV_Target0)
{
const float3 orig = tex2D(Ganossa_MF_NormColor, texcoord).rgb;
const float3 prev = tex2D(Ganossa_MF_PrevColor, texcoord).rgb;
const float diff = (abs(orig.r-prev.r)+abs(orig.g-prev.g)+abs(orig.b-prev.b))/3f;

const float3 quadFullPrev = tex2D(Ganossa_MF_QuadFullPrevColor,texcoord).rgb;

const float3 quadFull = 0.968*quadFullPrev + float3(diff,diff,diff);

const float3 quadFulldiff = float3(abs(quadFull.r-quadFullPrev.r),abs(quadFull.g-quadFullPrev.g),abs(quadFull.b-quadFullPrev.b));

quadFullR = float4((0.978-0.2*max(1.0f-pow(1.0f-quadFulldiff,2)*100000f,0))*quadFullPrev + float3(diff,diff,diff),1);

}

void PS_MotionFocusStorage(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 prevR : SV_Target0, out float4 prevQuadFullR : SV_Target1)
{
prevR = tex2D(Ganossa_MF_NormColor, texcoord);
prevQuadFullR = tex2D(Ganossa_MF_QuadFullColor, texcoord);
}

void PS_MotionFocus(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 quadR : SV_Target0)
{
quadR = float4(0,0,0,0);

if (!(texcoord.x <= BUFFER_RCP_WIDTH*2 && texcoord.y <= BUFFER_RCP_HEIGHT*2))
discard;

float2 coord = float2(0.0,0.0);

for (float i = 2.0f; i < Ganossa_MF_BUFFERX; i=i+xSprint)
{
coord.x = BUFFER_RCP_WIDTH*i*2;

[unroll]
for (float j = 2.0f; j < Ganossa_MF_BUFFERY; j=j+ySprint )
{
coord.y = BUFFER_RCP_HEIGHT*j*2;
float3 quadFull = tex2D(Ganossa_MF_QuadFullColor, coord).xyz;
float quadFullPow = quadFull.x+quadFull.y+quadFull.z;

if(i < Ganossa_MF_BUFFERXHalf && j < Ganossa_MF_BUFFERYHalf)
quadR.x += quadFullPow;
else if(i > Ganossa_MF_BUFFERXHalf && j < Ganossa_MF_BUFFERYHalf)
quadR.y += quadFullPow;
else if(i < Ganossa_MF_BUFFERXHalf && j > Ganossa_MF_BUFFERYHalf)
quadR.z += quadFullPow;
else
quadR.w += quadFullPow;
}
}
quadR.xyzw /= 5184f;
}

float4 PS_MotionFocusDisplay(float4 vpos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
float4 Ganossa_MF_Quad = saturate(tex2D(Ganossa_MF_QuadColor, float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT))-0.1f);

if (mfDebug)
{
//debug
if(texcoord.y < 0.01f) if(texcoord.x > Ganossa_MF_Quad.x-0.01f && texcoord.x < Ganossa_MF_Quad.x+0.01f) return float4(1,0,0,0);
if(texcoord.y > 0.01f && texcoord.y < 0.02f) if(texcoord.x > Ganossa_MF_Quad.y-0.01f && texcoord.x < Ganossa_MF_Quad.y+0.01f) return float4(0,1,0,0);
if(texcoord.y > 0.02f && texcoord.y < 0.03f) if(texcoord.x > Ganossa_MF_Quad.z-0.01f && texcoord.x < Ganossa_MF_Quad.z+0.01f) return float4(0,0,1,0);
if(texcoord.y > 0.03f && texcoord.y < 0.04f) if(texcoord.x > Ganossa_MF_Quad.w-0.01f && texcoord.x < Ganossa_MF_Quad.w+0.01f) return float4(1,1,0,0);
//debug
}

float2 focus = 0.5f + float2(max(min(0.5,(Ganossa_MF_Quad.y + Ganossa_MF_Quad.w - Ganossa_MF_Quad.x - Ganossa_MF_Quad.z)/2f),-1.0),max(min(0.5,(Ganossa_MF_Quad.z + Ganossa_MF_Quad.w - Ganossa_MF_Quad.x - Ganossa_MF_Quad.y)/2f),-0.5));

float focusPow = max(Ganossa_MF_Quad.x,max(Ganossa_MF_Quad.y,max(Ganossa_MF_Quad.z,Ganossa_MF_Quad.w)));

float focusPowDiff = 1.0f;
if (focusPow == Ganossa_MF_Quad.x) focusPowDiff += Ganossa_MF_Quad.x-(Ganossa_MF_Quad.y + Ganossa_MF_Quad.z + Ganossa_MF_Quad.w)/3f;
else if(focusPow == Ganossa_MF_Quad.y) focusPowDiff += Ganossa_MF_Quad.y-(Ganossa_MF_Quad.x + Ganossa_MF_Quad.z + Ganossa_MF_Quad.w)/3f;
else if(focusPow == Ganossa_MF_Quad.z) focusPowDiff += Ganossa_MF_Quad.z-(Ganossa_MF_Quad.x + Ganossa_MF_Quad.y + Ganossa_MF_Quad.w)/3f;
else focusPowDiff += Ganossa_MF_Quad.w-(Ganossa_MF_Quad.y + Ganossa_MF_Quad.z + Ganossa_MF_Quad.x)/3f;

float focusPowFull = 0.5f*max(1.0f,min(2.0f - pow((Ganossa_MF_Quad.x + Ganossa_MF_Quad.y + Ganossa_MF_Quad.z + Ganossa_MF_Quad.w)/4f,3),1.0f));

if (mfDebug)
{
//debug
if(texcoord.x < 0.5025f && texcoord.x > 0.4975f && texcoord.y < 0.505f && texcoord.y > 0.495f) return float4(0,1,0,0);
if(texcoord.x > pow(focus.x,2)+0.25f-0.0025f && texcoord.x < pow(focus.x,2)+0.25f+0.0025f && texcoord.y > pow(focus.y,2)+0.25f-0.005f && texcoord.y < pow(focus.y,2)+0.25f+0.005f) return float4(1,0,0,0);
//debug
}
const float2 finalZoom = focusPow*focusPowDiff*focusPowFull*mfZoomStrength;
const float2 finalFocus = focus*focusPow*pow(focusPowDiff,3)*focusPowFull*mfFocusStrength;

const float2 focusCorrection = min(0,float2(1,1)-(float2(1,1)*(1.0f-finalZoom)+finalFocus*min(0.55,0.6*mfZoomStrength)));

return tex2D(ReShade::BackBuffer, texcoord*(1.0f-finalZoom)+finalFocus*min(0.55,0.6*mfZoomStrength)+focusCorrection);
}

technique GanossaMotionFocus
{
pass MotionFocusNormPass
{
VertexShader = PostProcessVS;
PixelShader = PS_MotionFocusNorm;
RenderTarget0 = Ganossa_MF_NormTex;
}

pass MotionFocusQuadFullPass
{
VertexShader = PostProcessVS;
PixelShader = PS_MotionFocusQuadFull;
RenderTarget0 = Ganossa_MF_QuadFullTex;
}

pass MotionFocusPass
{
VertexShader = PostProcessVS;
PixelShader = PS_MotionFocus;
RenderTarget0 = Ganossa_MF_QuadTex;
}

pass MotionFocusDisplayPass
{
VertexShader = PostProcessVS;
PixelShader = PS_MotionFocusDisplay;
}

pass MotionFocusStoragePass
{
VertexShader = PostProcessVS;
PixelShader = PS_MotionFocusStorage;
RenderTarget0 = Ganossa_MF_PrevTex;
RenderTarget1 = Ganossa_MF_QuadFullPrevTex;
}
}