<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

<glsl_hacker>
	
  <window name="win3d01" title="OpenGL 4.0 - Tessellation / PN Triangles" 
          width="1280" height="720"
          gl_version_major="4" gl_version_minor="0" />
  
   
	<script name="init_scene" run_mode="INIT" >
		<raw_data><![CDATA[	
    
local demo_dir = gh_utils.get_demo_dir()    
local lib_dir = gh_utils.get_scripting_libs_dir()     
dofile(lib_dir .. "lua/libfont/libfont1.lua")   



winW, winH = gh_window.getsize(0)




camera_ortho = gh_camera.create_ortho(-winW/2, winW/2, -winH/2, winH/2, 1.0, 10.0)
gh_camera.set_viewport(camera_ortho, 0, 0, winW, winH)
gh_camera.set_position(camera_ortho, 0, 0, 4)




-----------------------------------------------------------------------
-- Perspective camera.
--
local aspect = winW / winH
camera_params = { fov=60, znear=0.1, zfar=1000.0 }
camera = gh_camera.create_persp(camera_params.fov, aspect, camera_params.znear, camera_params.zfar)
gh_camera.set_viewport(camera, 0, 0, winW, winH)
gh_camera.set_position(camera, 0, 0, 12)
gh_camera.set_lookat(camera, 0, 0, 0, 1)
gh_camera.set_upvec(camera, 0, 1, 0, 0)






bkg_prog = gh_node.getid("bkg_prog")




tess_prog = gh_node.getid("tess_prog")
gh_gpu_program.uniform4f(tess_prog, "tess_params", 16.0, 16.0, 0.5, 1.2)
gh_gpu_program.uniform4f(tess_prog, "light_position", 0, 20, 3, 1.0)
gh_gpu_program.uniform4f(tess_prog, "uv_tiling", 2.0, 2.0, 0.0, 0.0)
gh_gpu_program.uniform1i(tess_prog, "tex_diffuse", 0)
gh_gpu_program.uniform1i(tess_prog, "tex_normal", 1)
gh_gpu_program.uniform1i(tess_prog, "tex_bump", 2)


mesh = gh_mesh.create_sphere(1.0, 20, 20)
gh_object.set_tessellation_state(mesh, 1)


mesh_quad = gh_mesh.create_quad(winW, winH)




local PF_U8_RGB = 1
local PF_U8_RGBA = 3
local pixel_format = PF_U8_RGBA
local gen_mipmaps = 1
local compressed_texture = 0

tex0 = gh_texture.create_from_file_v6(demo_dir .. "../data/textures/10767-ambientocclusion.jpg", pixel_format, gen_mipmaps, compressed_texture)
tex1 = gh_texture.create_from_file_v6(demo_dir .. "../data/textures/10767-normal.jpg", pixel_format, gen_mipmaps, compressed_texture)
tex2 = gh_texture.create_from_file_v6(demo_dir .. "../data/textures/10767-bump.jpg", pixel_format, gen_mipmaps, compressed_texture)



gh_renderer.set_vsync(0)
gh_renderer.set_scissor_state(1)
gh_renderer.set_depth_test_state(1)


local elapsed_time = gh_utils.get_elapsed_time()
gh_object.set_euler_angles(mesh, 90 + 10*math.cos(elapsed_time), 20*math.sin(elapsed_time),  10*math.sin(elapsed_time))



gl_renderer = gh_renderer.get_renderer_model()
gl_version = gh_renderer.get_api_version()

max_tess_level = gh_renderer.get_capability_4i("GL_MAX_TESS_GEN_LEVEL")


fps_time = 0
fps = 0
frames = 0



		]]></raw_data>
	</script> 

 
	<script name="update_scene" run_mode="FRAME" >
		<raw_data><![CDATA[	

local elapsed_time = gh_utils.get_elapsed_time()
local dt = gh_utils.get_time_step()

frames = frames+1
fps_time = fps_time + dt
if (fps_time >= 1.0) then
  fps_time = 0
  fps = frames
  frames = 0
end  





gh_renderer.set_depth_test_state(0)

gh_camera.bind(camera_ortho)

gh_renderer.clear_color_depth_buffers(0.2, 0.2, 0.2, 1.0, 1.0)

gh_gpu_program.bind(bkg_prog)
gh_gpu_program.uniform2f(bkg_prog, "screen_size", winW, winH)
gh_gpu_program.uniform3f(bkg_prog, "sun_dir", 0.0, -1.0, 0.0)
local inv_tan_fov = 1.0 / math.tan(60.0)
gh_gpu_program.uniform1f(bkg_prog, "inv_tan_fov", inv_tan_fov)
gh_gpu_program.uniform_camera_matrices(bkg_prog, camera, "view_mat", "proj_mat")

gh_object.render(mesh_quad)





gh_renderer.set_depth_test_state(1)

local cx = 6.0 * math.cos(elapsed_time * 0.25)
local cy = 2.0
local cz = 6.0 * math.sin(elapsed_time * 0.25)
gh_camera.set_position(camera, cx, cy, cz)
gh_camera.set_lookat(camera, 0, 0, 0, 1)
gh_camera.bind(camera)

gh_texture.bind(tex0, 0)
gh_texture.bind(tex1, 1)
gh_texture.bind(tex2, 2)

gh_gpu_program.bind(tess_prog)
gh_gpu_program.uniform4f(tess_prog, "tess_params", 8.0, 8.0, 0.5, 1.1)


--gh_object.set_euler_angles(mesh, 90 + 10*math.cos(elapsed_time), 20*math.sin(elapsed_time),  10*math.sin(elapsed_time))
gh_object.render(mesh)




---[[
libfont_clear()

local y_offset = 40

libfont_print(20, y_offset, 1, 0.5, 0, 1, "OpenGL 4.0 - Tessellation demo")
y_offset = y_offset + 20

y_offset = y_offset + 20
libfont_print(20, y_offset, 1, 1, 0, 1, "FPS: " .. fps)
y_offset = y_offset + 20

y_offset = y_offset + 20
libfont_print(20, y_offset, 1, 1, 0, 1, "GL_RENDERER")
y_offset = y_offset + 20
libfont_print(20, y_offset, 1, 1, 1, 1, "> " .. gl_renderer)
y_offset = y_offset + 20

libfont_print(20, y_offset, 1, 1, 0, 1, "GL_VERSION")
y_offset = y_offset + 20
libfont_print(20, y_offset, 1, 1, 1, 1, "> " .. gl_version)
y_offset = y_offset + 20

libfont_print(20, y_offset, 1, 1, 0, 1, "GL_MAX_TESS_GEN_LEVEL")
y_offset = y_offset + 20
libfont_print(20, y_offset, 1, 1, 1, 1, "> " .. max_tess_level)


libfont_render()
--]]



		]]></raw_data>
	</script> 
	

	<script name="resize_scene" run_mode="SIZE" >
		<raw_data><![CDATA[	

winW, winH = gh_window.getsize(0)

local aspect = 1.333
if (winH > 0) then
  aspect = winW / winH
end  
gh_camera.update_persp(camera, camera_params.fov, aspect, camera_params.znear, camera_params.zfar)
gh_camera.set_viewport(camera, 0, 0, winW, winH)



gh_camera.update_ortho(camera_ortho, -winW/2, winW/2, -winH/2, winH/2, 1.0, 10.0)
gh_camera.set_viewport(camera_ortho, 0, 0, winW, winH)


gh_mesh.update_quad_size(mesh_quad, winW, winH)

		]]></raw_data>
	</script> 
  
  <gpu_program name="tess_prog" >
    <raw_data_vs><![CDATA[	 
#version 400
in vec4 gxl3d_Position;
in vec4 gxl3d_Normal;
in vec4 gxl3d_TexCoord0;
out vec4 v_normal;
out vec4 v_uv;
void main()
{
  gl_Position = gxl3d_Position;
  v_normal = gxl3d_Normal;
  v_uv = gxl3d_TexCoord0;
}
  ]]></raw_data_vs>
  
    <raw_data_tcs><![CDATA[	 
#version 400

layout(vertices = 3) out;

uniform vec4 tess_params;

struct pn_patch
{
  float b210;
  float b120;
  float b021;
  float b012;
  float b102;
  float b201;
  float b111;
  float n110;
  float n011;
  float n101;
};

in vec4 v_normal[];
in vec4 v_uv[];

out vec4 tcs_normal[3];
out vec4 tcs_uv[3];
out pn_patch tcs_patch[3];



float wij(int i, int j)
{
	return dot(gl_in[j].gl_Position.xyz - gl_in[i].gl_Position.xyz, v_normal[i].xyz);
}

float vij(int i, int j)
{
	vec3 Pj_minus_Pi = gl_in[j].gl_Position.xyz - gl_in[i].gl_Position.xyz;
	vec3 Ni_plus_Nj  = v_normal[i].xyz + v_normal[j].xyz;
	return 2.0*dot(Pj_minus_Pi, Ni_plus_Nj) / dot(Pj_minus_Pi, Pj_minus_Pi);
}



void main()
{
	// get data
	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
  
	tcs_normal[gl_InvocationID]  = v_normal[gl_InvocationID];
	tcs_uv[gl_InvocationID]  = v_uv[gl_InvocationID];

	// set base 
  float P0 = gl_in[0].gl_Position[gl_InvocationID];
	float P1 = gl_in[1].gl_Position[gl_InvocationID];
	float P2 = gl_in[2].gl_Position[gl_InvocationID];
	float N0 = v_normal[0][gl_InvocationID];
	float N1 = v_normal[1][gl_InvocationID];
	float N2 = v_normal[2][gl_InvocationID];
  

	// compute control points
	tcs_patch[gl_InvocationID].b210 = (2.0*P0 + P1 - wij(0,1)*N0)/3.0;
	tcs_patch[gl_InvocationID].b120 = (2.0*P1 + P0 - wij(1,0)*N1)/3.0;
	tcs_patch[gl_InvocationID].b021 = (2.0*P1 + P2 - wij(1,2)*N1)/3.0;
	tcs_patch[gl_InvocationID].b012 = (2.0*P2 + P1 - wij(2,1)*N2)/3.0;
	tcs_patch[gl_InvocationID].b102 = (2.0*P2 + P0 - wij(2,0)*N2)/3.0;
	tcs_patch[gl_InvocationID].b201 = (2.0*P0 + P2 - wij(0,2)*N0)/3.0;
	
  float E = ( tcs_patch[gl_InvocationID].b210
			+ tcs_patch[gl_InvocationID].b120
			+ tcs_patch[gl_InvocationID].b021
			+ tcs_patch[gl_InvocationID].b012
			+ tcs_patch[gl_InvocationID].b102
			+ tcs_patch[gl_InvocationID].b201 ) / 6.0;
	
  float V = (P0 + P1 + P2) / 3.0;
	
  tcs_patch[gl_InvocationID].b111 = E + (E - V)*0.5;
	tcs_patch[gl_InvocationID].n110 = N0+N1-vij(0,1)*(P1-P0);
	tcs_patch[gl_InvocationID].n011 = N1+N2-vij(1,2)*(P2-P1);
	tcs_patch[gl_InvocationID].n101 = N2+N0-vij(2,0)*(P0-P2);

  float tessLevelOuter = tess_params.x;
  float tessLevelInner  = tess_params.y;

  gl_TessLevelOuter[gl_InvocationID] = tessLevelOuter;
	gl_TessLevelInner[0] = tessLevelInner;
}

  ]]></raw_data_tcs>
  
  
    <raw_data_tes><![CDATA[	 
#version 400
layout(triangles, fractional_even_spacing, ccw) in;
//layout(triangles, fractional_odd_spacing, ccw) in;

struct pn_patch
{
  float b210;
  float b120;
  float b021;
  float b012;
  float b102;
  float b201;
  float b111;
  float n110;
  float n011;
  float n101;
};

in vec4 tcs_normal[gl_MaxPatchVertices];
in vec4 tcs_uv[gl_MaxPatchVertices];
in pn_patch tcs_patch[gl_MaxPatchVertices];

out vec4 tes_normal;
out vec4 tes_uv;
out vec4 v_eye_dir;
out vec4 v_light_dir;

uniform mat4 gxl3d_ProjectionMatrix;
uniform mat4 gxl3d_ViewMatrix;
uniform mat4 gxl3d_ModelMatrix;
uniform vec4 uv_tiling;
uniform vec4 light_position;
uniform vec4 tess_params;

uniform sampler2D tex_bump;

#define uvw gl_TessCoord

void main()
{
  vec3 uvwSquared = uvw * uvw;
  vec3 uvwCubed   = uvwSquared * uvw;

  // extract control points
  vec3 b210 = vec3(tcs_patch[0].b210, tcs_patch[1].b210, tcs_patch[2].b210);
  vec3 b120 = vec3(tcs_patch[0].b120, tcs_patch[1].b120, tcs_patch[2].b120);
  vec3 b021 = vec3(tcs_patch[0].b021, tcs_patch[1].b021, tcs_patch[2].b021);
  vec3 b012 = vec3(tcs_patch[0].b012, tcs_patch[1].b012, tcs_patch[2].b012);
  vec3 b102 = vec3(tcs_patch[0].b102, tcs_patch[1].b102, tcs_patch[2].b102);
  vec3 b201 = vec3(tcs_patch[0].b201, tcs_patch[1].b201, tcs_patch[2].b201);
  vec3 b111 = vec3(tcs_patch[0].b111, tcs_patch[1].b111, tcs_patch[2].b111);

  // extract control normals
  vec3 n110 = normalize(vec3(tcs_patch[0].n110, tcs_patch[1].n110, tcs_patch[2].n110));
  vec3 n011 = normalize(vec3(tcs_patch[0].n011, tcs_patch[1].n011, tcs_patch[2].n011));
  vec3 n101 = normalize(vec3(tcs_patch[0].n101, tcs_patch[1].n101, tcs_patch[2].n101));

  // compute texcoords
  vec4 uv  = (gl_TessCoord[2]*tcs_uv[0] + gl_TessCoord[0]*tcs_uv[1] + gl_TessCoord[1]*tcs_uv[2])  * uv_tiling;
  tes_uv = uv;
  
  float bump = texture(tex_bump, uv.xy).x;
  
  
  // normal
  // Barycentric normal
  vec3 barNormal = gl_TessCoord[2]*tcs_normal[0].xyz + gl_TessCoord[0]*tcs_normal[1].xyz + gl_TessCoord[1]*tcs_normal[2].xyz;
  vec3 pnNormal  = tcs_normal[0].xyz*uvwSquared[2] + tcs_normal[1].xyz*uvwSquared[0] + tcs_normal[2].xyz*uvwSquared[1]
                            + n110*uvw[2]*uvw[0] + n011*uvw[0]*uvw[1]+ n101*uvw[2]*uvw[1];
                            
  float tess_alpha = tess_params.z;
  
  vec3 Normal = normalize(tess_alpha*pnNormal + (1.0-tess_alpha) * barNormal);
  

  // compute interpolated pos
  vec3 barPos = gl_TessCoord[2]*gl_in[0].gl_Position.xyz + gl_TessCoord[0]*gl_in[1].gl_Position.xyz + gl_TessCoord[1]*gl_in[2].gl_Position.xyz;

  // save some computations
  uvwSquared *= 3.0;

  // compute PN position
  vec3 pnPos  = gl_in[0].gl_Position.xyz*uvwCubed[2]
                    + gl_in[1].gl_Position.xyz*uvwCubed[0]
                    + gl_in[2].gl_Position.xyz*uvwCubed[1]
                    + b210*uvwSquared[2]*uvw[0]
                    + b120*uvwSquared[0]*uvw[2]
                    + b201*uvwSquared[2]*uvw[1]
                    + b021*uvwSquared[0]*uvw[1]
                    + b102*uvwSquared[1]*uvw[2]
                    + b012*uvwSquared[1]*uvw[0]
                    + b111*6.0*uvw[0]*uvw[1]*uvw[2];


  // final position and normal
  vec3 finalPos = tess_alpha*pnPos + (1.0-tess_alpha)*barPos;
  
  float bump_scale = tess_params.w;
  finalPos += bump * Normal * bump_scale;

  mat4 modelview = gxl3d_ViewMatrix * gxl3d_ModelMatrix;
  
  tes_normal = modelview * vec4(Normal, 0.0);
  
  vec4 view_pos = modelview * vec4(finalPos,1.0);
  v_eye_dir = -view_pos;
  vec4 lp = gxl3d_ViewMatrix * light_position;
  v_light_dir = lp - view_pos;  

  gl_Position = gxl3d_ProjectionMatrix * view_pos;
}
  ]]></raw_data_tes>

  <raw_data_ps><![CDATA[	 
#version 400

in vec4 tes_normal;
in vec4 tes_uv;
in vec4 v_eye_dir;
in vec4 v_light_dir;

uniform sampler2D tex_diffuse;
uniform sampler2D tex_normal;

out vec4 FragColor;



// http://www.thetenthplanet.de/archives/1180
mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)
{
    // get edge vectors of the pixel triangle
    vec3 dp1 = dFdx( p );
    vec3 dp2 = dFdy( p );
    vec2 duv1 = dFdx( uv );
    vec2 duv2 = dFdy( uv );
 
    // solve the linear system
    vec3 dp2perp = cross( dp2, N );
    vec3 dp1perp = cross( N, dp1 );
    vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
    vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
 
    // construct a scale-invariant frame 
    float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );
    return mat3( T * invmax, B * invmax, N );
}

vec3 perturb_normal(vec3 N, vec3 V, vec2 texcoord)
{
    // assume N, the interpolated vertex normal and 
    // V, the view vector (vertex to eye)
   vec3 map = texture(tex_normal, texcoord ).xyz;
   map = map * 255./127. - 128./127.;
   mat3 TBN = cotangent_frame(N, -V, texcoord);
   return normalize(TBN * map);
}

void main()
{
  vec2 uv = tes_uv.xy;
  //uv.y = 1.0 - uv.y;
  
  vec3 c0 = texture(tex_diffuse, uv).rgb;

  vec3 N = normalize(tes_normal.xyz);
  vec3 E = normalize(v_eye_dir.xyz);
  vec3 L = normalize(v_light_dir.xyz);  
  //L.y *= -1.0; 
  vec3 PN = perturb_normal(N, E, uv);
 
  
  vec3 fc = vec3(0.0, 0.0, 0.0);
  
  vec3 la0 = vec3(0.2, 0.2, 0.2);
  vec3 ld0 = vec3(0.9, 0.85, 0.8);
  vec3 ls0 = vec3(0.9, 0.9, 0.9);
  vec3 mA = vec3(0.8, 0.8, 0.8);
  vec3 mD = vec3(0.9, 0.9, 0.9);
  vec3 mS = vec3(0.4, 0.4, 0.4);
  float mSpecExp = 16.0;  
  
  fc += (la0 * mA) * c0.rgb;
  float lambert = dot(PN, L);
  if (lambert > 0)
  {
    fc += ld0 * mD * lambert * c0;
    
    vec3 R = reflect(-L, PN);
    float specular = pow(abs(dot(R, E)), mSpecExp);
    fc += ls0 * mS * specular;	
  } 
  
  FragColor = vec4(fc, 1.0);
}
    ]]></raw_data_ps>
  </gpu_program>






<gpu_program name="bkg_prog" >
  <raw_data_vs><![CDATA[
#version 150
in vec4 gxl3d_Position;
in vec4 gxl3d_TexCoord0;
uniform mat4 gxl3d_ModelViewProjectionMatrix;
//out vec4 Vertex_UV;
void main()
{
  gl_Position = gxl3d_ModelViewProjectionMatrix * gxl3d_Position;
  //Vertex_UV = gxl3d_TexCoord0;
}
  ]]></raw_data_vs>
  
  <raw_data_ps><![CDATA[
#version 150
uniform mat4 view_mat;
uniform vec2 screen_size;
uniform vec3 sun_dir;
uniform float inv_tan_fov;

out vec4 FragColor;


float TraceFloor(vec3 ro, vec3 rd)
{
    return (-1.2 - ro.y) / rd.y;
}

vec3 Sky(vec3 rd)
{
    float rdl = max(dot(sun_dir, rd), 0.0);

    vec3 color = vec3(0.18, 0.25, 0.35) * (1.0 - 0.8 * rd.y) * 0.85;
    color += 0.15 * vec3(1.0, 0.95, 0.7) * pow(rdl, 1.0);
    color += 0.25 * vec3(1.0, 0.95, 0.7) * pow(rdl, 16.0);
    color += 0.25 * vec3(1.0, 0.95, 0.8) * pow(rdl, 512.0);
    color += 0.15 * vec3(1.0, 0.95, 0.5) * pow(clamp(1.0 - rd.y, 0.0, 1.0), 4.0);
    return color;
}
float CheckerPattern(vec3 vPosition)
{
    // Procedural checkerpatterns! :D
    float d = 0.03;
    float size = 0.75;
    float fx = mod(vPosition.x / size, 2.0);
    fx = smoothstep(1.0 - d, 1.0, fx) - smoothstep(2.0 - d, 2.0, fx);
    float fz = mod(vPosition.z / size, 2.0);
    fz = smoothstep(1.0 - d, 1.0, fz) - smoothstep(2.0 - d, 2.0, fz);
    float pattern = fx * fz + (1.0 - fx) * (1.0 - fz);
    return pattern;
}

void main()
{
    float u = -1.0 + 2.0 * gl_FragCoord.x / screen_size.x;
    float v = -1.0 + 2.0 * gl_FragCoord.y / screen_size.y;
    u *= screen_size.x / screen_size.y;

    // Compute the camera basis from the view-transformation matrix
    mat3 R = mat3(transpose(view_mat));
    vec3 right = normalize(R[0].xyz);
    vec3 up = normalize(R[1].xyz);
    vec3 forward = normalize(R[2].xyz);

    // Compute the ray origin and direction
    vec3 ro = -R * view_mat[3].xyz;
    vec3 rd = normalize(-forward * inv_tan_fov + right * u + up * v);

    float t = TraceFloor(ro, rd);
    vec3 p = ro + t * rd;
    if (t >= 0.0)
    {
        float r2 = dot(p.xz, p.xz);
        float cp = CheckerPattern(p);
        float fog = clamp(length(p.xz) / 25.0, 0.0, 1.0);
        FragColor.rgb = vec3(0.8, 0.79, 0.72);
        FragColor.rgb -= vec3(0.22, 0.23, 0.25) * cp * (1.0 - fog);
        FragColor.rgb = mix(FragColor.rgb, Sky(rd), fog);

        // Sphere shadow
        FragColor.rgb *= 1.0 - exp2(-1.6 * r2);
    }
    else
    {
        FragColor.rgb = Sky(rd);
    }

    FragColor.a = 1.0;
}  ]]></raw_data_ps>

</gpu_program>

  

  
  
</glsl_hacker>
