#version 150 compatibility

// bump map texture
uniform sampler2D bump_map;
// bump map texture resolution
uniform int bump_map_res;
// scaling of height function
uniform float bump_scale;

vec3 bump_map_normal(vec3 s, vec2 tc, vec3 N)
{
	// lookup height value
	float h_0 = texture2D(bump_map,tc).x;

	// derivatives in image space
	mat2 tc_i = mat2(dFdx(tc),dFdy(tc));
	vec2 h_i  = vec2(texture2D(bump_map,tc+tc_i[0]).x - h_0,
		             texture2D(bump_map,tc+tc_i[1]).x - h_0);
	vec3 s_x = dFdx(s);
	vec3 s_y = dFdy(s);

	// compute T_i^(-1)
	float det_tc_i = tc_i[0][0]*tc_i[1][1] - tc_i[1][0]*tc_i[0][1];
	mat2  i_tc     = mat2(tc_i[1][1], -tc_i[1][0], 
		                 -tc_i[0][1], tc_i[0][0])/det_tc_i;

	// transform derivatives to texture space
	vec2 h_tc   = i_tc*h_i;
	vec2 s_tc0  = i_tc * vec2(s_x[0],s_y[0]);
	vec2 s_tc1  = i_tc * vec2(s_x[1],s_y[1]);
	vec2 s_tc2  = i_tc * vec2(s_x[2],s_y[2]);
	vec3 s_u  = vec3(s_tc0.x,s_tc1.x,s_tc2.x);
	vec3 s_v  = vec3(s_tc0.y,s_tc1.y,s_tc2.y);

	// compute scaling factor for original normal
	float f = length(cross(s_u,s_v));

	// compute bump mapped normal
	vec3 n = normalize(N);
	float height_scale = bump_scale / float(bump_map_res);
	n = f*n + cross(height_scale*(h_tc[1]*s_u-h_tc[0]*s_v),n);
	return normalize(n);
}