/// LSU EE 4702-1 (Fall 2023), GPU Programming
//

 /// See demo-10-shader.cc for details.


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

#extension GL_GOOGLE_include_directive : enable
#include <light.h>
#include <transform.h>
#include "shader-common-generic-lighting.h"

 /// Declare Uniform Variables
//
layout ( binding = BIND_MATERIAL ) uniform Uni_Material
{
  vec4 color_front, color_back;
};

layout ( binding = BIND_BULGE_INFO ) uniform Buldge_Info
{
  float bulge_loc;
  float bulge_dist_thresh;
  float wire_radius;
  bool opt_texture;
};

 /// Declare (Bind) Storage Buffer
//
layout ( binding = BIND_HELIX_COORDS ) buffer Helix_Coord
{
  vec4 helix_coord[];
};


#ifdef _VERTEX_SHADER_

// Helix Index: An integer giving position along helix.
// Used to index the helix coordinate array.
//
layout ( location = LOC_IN_INT1 ) in int in_helix_index;

layout (location = LOC_IN_NORMAL) in vec3 in_normal;
layout (location = LOC_IN_TCOOR) in vec2 in_tex_coord;

// Declare variables for communication between vertex shader
// and fragment shader.
//
layout (location = 0) out Data
{
  vec4 vertex_c;
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  int hidx;
} Out;

void
vs_main_helix()
{
  // Perform basic vertex shading operations, but also:
  //   - Compute wire surface coordinates.
  //   - Adjust wire diameter based on location of bulge.
  //
  // Note that this shader only works for the helix.

  // Pass helix index to fragment shader output.
  //
  Out.hidx = in_helix_index;

  // Set wire radius based on location of bulge.
  //
  float radius = wire_radius * ( Out.hidx == int(bulge_loc) ? 1.2f : 1.0f );

  /// Compute wire surface location by adding normal to helix coordinate.
  //
  vec4 vertex_o;
  vertex_o.xyz = helix_coord[Out.hidx].xyz + radius * in_normal;
  vertex_o.w = 1;

  // Transform vertex coordinate to clip space.
  //
  Out.vertex_c = ut.clip_from_object * vertex_o;

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

  // Pass texture coordinate to next stage in pipeline.
  // Only copy x and y components since it's a 2D texture.
  //
  Out.tex_coord = in_tex_coord;
}

#endif


#ifdef _GEOMETRY_SHADER_

 /// Geometry Shader Input Primitive
//
// Must fit pipeline input topology set by the CPU.
//
layout ( triangles ) in;
//
// TriangleList, TriangleStrip, TriangleFan -> triangle

 /// Geometry Shader Input
//
//   Must be declared as an array, and the number of elements
//   must fit the input primitive.
//
layout (location = 0) in DataVG
{
  vec4 vertex_c;
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  int hidx;
} In[3];


 /// Geometry Shader Output
//
//   Not declared as an array.
//   Declaration describes one vertex.
//   These are written for each call to EmitVertex.
//
layout (location = 0) out DataGF
{
  vec4 color;
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  int hidx;
} Out;

 /// Geometry Shader Output Primitive.
//
//   Does NOT need to match pipeline input topology.
//
layout ( triangle_strip, max_vertices = 3 ) out;
//
//   Note: triangle_strip is used even if only one triangle is written.


void
gs_main_helix()
{
  /// Geometry Shader
  //

  // Adjust color of certain triangles.
  //
  const bool type_a = In[0].hidx < In[2].hidx;
  vec4 color_adjust = type_a ? vec4(0.5,0.5,0.5,1) : vec4(1);

  for ( int i=0; i<3; i++ )
    {
      // Send the adjusted colors.
      //
      Out.color = color_adjust;

      // Pass the other values through unmodified.
      //
      gl_Position = In[i].vertex_c;
      Out.tex_coord = In[i].tex_coord;
      Out.normal_e = In[i].normal_e;
      Out.vertex_e = In[i].vertex_e;

      EmitVertex();
    }
  EndPrimitive();
}

#endif


#ifdef _FRAGMENT_SHADER_

layout ( binding = BIND_TEXUNIT ) uniform sampler2D tex_unit_0;

layout (location = 0) in Data
{
  vec4 color;
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  int hidx;
} In;

layout (location = 0) out vec4 out_frag_color;

void
fs_main_phong()
{
  // Perform lighting, fetch and blend texture, then emit fragment.
  //

  // Get filtered texel.
  //
  vec4 texel = opt_texture ? texture(tex_unit_0,In.tex_coord) : vec4(1,1,1,1);

  // Why are we using both?
  vec4 material_color = gl_FrontFacing ? color_front : color_back;
  vec4 shaded_color = material_color * In.color;

  // Compute lighted color of fragment.
  //
  vec4 lit_color = generic_lighting( In.vertex_e, shaded_color, In.normal_e);

  // Multiply filtered texel color with lighted color of fragment.
  //
  out_frag_color = opt_texture ? texel * lit_color : lit_color;
}
#endif