/// LSU EE 4702-1 (Fall 2022), GPU Programming
//
 /// Homework 4 -- SOLUTION
//
//   Modify this file AND hw04.cc.
//   Assignment: https://www.ece.lsu.edu/koppel/gpup/2022/hw04.pdf


// Specify version of OpenGL Shading Language.
//
#version 460

#extension GL_GOOGLE_include_directive : enable

////////////////////////////////////////////////////////////////////////////////
///
 /// Uniform Variable Declarations
//

layout ( binding = BIND_MISC ) uniform Uni_Misc
{
  ivec4 uni_misc;
};

// Use these for debugging, experimentation, etc.
bool opt_tryout1 = bool(uni_misc.x);
bool opt_tryout2 = bool(uni_misc.y);

layout ( binding = BIND_TRANSFORM ) uniform Uni_Transform
{
  // Transformation Matrices
  mat4 eye_from_object, clip_from_eye, clip_from_object;
  mat4 object_from_eye, eye_from_clip;
} ut;

layout ( binding = BIND_LIGHT_SIMPLE ) uniform Uni_Light
{
  vec4 position;
  vec4 color;
} uni_light;

const int ncolors = 10;
layout ( binding = BIND_HW04 ) uniform Uni_Colors
{
  vec4 front[ncolors], back[ncolors];
} uc;

#ifdef _VERTEX_SHADER_
////////////////////////////////////////////////////////////////////////////////
///
 /// Vertex Shader Code
//

 // Vertex Shader Inputs
//
layout (location = LOC_IN_POS) in vec4 in_vertex_o;
#ifdef LOC_IN_NORMAL
layout (location = LOC_IN_NORMAL) in vec3 in_normal_o;
#endif
#ifdef LOC_IN_INT1
layout (location = LOC_IN_INT1) in int in_color_idx;
#endif

 // Vertex Shader Outputs
//
layout (location = 0) out Data_VG
{
  vec4 vertex_c;

  /// SOLUTION
  //
  //  Send send object- and eye-space vertex coordinates and color
  //  index to geometry shader.
  //
  vec4 vertex_o, vertex_e;
  int c_idx;
};

void
vs_main_hw04()
{
  // The Vertex Shader

  /// SOLUTION
  //
  // Remove the code computing the lighted color. We can't compute the
  // lighted color in the vertex shader because we don't have a normal
  // and it can't be computed here because we only have one
  // vertex. Instead, compute the clip- and eye-space vertex
  // coordinate and send them to the geometry shader.


  c_idx = int(in_vertex_o.w);
  vertex_o = vec4(in_vertex_o.xyz,1);
  vertex_c = ut.clip_from_object * vertex_o;
  vertex_e = ut.eye_from_object * vertex_o;
}
#endif

#ifdef _GEOMETRY_SHADER_
////////////////////////////////////////////////////////////////////////////////
///
 /// Geometry Shader Code
/// 

// Geometry Shader Inputs
//
layout (location = 0) in Data_VG
{
  vec4 vertex_c;
  /// SOLUTION
  vec4 vertex_o;
  vec4 vertex_e;
  int c_idx;
} In[];

// Geometry Shader Input Primitive
//
layout ( triangles ) in;


// Geometry Shader Outputs
//
layout (location = 0) out Data_GF
{
  vec4 color;
  vec4 color_back;
};

// Geometry Shader Output Primitive
//
layout ( triangle_strip, max_vertices = 3 ) out;

void
gs_main_hw04()
{
  // The Geometry Shader
  //
  // Pre-defined output: gl_Position (vec4, read when EmitVertex called).

  /// SOLUTION
  //
  //  Compute the triangle normal using the three vertex coordinates.
  //
  vec3 normal_e =
    normalize( cross( In[1].vertex_e.xyz - In[0].vertex_e.xyz,
                      In[2].vertex_e.xyz - In[0].vertex_e.xyz ) );

  // Use the color of the first vertex for the triangle.
  //
  int c_idx = In[0].c_idx;
  //
  // The color In[1].c_idx and In[2].c_idx will be used for the adjacent
  // triangles.

  for ( int i=0; i<3; i++ )
    {
      gl_Position = In[i].vertex_c;

      /// SOLUTION
      vec4 vertex_e = In[i].vertex_e;

      // Compute lighted color.
      //
      vec3 vec_vl = uni_light.position.xyz - vertex_e.xyz;
      float dist_to_light = length( vec_vl );
      float phase = abs( dot( normal_e, vec_vl ) / dist_to_light );

      vec4 illum = uni_light.color * max( 0.025, phase / dist_to_light );
      color = illum * uc.front[c_idx];
      color_back = illum * uc.back[c_idx];
      EmitVertex();
    }
  EndPrimitive();
}

#endif


#ifdef _FRAGMENT_SHADER_
////////////////////////////////////////////////////////////////////////////////
///
 /// Fragment Shader Code
///

// Fragment Shader Inputs
//
layout (location = 0) in Data_GF
{
  vec4 color;
  vec4 color_back;
};

// Fragment Shader Output
//
layout (location = 0) out vec4 frag_color;
//
// This will get written to frame buffer if tests pass, such as the
// depth (z-buffer) test.

void
fs_main_hw04()
{
  // The Fragment Shader

  frag_color = gl_FrontFacing ? color : color_back;
}
#endif