```/// LSU EE 4702-1 (Fall 2017), GPU Programming
//
/// Homework 5 -- SOLUTION

/// Instructions
//
//  Solution Discussion: http://www.ece.lsu.edu/koppel/gpup/2017/hw05_sol.pdf
//
///  Put the solution in this file.
//   To solve the problem code in hw05.cc needs to be inspected.

// Specify version of OpenGL Shading Language.
//
#version 450 compatibility

//
#endif

// ----------------------------------------------------------------------------
/// PROBLEM 1 - 3      ↓↓  Code can be placed below.   ↓↓

layout ( location = 1 ) uniform bvec2 opt_tryout;
layout ( location = 2 ) uniform int lighting_options;
layout ( location = 3 ) uniform float world_time;

layout ( binding = 1 ) buffer b1 { vec4 pt_00s[]; };
layout ( binding = 2 ) buffer b2 { vec4 axs[]; };
layout ( binding = 3 ) buffer b3 { vec4 ays[]; };
layout ( binding = 4 ) buffer b4 { vec4 colors[]; };
layout ( binding = 5 ) buffer b5 { vec4 tact_pos_times[]; };

/// PROBLEM 1 - 3       ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------

#ifndef _PROBLEM_2_

// ----------------------------------------------------------------------------
/// PROBLEM 1 and 3   ↓↓  Code can be placed below.   ↓↓

//
out Data_to_GS
{
vec4 gl_Position;
vec3 normal_e;
vec4 vertex_e;
vec4 color;

// Any changes here must also be made to the geometry shader input.
};

void
vs_main_tiles_0()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vertex_e = gl_ModelViewMatrix * gl_Vertex;
normal_e = normalize(gl_NormalMatrix * gl_Normal);
color = gl_Color;
}

void
vs_main_tiles_1()
{
/// SOLUTION - Problem 1

// Pass through inputs unchanged.
//
vertex_e = gl_Vertex;  // Either pt_00, ax, or ay.
color = gl_Color;      // Either color or tact_pos_time.
}

/// PROBLEM 1  and 3   ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------
#else
// ----------------------------------------------------------------------------
/// PROBLEM 2 and 3    ↓↓  Code can be placed below.   ↓↓

//
out Data_to_GS
{
vec4 gl_Position;
vec3 normal_e;
vec4 vertex_e;
vec4 color;

// Any changes here must also be made to the geometry shader input.

/// SOLUTION -- Problem 2

int vertex_id;
};

void
vs_main_tiles_2()
{
/// SOLUTION -- Problem 2

// Pass vertex id unchanged.
//
vertex_id = gl_VertexID;
//
// Note: Doing more work here and sending the results to the
// geometry shader might result in exactly the same amount of
// computation, but it will definitely increase the amount of data
// sent between the vertex and geometry shaders, which is not
// a good thing.
}

#endif

/// PROBLEM 2 and 3          ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------

#endif

// ----------------------------------------------------------------------------
/// PROBLEM 1 - 3             ↓↓  Code can be placed below.   ↓↓

out Data_to_FS
{
flat vec3 normal_e;
vec4 vertex_e;
flat vec4 color;

/// SOLUTION -- Problem 3

// Pass contact position and time to fragment shader to draw the ring.
//
flat vec4 tact_pos_time_e;
//
// Note the use of a flat qualifier since the values will be the
// same at all three vertices and so there is no point in doing
// interpolation, which isn't free.
};

/// PROBLEM 1 - 3           ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------

#ifndef _PROBLEM_2_

// ----------------------------------------------------------------------------
/// PROBLEM 1 and 3     ↓↓  Code can be placed below.   ↓↓

//
in Data_to_GS
{
vec4 gl_Position;
vec3 normal_e;
vec4 vertex_e;
vec4 color;

// Any changes here must also be made to the vertex shader output.
} In[3];

// Type of primitive at geometry shader input.
//
layout ( triangles ) in;

// Type of primitives emitted geometry shader output.
//
/// SOLUTION -- Problem 1 and 2
//
// Increase max_vertices by 1.
//
layout ( triangle_strip, max_vertices = 4 ) out;

void
gs_main_tiles_0()
{
/// Do not put solution here.  Use this file for comparison or minor experiments.

// Color and normal will be the same for each vertex.
normal_e = In[0].normal_e;
color = In[0].color;

for ( int i=0; i<3; i++ )
{
// Pass along values unchanged.
vertex_e = In[i].vertex_e;
gl_Position = In[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}

void
gs_main_tiles_1()
{
/// SOLUTION -- Problem 1

vec4 vtx_o[4];

// Write tile information into appropriately named variables.
//
vec4 pt_00 = In[0].vertex_e;
vec4 ax_o = vec4(In[1].vertex_e.xyz,0);
vec4 ay_o = vec4(In[2].vertex_e.xyz,0);
//
// Note: The w component of ax and ay are set to zero. The OpenGL
// code on the CPU would have set them to one since they were
// specified using glVertex3f(FOO). Setting w to 1 is correct for
// coordinates (when only x, y, and z are given) but wrong for
// vectors.
//
// By setting the w components of vectors to 0 expressions like
// pt_00 + ax_o compute the correct results.

// Compute the object-space coordinates of the tile corners.
//
vtx_o[0] = pt_00;
vtx_o[1] = pt_00 + ax_o;
vtx_o[2] = pt_00 + ay_o;
vtx_o[3] = pt_00 + ay_o + ax_o;
//
// The order is chosen for a triangle strip.

// Compute the tile normal.
//
vec3 normal_o = normalize(cross(ax_o.xyz,ay_o.xyz));
normal_e = normalize(gl_NormalMatrix * normal_o);
//
// Note that the normal is set once here and used for all four
// vertices.
//
// Note that the CPU case 1 code does NOT send a normal. The vertex
// shader still has an input named gl_Normal, but since the CPU code
// does not set it, its value will be arbitrary.

color = In[0].color;
//
// The same color is also used for all four vertices.

for ( int i=0; i<4; i++ )
{
gl_Position = gl_ModelViewProjectionMatrix * vtx_o[i];
vertex_e = gl_ModelViewMatrix * vtx_o[i];
EmitVertex();
}

EndPrimitive();
}

/// PROBLEM 1 and 3     ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------

#else

// ----------------------------------------------------------------------------
/// PROBLEM 2 and 3     ↓↓  Code can be placed below.   ↓↓

//
in Data_to_GS
{
vec4 gl_Position;
vec3 normal_e;
vec4 vertex_e;
vec4 color;

// Any changes here must also be made to the vertex shader output.

/// SOLUTION -- Problem 2

int vertex_id;

} In[1];

// Type of primitive at geometry shader input.
//
layout ( points ) in;

// Type of primitives emitted geometry shader output.
//
/// SOLUTION -- Problem 2
//
layout ( triangle_strip, max_vertices = 4 ) out;

void
gs_main_tiles_2()
{
/// SOLUTION -- Problem 2

int vertex_id = In[0].vertex_id;

//
vec4 vtx_o[4];
vec4 pt_00 = pt_00s[vertex_id];
vec4 ax_o = vec4(axs[vertex_id].xyz,0);
vec4 ay_o = vec4(ays[vertex_id].xyz,0);

// Compute tile object space vertex coordinates.
//
vtx_o[0] = pt_00;
vtx_o[1] = pt_00 + ax_o;
vtx_o[2] = pt_00 + ay_o;
vtx_o[3] = pt_00 + ay_o + ax_o;

// Compute normal.
//
vec3 normal_o = normalize(cross(ax_o.xyz,ay_o.xyz));
normal_e = normalize(gl_NormalMatrix * normal_o);
//
// Don't write comments like "compute normal" in real-world code
// for situations when it's obvious what the code is doing.

/// SOLUTION -- Problem 3

// Convert contact position to eye space and write it to a
// geometry shader output. Also write time.
//
vec4 tact_pos_time = tact_pos_times[vertex_id];
tact_pos_time_e = gl_ModelViewMatrix * vec4(tact_pos_time.xyz,1);
tact_pos_time_e.w = tact_pos_time.w;
//
// Using one variable for position and time is not the best coding
// style. But, in GPUs movement of 4-element vectors is more
// efficient than scalars or smaller vectors.

/// SOLUTION -- Problem 2
//
//  The code below is identical to the corresponding code in Problem 1.

color = colors[vertex_id];

for ( int i=0; i<4; i++ )
{
gl_Position = gl_ModelViewProjectionMatrix * vtx_o[i];
vertex_e = gl_ModelViewMatrix * vtx_o[i];
EmitVertex();
}
EndPrimitive();
}

/// PROBLEM 2 and 3      ↑↑   Code can be placed above.   ↑↑
// ----------------------------------------------------------------------------

#endif

#endif

// ----------------------------------------------------------------------------
/// PROBLEM 1 - 3        ↓↓  Code can be placed below.   ↓↓

in Data_to_FS
{
flat vec3 normal_e;
vec4 vertex_e;
flat vec4 color;

/// SOLUTION -- Problem 3

// Pass contact position and time to fragment shader to draw the ring.
//
flat vec4 tact_pos_time_e;
//
// Note the use of a flat qualifier since the values will be the
// same at all three vertices and so there is no point in doing
// interpolation, which isn't free.
};

vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e);

void
fs_main_tiles_0()
{
/// Do not put solution here. Use this for minor experiments.
//

// Multiply filtered texel color with lighted color of fragment.
//
gl_FragColor = generic_lighting(vertex_e, color, normal_e);

// Copy fragment depth unmodified.
//
gl_FragDepth = gl_FragCoord.z;
}

void
fs_main_tiles_1()
{
fs_main_tiles_0();
}

void
fs_main_tiles_2()
{
/// SOLUTION -- Problem 3

float age = world_time - tact_pos_time_e.w;

// Quantities below are in object space.
//
float speed = 0.5;   // Speed of increase of the ring radius.
float width = 0.1;   // Width of ring.
//
// Note: The distance between two points in object-space coordinates
// is (or should be) the same as the distance in eye-space
// coordinates and so it's okay to use an object-space width and
// speed on eye-space distances.

// Compute distance from this fragment to the contact point.
//
float dist = distance(vertex_e.xyz,tact_pos_time_e.xyz);

float inner_radius = age * speed;

// Determine if this fragment is in the ring, and set color appropriately.
//
vec4 color_red = vec4(1,0,0,1);
vec4 color2 = ring ? color_red : color;

gl_FragColor = generic_lighting(vertex_e, color2, normal_e);

// Copy fragment depth unmodified.
//
gl_FragDepth = gl_FragCoord.z;
}

vec4
generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_er)
{
// Return lighted color of vertex_e.
//
vec3 nspc_color = color.rgb * gl_LightModel.ambient.rgb;
vec3 spec_color = vec3(0);
vec3 normal_e = normalize(normal_er);

for ( int i=0; i<2; i++ )
{
if ( ( lighting_options & ( 1 << i ) ) == 0 ) continue;
vec4 light_pos = gl_LightSource[i].position;
vec3 v_vtx_light = light_pos.xyz - vertex_e.xyz;
float dist = length(v_vtx_light);
float dist_vl_inv = 1.0 / dist;
vec3 v_vtx_l_n = v_vtx_light * dist_vl_inv;

float d_n_vl = dot(normal_e, v_vtx_l_n);
float phase_light = max(0,gl_FrontFacing ? d_n_vl : -d_n_vl );

vec3 ambient_light = gl_LightSource[i].ambient.rgb;
vec3 diffuse_light = gl_LightSource[i].diffuse.rgb;
float distsq = dist * dist;
float atten_inv =
gl_LightSource[i].constantAttenuation +
gl_LightSource[i].linearAttenuation * dist +
vec3 lighted_color =
color.rgb
* ( ambient_light + phase_light * diffuse_light ) / atten_inv;
nspc_color += lighted_color;

vec3 h = normalize( v_vtx_l_n - normalize(vertex_e.xyz) );

spec_color +=
pow(max(0.0,dot(normal_e,h)),16)
* color.rgb
* gl_LightSource[i].specular.rgb / atten_inv;
}

return vec4(nspc_color+spec_color,1);
}

#endif
```