﻿#pragma kernel CSMain

RWTexture2D<float4> Result;

float Width;
float Height;

float Rotation;

float Hue;
float Saturation;
float Brightness;
float2 Scale;
float2 Offset;

Texture2D<float4> R_Input_0;
Texture2D<float4> R_Input_1;
Texture2D<float4> R_Input_2;
Texture2D<float4> R_Input_3;
SamplerState sampler_R_Input_0;
SamplerState sampler_R_Input_1;
SamplerState sampler_R_Input_2;
SamplerState sampler_R_Input_3;
int R_Channel_0;
int R_Channel_1;
int R_Channel_2;
int R_Channel_3;
int R_Count;
int R_BlendMode;
float R_Fallback;
bool R_Invert;

Texture2D<float4> G_Input_0;
Texture2D<float4> G_Input_1;
Texture2D<float4> G_Input_2;
Texture2D<float4> G_Input_3;
SamplerState sampler_G_Input_0;
SamplerState sampler_G_Input_1;
SamplerState sampler_G_Input_2;
SamplerState sampler_G_Input_3;
int G_Channel_0;
int G_Channel_1;
int G_Channel_2;
int G_Channel_3;
int G_Count;
int G_BlendMode;
float G_Fallback;
bool G_Invert;

Texture2D<float4> B_Input_0;
Texture2D<float4> B_Input_1;
Texture2D<float4> B_Input_2;
Texture2D<float4> B_Input_3;
SamplerState sampler_B_Input_0;
SamplerState sampler_B_Input_1;
SamplerState sampler_B_Input_2;
SamplerState sampler_B_Input_3;
int B_Channel_0;
int B_Channel_1;
int B_Channel_2;
int B_Channel_3;
int B_Count;
int B_BlendMode;
float B_Fallback;
bool B_Invert;

Texture2D<float4> A_Input_0;
Texture2D<float4> A_Input_1;
Texture2D<float4> A_Input_2;
Texture2D<float4> A_Input_3;
SamplerState sampler_A_Input_0;
SamplerState sampler_A_Input_1;
SamplerState sampler_A_Input_2;
SamplerState sampler_A_Input_3;
int A_Channel_0;
int A_Channel_1;
int A_Channel_2;
int A_Channel_3;
int A_Count;
int A_BlendMode;
float A_Fallback;
bool A_Invert;

// blendmodes
// 0 = add
// 1 = multiply
// 3 = max
// 4 = min

float SampleTexture(Texture2D<float4> tex, SamplerState texSampler, int channel, float2 uv)
{
	float4 pixelColor = tex.SampleLevel(texSampler,uv,0);
	if (channel == 0) return pixelColor.r;
	else if (channel == 1) return pixelColor.g;
	else if (channel == 2) return pixelColor.b;
	else if (channel == 3) return pixelColor.a;
	else return max(pixelColor.r, max(pixelColor.g, pixelColor.b));
}

float SampleAndBlendTexture(float value, Texture2D<float4> tex, SamplerState texSampler, int blendMode, int channel, float2 uv)
{
	float newValue = SampleTexture(tex, texSampler, channel, uv);
	if (blendMode == 0) return value + newValue;
	else if (blendMode == 1) return value * newValue;
	else if (blendMode == 2) return max(value, newValue);
	else if (blendMode == 3) return min(value, newValue);
	else return newValue;
}

float SampleAndBlendTextures(Texture2D<float4> tex1, Texture2D<float4> tex2, Texture2D<float4> tex3, Texture2D<float4> tex4, 
	int channel1, int channel2, int channel3, int channel4,
	SamplerState texSampler1, SamplerState texSampler2, SamplerState texSampler3, SamplerState texSampler4, 
	int count, int blendMode, float2 uv)
{
	float value = SampleTexture(tex1, texSampler1, channel1, uv);
	if(count > 1) value = SampleAndBlendTexture(value, tex2, texSampler2, blendMode, channel2, uv);
	if(count > 2) value = SampleAndBlendTexture(value, tex3, texSampler3, blendMode, channel3, uv);
	if(count > 3) value = SampleAndBlendTexture(value, tex4, texSampler4, blendMode, channel4, uv);
	return value;
}

float SampleInput(Texture2D<float4> tex1, Texture2D<float4> tex2, Texture2D<float4> tex3, Texture2D<float4> tex4,
	int channel1, int channel2, int channel3, int channel4,
	SamplerState texSampler1, SamplerState texSampler2, SamplerState texSampler3, SamplerState texSampler4,
	int count, int blendMode, float fallback, bool doInvert, float2 uv) { 
	float value = fallback;
	if(count > 0)
	{
		value = SampleAndBlendTextures(tex1, tex2, tex3, tex4, channel1, channel2, channel3, channel4, texSampler1, texSampler2, texSampler3, texSampler4, count, blendMode, uv);
		if(doInvert) value = 1 - value;
	}
	return value;
}

float3 rgb2hsv(float3 rgb){
	float4 p = (rgb.g < rgb.b) ? float4(rgb.bg, -1.0, 2.0/3.0) : float4(rgb.gb, 0.0, -1.0/3.0);
	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);
}

float4 ApplyHSV(float4 pixel, float hue, float saturation, float value){
	float3 hsv = rgb2hsv(pixel.rgb);
	hsv.r += hue;
	hsv.g *= saturation;
	hsv.b *= value;
	return float4(hsv2rgb(hsv.rgb), pixel.a);
}

[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
	float2 uv = float2(id.x / Width, id.y / Height)  + Offset;

	// Rotate uv by Rotation around 0.5 0.5 and scale by Scale
	float2 center = float2(0.5, 0.5);
	float2 rotated = float2(0, 0);
	rotated.x = (uv.x - center.x) * cos(Rotation) - (uv.y - center.y) * sin(Rotation);
	rotated.y = (uv.x - center.x) * sin(Rotation) + (uv.y - center.y) * cos(Rotation);
	uv = rotated * Scale + center;

	float4 pixel = float4(1, 1, 1, 1);
	pixel.r = SampleInput(R_Input_0, R_Input_1, R_Input_2, R_Input_3, R_Channel_0, R_Channel_1, R_Channel_2, R_Channel_3, sampler_R_Input_0, sampler_R_Input_1, sampler_R_Input_2, sampler_R_Input_3, R_Count, R_BlendMode, R_Fallback, R_Invert, uv);
	pixel.g = SampleInput(G_Input_0, G_Input_1, G_Input_2, G_Input_3, G_Channel_0, G_Channel_1, G_Channel_2, G_Channel_3, sampler_G_Input_0, sampler_G_Input_1, sampler_G_Input_2, sampler_G_Input_3, G_Count, G_BlendMode, G_Fallback, G_Invert, uv);
	pixel.b = SampleInput(B_Input_0, B_Input_1, B_Input_2, B_Input_3, B_Channel_0, B_Channel_1, B_Channel_2, B_Channel_3, sampler_B_Input_0, sampler_B_Input_1, sampler_B_Input_2, sampler_B_Input_3, B_Count, B_BlendMode, B_Fallback, B_Invert, uv);
	pixel.a = SampleInput(A_Input_0, A_Input_1, A_Input_2, A_Input_3, A_Channel_0, A_Channel_1, A_Channel_2, A_Channel_3, sampler_A_Input_0, sampler_A_Input_1, sampler_A_Input_2, sampler_A_Input_3, A_Count, A_BlendMode, A_Fallback, A_Invert, uv);
	pixel = ApplyHSV(pixel, Hue, Saturation, Brightness);
	Result[id.xy] = pixel;
}


#pragma kernel CS_Kernel

Texture2D<float4> Kernel_Input;
float4 Kernel_X[25];
float4 Kernel_Y[25];
bool Kernel_Grayscale;
bool Kernel_TwoPass;
float4 Kernel_Channels;

[numthreads(8, 8, 1)]
void CS_Kernel(uint3 id : SV_DispatchThreadID)
{
	float4 pixel_x = float4(0, 0, 0, 0);
	float4 pixel_y = float4(0, 0, 0, 0);
	
	for (int x = -2; x <= 2; x++)
	{
		for (int y = -2; y <= 2; y++)
		{
			float4 p = Kernel_Input.Load(int3(id.xy + int2(x,y), 0));
			[branch]
			if(Kernel_Grayscale)
			{
				float gray = dot(p.rgb, float3(0.2126, 0.7152, 0.0722));
				pixel_x += float4(gray * Kernel_X[x + 2 + (y + 2) * 5].x, 0, 0, 0);
				[branch]
				if(Kernel_TwoPass)
				{
					pixel_y += float4(gray * Kernel_Y[x + 2 + (y + 2) * 5].x, 0, 0, 0);
				}
			}else
			{
				pixel_x += p * Kernel_X[x + 2 + (y + 2) * 5].x;
				[branch]
				if(Kernel_TwoPass)
				{
					pixel_y += p * Kernel_Y[x + 2 + (y + 2) * 5].x;
				}
			}
		}
	}

	float4 color = Kernel_Input.Load(int3(id.xy, 0));
	float4 res = color;
	[branch]
	if(Kernel_Grayscale)
	{
		color = float4(dot(color.rgb, float3(0.2126, 0.7152, 0.0722)), 0, 0, color.a);
		[branch]
		if(Kernel_TwoPass)
		{
			res = sqrt((pixel_x * pixel_x + pixel_y * pixel_y)).x;
		}
		else
		{
			res = pixel_x.x;
		}
	}else
	{
		[branch]
		if(Kernel_TwoPass)
		{
			res = sqrt((pixel_x * pixel_x + pixel_y * pixel_y));
		}
		else
		{
			res = pixel_x;
		}
	}
	color.r = lerp(color.r, res.r, Kernel_Channels.x);
	color.g = lerp(color.g, res.g, Kernel_Channels.y);
	color.b = lerp(color.b, res.b, Kernel_Channels.z);
	color.a = lerp(color.a, res.a, Kernel_Channels.w);
	Result[id.xy] = color;
	
}