/* 
BSL Shaders v8 Series by Capt Tatsu 
https://bitslablab.com 
*/ 

vec2 neighbourOffsets[8] = vec2[8](
	vec2( 0.0, -1.0),
	vec2(-1.0,  0.0),
	vec2( 1.0,  0.0),
	vec2( 0.0,  1.0),
	vec2(-1.0, -1.0),
	vec2( 1.0, -1.0),
	vec2(-1.0,  1.0),
	vec2( 1.0,  1.0)
);

vec3 GetBlurredColor(vec2 view) {
	float blurFactor = 0.1667;
	vec3 color = texture2DLod(colortex1, texCoord + neighbourOffsets[4] * blurFactor / view, 0).rgb;
		 color+= texture2DLod(colortex1, texCoord + neighbourOffsets[5] * blurFactor / view, 0).rgb;
		 color+= texture2DLod(colortex1, texCoord + neighbourOffsets[6] * blurFactor / view, 0).rgb;
		 color+= texture2DLod(colortex1, texCoord + neighbourOffsets[7] * blurFactor / view, 0).rgb;
		 
	color /= 4.0;

	return color;
}

#ifdef TAA_SELECTIVE
float GetSkipFlag(float depth, vec2 view) {
	float skip = texture2D(colortex3, texCoord.xy).b;
	float skipDepth = depth;

	for (int i = 0; i < 4; i++) {
		float sampleDepth = texture2D(depthtex1, texCoord + neighbourOffsets[i + 4] / view).r;
		float sampleSkip = texture2D(colortex3, texCoord + neighbourOffsets[i + 4] / view).b;

		skip = (sampleDepth < skipDepth && sampleSkip == 0) ? 0 : skip;
		skipDepth = min(skipDepth, sampleDepth);
	}

	return skip;
}
#endif

//Previous frame reprojection from Chocapic13
vec2 Reprojection(vec3 pos) {
	pos = pos * 2.0 - 1.0;

	vec4 viewPosPrev = gbufferProjectionInverse * vec4(pos, 1.0);
	viewPosPrev /= viewPosPrev.w;
	viewPosPrev = gbufferModelViewInverse * viewPosPrev;

	vec3 cameraOffset = cameraPosition - previousCameraPosition;
	cameraOffset *= float(pos.z > 0.56);

	vec4 previousPosition = viewPosPrev + vec4(cameraOffset, 0.0);
	previousPosition = gbufferPreviousModelView * previousPosition;
	previousPosition = gbufferPreviousProjection * previousPosition;
	return previousPosition.xy / previousPosition.w * 0.5 + 0.5;
}

//Catmull-Rom sampling from Filmic SMAA presentation
vec3 textureCatmullRom(sampler2D colortex, vec2 texcoord, vec2 view) {
    vec2 position = texcoord * view;
    vec2 centerPosition = floor(position - 0.5) + 0.5;
    vec2 f = position - centerPosition;
    vec2 f2 = f * f;
    vec2 f3 = f * f2;

    float c = 0.7;
    vec2 w0 =        -c  * f3 +  2.0 * c         * f2 - c * f;
    vec2 w1 =  (2.0 - c) * f3 - (3.0 - c)        * f2         + 1.0;
    vec2 w2 = -(2.0 - c) * f3 + (3.0 -  2.0 * c) * f2 + c * f;
    vec2 w3 =         c  * f3 -                c * f2;

    vec2 w12 = w1 + w2;
    vec2 tc12 = (centerPosition + w2 / w12) / view;

    vec2 tc0 = (centerPosition - 1.0) / view;
    vec2 tc3 = (centerPosition + 2.0) / view;
    vec4 color = vec4(texture2DLod(colortex, vec2(tc12.x, tc0.y ), 0).gba, 1.0) * (w12.x * w0.y ) +
                 vec4(texture2DLod(colortex, vec2(tc0.x,  tc12.y), 0).gba, 1.0) * (w0.x  * w12.y) +
                 vec4(texture2DLod(colortex, vec2(tc12.x, tc12.y), 0).gba, 1.0) * (w12.x * w12.y) +
                 vec4(texture2DLod(colortex, vec2(tc3.x,  tc12.y), 0).gba, 1.0) * (w3.x  * w12.y) +
                 vec4(texture2DLod(colortex, vec2(tc12.x, tc3.y ), 0).gba, 1.0) * (w12.x * w3.y );
    return color.rgb / color.a;
}

vec3 RGBToYCoCg(vec3 col) {
	return vec3(
		col.r * 0.25 + col.g * 0.5 + col.b * 0.25,
		col.r * 0.5 - col.b * 0.5,
		col.r * -0.25 + col.g * 0.5 + col.b * -0.25
	);
}

vec3 YCoCgToRGB(vec3 col) {
	float n = col.r - col.b;
	return vec3(n + col.g, col.r + col.b, n - col.g);
}

vec3 ClipAABB(vec3 q,vec3 aabb_min, vec3 aabb_max){
	vec3 p_clip = 0.5 * (aabb_max + aabb_min);
	vec3 e_clip = 0.5 * (aabb_max - aabb_min) + 0.00000001;

	vec3 v_clip = q - vec3(p_clip);
	vec3 v_unit = v_clip.xyz / e_clip;
	vec3 a_unit = abs(v_unit);
	float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z));

	if (ma_unit > 1.0)
		return vec3(p_clip) + v_clip / ma_unit;
	else
		return q;
}

vec3 NeighbourhoodClipping(vec3 color, vec3 tempColor, vec2 view) {
	vec3 minclr = RGBToYCoCg(color);
	vec3 maxclr = minclr;

	for(int i = 0; i < 8; i++) {
		vec2 offset = neighbourOffsets[i] * view;
		vec3 clr = texture2DLod(colortex1, texCoord + offset, 0.0).rgb;

		clr = RGBToYCoCg(clr);
		minclr = min(minclr, clr); maxclr = max(maxclr, clr);
	}

	tempColor = RGBToYCoCg(tempColor);
	tempColor = ClipAABB(tempColor, minclr, maxclr);

	return YCoCgToRGB(tempColor);
}

vec4 TemporalAA(inout vec3 color, float tempData) {
	vec2 view = vec2(viewWidth, viewHeight);

	vec3 blur = GetBlurredColor(view);
	float depth = texture2D(depthtex1, texCoord).r;

	#ifdef TAA_SELECTIVE
	float skip = GetSkipFlag(depth, view);

	if (skip > 0.0) {
		color = blur;
		return vec4(tempData, vec3(0.0));
	}
	#endif

	vec3 coord = vec3(texCoord, depth);
	vec2 prvCoord = Reprojection(coord);
	
	vec3 tempColor = textureCatmullRom(colortex2, prvCoord, view);

	if(tempColor == vec3(0.0)) {
		color = blur;
		return vec4(tempData, color);
	}

	vec3 tempColorRaw = tempColor;
	tempColor = NeighbourhoodClipping(color, tempColor, 1.0 / view);
	
	#if TAA_MODE == 0
	vec2 velocity = (texCoord - prvCoord.xy) * view;
	float blendFactor = float(
		prvCoord.x > 0.0 && prvCoord.x < 1.0 &&
		prvCoord.y > 0.0 && prvCoord.y < 1.0
	);
	blendFactor *= exp(-length(velocity)) * 0.2 + 0.7;
	
	color = mix(color, tempColor, blendFactor);
	#endif

	vec3 outColor = color;
	
	#if TAA_MODE == 1
	color = mix(color, tempColor, 0.5);
	#endif

	return vec4(tempData, outColor);
}