#vertexshader

void main()
{
	gl_Position = ftransform();
	gl_TexCoord[0] = gl_MultiTexCoord0;
}

#fragmentshader

uniform sampler2D color_texture;
uniform sampler2D depth_texture;

/* *********** ssao *********** */

// really crappy version but when I started to fix all issues with,
// everything started to look booring so I kept it

uniform vec2 camera_range;
uniform vec2 screen_size;
uniform vec2 sample_positions[16];

uniform float focus_distance;
uniform float focus_range;
uniform float focus_falloff;

float read_depth(in vec2 coord)
{
	return (2.0 * camera_range.x) / (camera_range.y + camera_range.x - texture2D(depth_texture, coord).x * (camera_range.y - camera_range.x));	
}

/*float read_depth(in vec2 coord)
{
	float Z = texture2D(depth_texture, coord).x;
	return camera_range.x * (Z + 1.0) / (camera_range.y + camera_range.x - Z * (camera_range.y - camera_range.x)); // depth buffer Z-value remapped to a linear [0..1] range (near plane to far plane)
}*/

vec4 ssao(vec4 color_sample)
{	
	float depth = read_depth(gl_TexCoord[0].xy);
	float d;
	
	float pw = 1.0 / screen_size.x;
	float ph = 1.0 / screen_size.y;

	float ao = 0.0;
	float intensity = 1000.0;
	float tolerance = 0.0001;
	
	for(int i = 0; i < 4; i++)
	{
		d = read_depth(vec2(gl_TexCoord[0].x+pw, gl_TexCoord[0].y));
		ao += min(1.0, max(0.0, depth-d-tolerance) * intensity);
		
		d = read_depth(vec2(gl_TexCoord[0].x-pw, gl_TexCoord[0].y+ph));
		ao += min(1.0, max(0.0, depth-d-tolerance) * intensity);

		d = read_depth(vec2(gl_TexCoord[0].x+pw, gl_TexCoord[0].y-ph));
		ao += min(1.0, max(0.0, depth-d-tolerance) * intensity);

		d = read_depth(vec2(gl_TexCoord[0].x-pw, gl_TexCoord[0].y-ph));
		ao += min(1.0, max(0.0, depth-d-tolerance) * intensity);
		
		pw *= 2.0;
		ph *= 2.0;
		intensity /= 2.0;
	}
	ao /= 16.0;
	
	return vec4(vec3(1.0-ao) * color_sample.rgb, 1.0);
}

/* *********** tone map *********** */

const float exposure = 1.0;
const float brightmax = 4.0;

vec4 tonemap(vec4 color_sample)
{
	float y = dot(vec4(0.30, 0.59, 0.11, 0.0), color_sample);
	float yd = exposure * (exposure/brightmax + 1.0) / (exposure + 1.0);
	return color_sample*yd;

}

/* *********** light extract *********** */
const float trigger_value = 0.6;

vec4 lightextract(vec4 color_sample)
{
	vec4 c = texture2D(color_texture, gl_TexCoord[0].xy);

	float y = (0.257 * color_sample.r) + (0.504 * color_sample.g) + (0.098 * color_sample.b);
	
	if(y > trigger_value)
		return vec4(c.rgb*(y-trigger_value), 1.0);
	else
		return vec4(0.0, 0.0, 0.0, 0.0);
}

/* *********** dof *********** */

const float ZNear = 1.0;
const float ZFar = 10000.0;

float read_depth2(in vec2 coord)
{
	float Z = texture2D(depth_texture, coord).x;
	return ZNear * (Z + 1.0) / (ZFar + ZNear - Z * (ZFar - ZNear)); // depth buffer Z-value remapped to a linear [0..1] range (near plane to far plane)
}

float calcblurriness()
{
	float rd = texture2D(depth_texture, gl_TexCoord[0].xy).x;
	float d = read_depth2(gl_TexCoord[0].xy);

	d = camera_range.x + d * (camera_range.y - camera_range.x) / 2.0; // div by 2 works. but whyyyyyyy???
	return clamp((abs(d-focus_distance)-focus_range)/focus_falloff,0.0,1.0);
}

/* *********** main *********** */

void main(void)
{
	vec4 color_sample = texture2D(color_texture, gl_TexCoord[0].xy);
	
	gl_FragData[0] = vec4(tonemap(ssao(color_sample)).xyz, calcblurriness());
	gl_FragData[1] = lightextract(color_sample);
}
