uniform float4x4 ViewProj;
uniform texture2d image;

uniform texture2d clut_1d;
uniform texture3d clut_3d;
uniform float clut_amount;
uniform float3 clut_scale;
uniform float3 clut_offset;
uniform float3 domain_min;
uniform float3 domain_max;

sampler_state textureSampler {
	Filter    = Linear;
	AddressU  = Clamp;
	AddressV  = Clamp;
	AddressW  = Clamp;
};

struct VertDataIn {
	float4 pos : POSITION;
	float2 uv  : TEXCOORD0;
};

struct VertDataOut {
	float4 pos : POSITION;
	float2 uv  : TEXCOORD0;
};

VertDataOut VSDefault(VertDataIn v_in)
{
	VertDataOut vert_out;
	vert_out.uv = v_in.uv;
	vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
	return vert_out;
}

float srgb_linear_to_nonlinear_channel(float u)
{
	return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1.0 / 2.4)) - 0.055);
}

float3 srgb_linear_to_nonlinear(float3 v)
{
	return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
}

float4 LUT1D(VertDataOut v_in) : TARGET
{
	float4 textureColor = image.Sample(textureSampler, v_in.uv);
	textureColor.rgb *= (textureColor.a > 0.) ? (1. / textureColor.a) : 0.;
	float3 nonlinear = srgb_linear_to_nonlinear(textureColor.rgb);

	if (nonlinear.r >= domain_min.r && nonlinear.r <= domain_max.r) {
		float u = nonlinear.r * clut_scale.r + clut_offset.r;
		float channel = clut_1d.Sample(textureSampler, float2(u, 0.5)).r;
		textureColor.r = lerp(textureColor.r, channel, clut_amount);
	}

	if (nonlinear.g >= domain_min.g && nonlinear.g <= domain_max.g) {
		float u = nonlinear.g * clut_scale.g + clut_offset.g;
		float channel = clut_1d.Sample(textureSampler, float2(u, 0.5)).g;
		textureColor.g = lerp(textureColor.g, channel, clut_amount);
	}

	if (nonlinear.b >= domain_min.b && nonlinear.b <= domain_max.b) {
		float u = nonlinear.b * clut_scale.b + clut_offset.b;
		float channel = clut_1d.Sample(textureSampler, float2(u, 0.5)).b;
		textureColor.b = lerp(textureColor.b, channel, clut_amount);
	}

	return textureColor;
}

float4 LUT3D(VertDataOut v_in) : TARGET
{
	float4 textureColor = image.Sample(textureSampler, v_in.uv);
	float3 nonlinear = srgb_linear_to_nonlinear(textureColor.rgb);

	float3 clut_uvw = nonlinear * clut_scale + clut_offset;
	textureColor.rgb = clut_3d.Sample(textureSampler, clut_uvw).rgb;

	return textureColor;
}

float4 LUTAlpha3D(VertDataOut v_in) : TARGET
{
	float4 textureColor = image.Sample(textureSampler, v_in.uv);
	textureColor.rgb *= (textureColor.a > 0.) ? (1. / textureColor.a) : 0.;
	float3 nonlinear = srgb_linear_to_nonlinear(textureColor.rgb);

	float3 clut_uvw = nonlinear * clut_scale + clut_offset;
	textureColor.rgb = clut_3d.Sample(textureSampler, clut_uvw).rgb;

	textureColor.rgb *= textureColor.a;
	return textureColor;
}

float4 LUTAmount3D(VertDataOut v_in) : TARGET
{
	float4 textureColor = image.Sample(textureSampler, v_in.uv);
	textureColor.rgb *= (textureColor.a > 0.) ? (1. / textureColor.a) : 0.;
	float3 nonlinear = srgb_linear_to_nonlinear(textureColor.rgb);

	float3 clut_uvw = nonlinear * clut_scale + clut_offset;
	float3 luttedColor = clut_3d.Sample(textureSampler, clut_uvw).rgb;
	textureColor.rgb = lerp(textureColor.rgb, luttedColor, clut_amount);

	textureColor.rgb *= textureColor.a;
	return textureColor;
}

float4 LUTDomain3D(VertDataOut v_in) : TARGET
{
	float4 textureColor = image.Sample(textureSampler, v_in.uv);
	textureColor.rgb *= (textureColor.a > 0.) ? (1. / textureColor.a) : 0.;
	float3 nonlinear = srgb_linear_to_nonlinear(textureColor.rgb);

	float r = nonlinear.r;
	float g = nonlinear.g;
	float b = nonlinear.b;
	if (r >= domain_min.r && r <= domain_max.r &&
		g >= domain_min.g && g <= domain_max.g &&
		b >= domain_min.b && b <= domain_max.b)
	{
		float3 clut_uvw = nonlinear * clut_scale + clut_offset;
		float3 luttedColor = clut_3d.Sample(textureSampler, clut_uvw).rgb;
		textureColor.rgb = lerp(textureColor.rgb, luttedColor, clut_amount);
	}

	textureColor.rgb *= textureColor.a;
	return textureColor;
}

technique Draw1D
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		pixel_shader  = LUT1D(v_in);
	}
}

technique Draw3D
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		pixel_shader  = LUT3D(v_in);
	}
}

technique DrawAlpha3D
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		pixel_shader  = LUTAlpha3D(v_in);
	}
}

technique DrawAmount3D
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		pixel_shader  = LUTAmount3D(v_in);
	}
}

technique DrawDomain3D
{
	pass
	{
		vertex_shader = VSDefault(v_in);
		pixel_shader  = LUTDomain3D(v_in);
	}
}
