/// LSU EE 4702-1 (Fall 2026), GPU Programming
//
 ///  Helix Rendering Code With Geometry Shaders
//
//    See demo-10-shdr-ngeo.cc for helix rendering without geometry shaders.
//
 ///  Helix Rendering with a Smooth Bulge
//
//    Vertex shader:    vs_main_helix()
//    Geometry shader:  gs_main_helix()
//    Fragment shader:  fs_main_phong()



 /// 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[];  };


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

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

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

layout (location = 0) out Data
{
  int hidx;
  vec3 normal_o;
  vec2 tex_coord;
} Out;

void
vs_main_helix()
{
  // Here, the vertex shader does nothing except pass variables
  // to the geometry shader.

  Out.normal_o = in_normal_o;
  Out.hidx = in_helix_index;
  Out.tex_coord = in_tex_coord;
}
#endif


#ifdef _GEOMETRY_SHADER_

// Indicate type of input primitive expected by geometry shader.
//
layout ( triangles ) in;

layout (location = 0) in Data
{
  int hidx;
  vec3 normal_o;
  vec2 tex_coord;
} In[];

layout (location = 0) out Data_GF
{
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  flat vec4 color;
} Out;

const int slices = 7;
layout ( triangle_strip, max_vertices = ( slices + 1 ) * 2 ) out;

void
gs_main_helix()
{
  // If "bulge" is far away, just emit one triangle.
  // If bulge is near, emit multiple triangles so that bulge is
  // smooth.

  float bulge_dist_0 = abs( bulge_loc - In[0].hidx );

  // If true, don't apply any special coloring to the triangle.
  // If false, give the triangle a cyanish tint.
  //
  const bool type_a = bool(gl_PrimitiveIDIn & 1);
  const vec4 base_shade = type_a ? vec4(1,1,1,1) : vec4(0.625,1,1,1);

  if ( bulge_dist_0 > bulge_dist_thresh )
    {
      /// Bulge is far away, just emit one triangle.
      //
      for ( int i=0; i<3; i++ )
        {
          // Lighten the color to distinguish this area of helix from
          // the part with the bulge.
          //
          Out.color = vec4(0.8,0.8,0.8,1) * base_shade;

          // Compute the vertex coordinate.
          //
          int hidx = In[i].hidx;
          vec4 c = helix_coord[hidx];  // Coordinate of core of helix.

          // Vertex normal in object space.
          vec3 norm_o = wire_radius * In[i].normal_o;

          // Vertex coordinate in object space.
          vec4 pos_o = c + vec4(norm_o,0);

          gl_Position = ut.clip_from_object * pos_o;
          Out.normal_e = mat3(ut.eye_from_object) * In[i].normal_o;
          Out.vertex_e = ut.eye_from_object * pos_o;
          Out.tex_coord = In[i].tex_coord;

          // Indicate that vertex is finished.
          //
          EmitVertex();
          //
          // The attributes (normal_e, etc.) will be sent to the
          // primitive assembly unit at the input to the rasterizer
          // where they will be collected to form triangles.
        }

      // Indicate that this triangle strip is finished.
      //
      EndPrimitive();
      //
      // This strip just contains one triangle. Also in this shader
      // invocation we're done so there won't be another strip.

      return;
    }

  //
  /// Sort Vertices
  //
  // Sort vertex indices so that gs_vidx_10 and gs_vidx_11 are at same
  // position along helix (though at different positions on surface of
  // wire). The illustration below shows relationship of gs_vidx_FOO
  // to the helix and triangles.

  int gs_vidx_00;
  int gs_vidx_10;
  int gs_vidx_11;

  if ( In[0].hidx == In[1].hidx )
    { gs_vidx_00 = 2;  gs_vidx_10 = 1;  gs_vidx_11 = 0; }
  else if ( In[0].hidx == In[2].hidx )
    { gs_vidx_00 = 1;  gs_vidx_10 = 0;  gs_vidx_11 = 2; }
  else
    { gs_vidx_00 = 0;  gs_vidx_10 = 2;  gs_vidx_11 = 1; }

  int hidx0 = In[gs_vidx_00].hidx;
  int hidx1 = In[gs_vidx_10].hidx;
  vec3 c0 = helix_coord[hidx0].xyz;
  vec3 c1 = helix_coord[hidx1].xyz;
  vec3 n0 =  In[gs_vidx_00].normal_o;
  vec3 n10 = In[gs_vidx_10].normal_o;
  vec3 n11 = In[gs_vidx_11].normal_o;

  // 

  // Variable slices is the number of pairs of triangles to emit.
  //
  float bulge_rad = 1;
  float delta_f = 1.0f/slices;

  for ( int i=0; i<=slices; i++ )
    {
      float f = 1 - i * delta_f;

      // Interpolate
      //
      float hidxx = mix( float(hidx1), float(hidx0), f );
      vec3 c =   mix( c1,  c0, f );
      vec3 nx0 = mix( n10, n0, f );
      vec3 nx1 = mix( n11, n0, f );
      //
      // Note: mix( x, y, f )  =  x * (1-f)  +  y * f;

      float bulge_dist = abs( bulge_loc - hidxx );

      // Make surface of bulge curved.
      //
      float mult = 1.25f
        - 0.25f *
        cos( 3.1415f * max(0.0f, ( bulge_rad - bulge_dist ) / bulge_rad ) );
      float rx = wire_radius * mult;

      // Coordinates on wire surface.
      //
      vec4 pos0 = vec4( c + rx * nx0, 1);
      vec4 pos1 = vec4( c + rx * nx1, 1);

      // Form two triangles (we are emitting a triangle strip).
      //
      Out.color = base_shade;
      gl_Position = ut.clip_from_object * pos0;
      Out.vertex_e = ut.eye_from_object * pos0;
      Out.normal_e = mat3(ut.eye_from_object) * nx0; // Not quite correct.
      Out.tex_coord = vec2(-1,0);  // Save this for a homework assignment.
      EmitVertex();

      Out.color = vec4(1,0.8,1,1) * base_shade; // Different tint.
      gl_Position = ut.clip_from_object * pos1;
      Out.vertex_e = ut.eye_from_object * pos1;
      Out.normal_e = mat3(ut.eye_from_object) * nx1;  // Not quite correct.
      Out.tex_coord = vec2(-1,0);  // Save this for a homework assignment.
      EmitVertex();
    }
  EndPrimitive();
}

#endif


#ifdef _FRAGMENT_SHADER_

layout ( binding = BIND_TEXUNIT ) uniform sampler2D tex_unit_0;

layout (location = 0) in Data_GF
{
  vec3 normal_e;
  vec4 vertex_e;
  vec2 tex_coord;
  flat vec4 color;
} 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 || In.tex_coord.x == -1f
    ? vec4(1,1,1,1) : texture(tex_unit_0,In.tex_coord);


  // Coloring Scheme
  //
  //  color_front, color_back
  //    The base color of the helix (outside and inside).
  //  In.color
  //    Tinting to apply to the color. This is done so that it is
  //    easy to distinguish one triangle from another.

  vec4 material_color = gl_FrontFacing ? color_front : color_back;
  vec4 shaded_color = material_color * In.color;

  // Compute lighted color of fragment.
  //
  vec4 lighted_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 * lighted_color : lighted_color;
}
#endif