/// LSU EE 4702-1 (Fall 2015), GPU Programming
//
 /// Homework 4 -- SOLUTION
 //
 //  Use this file for your solution.

 /// Instructions
 //
 //  Read the assignment: http://www.ece.lsu.edu/koppel/gpup/2015/hw04.pdf



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

// Use this variable to debug your code. Press 'y' to toggle tryout.x
// and 'Y' to toggle debug_bool.y (between true and false).
//
layout ( location = 3 ) uniform bvec2 tryout;


// Array of ball positions.
//
layout ( binding = 1 ) buffer Balls_Pos { vec4 balls_pos_rad[]; };
layout ( binding = 2 ) buffer Balls_Color { vec4 balls_color[]; };

 /// SOLUTION -- Problem 2
layout ( binding = 3 ) buffer Links_Color { vec4 links_color[]; };

#ifdef _VERTEX_SHADER_

in ivec2 gl_Vertex;

// Interface block for vertex shader output / geometry shader input.
//
out Data_to_GS
{
  ivec2 indices;

  /// SOLUTION -- Problem 2
  //
  //  Replace vec4 color with int vtx_id;
  int vtx_id;
};


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

  indices = gl_Vertex;

  /// SOLUTION -- Problem 2
  //
  //  Transfer vertex id
  vtx_id = gl_VertexID;
}

#endif


#ifdef _GEOMETRY_SHADER_

in Data_to_GS
{
  ivec2 indices;

  /// SOLUTION -- Problem 2
  //  Replace color with vtx_id.
  int vtx_id;
} In[];

out Data_to_FS
{
  vec3 normal_e;
  vec4 vertex_e;
  /// SOLUTION -- Problem 2
  //  Replace color with vtx_id.
  flat int vtx_id;
};

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

// Type of primitives emitted geometry shader output.
//
layout ( triangle_strip, max_vertices = 42 ) out;

void
gs_main_links()
{
  vec4 pos_rad1 = balls_pos_rad[In[0].indices.x];
  vec4 pos_rad2 = balls_pos_rad[In[0].indices.y];
  vec3 pos1 = pos_rad1.xyz;
  vec3 pos2 = pos_rad2.xyz;
  float radius = pos_rad1.w * 0.3;
  vec3 v12 = pos2 - pos1;
  vec3 vna =
    normalize(v12.x == 0 ? vec3(0,-v12.z,v12.y) : vec3(-v12.y,v12.x,0));
  vec3 vnb = normalize(cross(v12,vna));
  mat4 to_obj;
  to_obj[0] = vec4(radius * vna, 0);
  to_obj[1] = vec4(radius * vnb, 0);
  to_obj[2] = vec4(v12, 0);
  to_obj[3] = vec4(pos1,1);
  mat4 mv = gl_ModelViewMatrix * to_obj;
  mat4 mvp = gl_ModelViewProjectionMatrix * to_obj;
  vec4 pos1_e = gl_ModelViewMatrix * vec4(pos1,1);

  const int sides = 20;
  const float M_PI = 3.1415926535f;
  const float delta_theta = 2 * M_PI / sides;

  /// SOLUTION -- Problem 2
  //  Replace color with vtx_id.
  vtx_id = In[0].vtx_id;
  for ( int i=0; i<=sides; i++ )
    {
      const float theta = delta_theta * i;
      const float cos_t = cos(theta);
      const float sin_t = sin(theta);
      for ( int j=0; j<2; j++ )
        {
          vec4 p1 = vec4( cos_t, sin_t, j, 1 );
          vertex_e = mv * p1;
          if ( j == 0 ) normal_e = vertex_e.xyz - pos1_e.xyz;
          gl_Position = mvp * p1;
          EmitVertex();
        }
    }
  EndPrimitive();
}

#endif


#ifdef _FRAGMENT_SHADER_

in Data_to_FS
{
  vec3 normal_e;
  vec4 vertex_e;
  /// SOLUTION -- Problem 2
  //  Replace color with vtx_id.
  flat int vtx_id;

};

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


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

  /// SOLUTION -- Problem 2
  //  Use vtx_id to retrieve color.
  //
  vec4 color = links_color[vtx_id];

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

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


///
/// Routine used by Either Vertex or Fragment Shader
///

vec4
generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e)
{
  // Return lighted color of vertex_e.
  //
  vec4 light_pos = gl_LightSource[0].position;
  vec3 v_vtx_light = light_pos.xyz - vertex_e.xyz;
  float d_n_ve = -dot(normal_e,vertex_e.xyz);
  float d_n_vl = dot(normal_e, normalize(v_vtx_light).xyz);
  bool same_sign = ( d_n_ve > 0 ) == ( d_n_vl > 0 );
  float phase_light = same_sign ? abs(d_n_vl) : 0;

  vec3 ambient_light = gl_LightSource[0].ambient.rgb;
  vec3 diffuse_light = gl_LightSource[0].diffuse.rgb;
  float dist = length(v_vtx_light);
  float distsq = dist * dist;
  float atten_inv =
    gl_LightSource[0].constantAttenuation +
    gl_LightSource[0].linearAttenuation * dist +
    gl_LightSource[0].quadraticAttenuation * distsq;
  vec4 lighted_color;
  lighted_color.rgb =
    color.rgb * gl_LightModel.ambient.rgb
    + color.rgb * ( ambient_light + phase_light * diffuse_light ) / atten_inv;
  lighted_color.a = color.a;
  return lighted_color;
}

#endif