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

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

#extension GL_GOOGLE_include_directive : enable

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

bool opt_tryout1 = bool(uni_misc.x);
bool opt_tryout2 = bool(uni_misc.y);


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


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


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;


// Declare variables for communication between vertex shader
// and fragment shader.
//
#ifdef _VERTEX_SHADER_

 /// Vertex Shader Inputs
//
layout (location = LOC_IN_POS) in vec4 in_vertex_o;
layout (location = LOC_IN_NORMAL) in vec3 in_normal_o;
#ifdef LOC_IN_INT1
layout (location = LOC_IN_INT1) in int in_color_idx;
#endif
//
// The CPU must provide these inputs.


 /// Vertex Shader Output
//
layout (location = 0) out Data_VG
{
  vec4 vertex_c, vertex_e;
  vec3 normal_e;
  int color_idx;
};

 /// The Vertex Shader
void
vs_main()
{
  // Transform the vertex object-space coordinate to clip space.
  //
  vertex_c = ut.clip_from_object * in_vertex_o;

  // Compute eye-space coordinates for vertex and normal.
  //
  vertex_e = ut.eye_from_object * in_vertex_o;
  normal_e = normalize( mat3(ut.eye_from_object) * in_normal_o );

  color_idx = in_color_idx;
}
#endif

#ifdef _GEOMETRY_SHADER_

layout ( triangles ) in;
layout ( triangle_strip, max_vertices = 3 ) out;

layout (location = 0) in Data_VG
{
  vec4 vertex_c, vertex_e;
  vec3 normal_e;
  int color_idx;
} In[];

layout (location = 0) out Data_GF
{
  vec4 vertex_e;
  vec3 normal_e;
  int color_idx;
} Out;

void
gs_main()
{
  int i_last = 2 - gl_PrimitiveIDIn % 2;
  if ( !opt_tryout2 && In[i_last].color_idx == 0 ) return;

  for ( int i=0; i<3; i++ )
    {
      gl_Position = In[i].vertex_c;
      Out.color_idx = In[i_last].color_idx;
      Out.vertex_e = In[i].vertex_e;
      Out.normal_e = In[i].normal_e;
      EmitVertex();
    }
  EndPrimitive();
}

#endif

#ifdef _FRAGMENT_SHADER_

layout (location = 0) in Data_VF
{
  vec4 vertex_e;
  vec3 normal_e;
  flat int color_idx;
};

// 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()
{
  // Vector from fragment to light.
  vec3 vec_vl = uni_light.position.xyz - vertex_e.xyz;

  // Distance squared.
  float dist_sq = dot( vec_vl, vec_vl );

  // False if the light illuminates the side we can't see.
  bool lit_side = dot( normal_e, vec_vl )>0 == dot( normal_e, -vertex_e.xyz )>0;

  // Amount of light reaching the fragment.

  /// SOLUTION -- Problem 2
  //
  //  Don't set the attenuation to zero if primitive is lit from behind.
  //
  float attenuation = abs( dot( normal_e, vec_vl ) / dist_sq );

  // Material color.
  vec4 mat_color = gl_FrontFacing ? uc.front[color_idx] : uc.back[color_idx];

  /// SOLUTION -- Problem 2
  if ( !lit_side )
    {
      // Blend front and back colors, and darken them to account for
      // light going through material.
      //
      mat_color = ( uc.front[color_idx] + uc.back[color_idx] ) * 0.25;
    }

  // Compute lighted color.
  frag_color = mat_color * uni_light.color * attenuation;
  //
  // One important thing fragment shaders do--but not this one--is
  // apply textures.
}
#endif