```/// LSU EE 4702-1 (Fall 2014), GPU Programming
//
/// Homework 6
//
/// Ported to Vulkan Fall 2021

/// SOLUTION

//  Assignment: http://www.ece.lsu.edu/koppel/gpup/2014/hw06.pdf
//  Solution Discussion: http://www.ece.lsu.edu/koppel/gpup/2014/hw06_sol.pdf

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

#include <light.h>
#include <transform.h>
#include "shdr-common.h"
#include "shdr-generic-lighting.h"

layout ( binding = BIND_UNI_COMMON ) uniform UC
{
Shdr_Uni_Common uc;
};

// Use this variable to debug your code. Press 'd' to toggle
// debug_bool.x and 'D' to toggle debug_bool.y (between true and
// false).
//
bvec2 debug_bool = bvec2( bool(uc.debug_bool.x), bool(uc.debug_bool.y) );

// Use this to debug your code. Press TAB until "debug_float"
// appears, then press +/- to adjust its value.
//
float debug_float = uc.debug_float;

// Array of ball positions.
//
layout ( binding = BIND_BALLS_POS ) buffer Balls_Pos { vec4 balls_pos[]; };

// Redefine this vertex shader input to be an integer vector.
//
/// SOLUTION
//
//   Reduce in_indices from four to two components since the third
//   component was used to indicate whether to compute an inner or outer
//   vertex, and now we always do both.
layout ( location = LOC_IN_INT2 ) in ivec2 in_indices;

//
layout ( location = 0 ) out Data_to_GS
{
/// SOLUTION
//
//  Change vertex_e from a vec4 to a 2-D array of vec4's.  The
//    four values are for inner/outer, and upper/lower vertices.
//    Indexing:  [level (upper/lower)] [ radius (inner/outer) ]
//
//  Also declare four position vectors (and don't bother using gl_Position).
//
vec4 vertex_e[2][2];  // Vertex coordinates in eye space.
vec4 vertex_c[2][2];  // Vertex coordinates in clip space.

//  Only two texCoord and normal_e vectors are declared because the
//  upper and lower spirals have the same texture coordinates and normals.
//
vec2 texCoord[2];
vec3 normal_e[2];

//  All four vertices have the same radial vector.
//
vec3 radial_e;  // Normal for edge primitives.
};

void
vs_main_lines()
{
const float omega = 10;

const int bidx = in_indices.x;  // i in the CPU code, the ball number.
const int ti = in_indices.y;    // Integer version of t from CPU code.

// Position relative to top of whole spiral. Used only to compute angle.
const int radial_idx = bidx * uc.opt_segments + ti;

const float delta_t = 1.0 / uc.opt_segments;
// Position relative to top of segment: 0 top, 1 bottom, etc.
const float t = float(ti) * delta_t;
const float theta = delta_t * radial_idx * omega;

vec3 pos1 = balls_pos[bidx-1].xyz;
vec3 pos2 = balls_pos[bidx].xyz;

vec3 v12 = pos2.xyz - pos1.xyz;

// Find a vector that's orthogonal to v12.
//
vec3 ax =
normalize(v12.x == 0 ? vec3(0,v12.z,-v12.y) : vec3(v12.y,-v12.x,0));

// Find a vector that's orthogonal to v12 and ax.
//
vec3 ay = normalize(cross(v12,ax));

vec3 vx = ax * spiral_radius;
vec3 vy = ay * spiral_radius;

// Point on line between ball1 and ball2.
//
vec3 p = pos1 + t * v12;

// Vector from p to spiral outer edge.
//
vec3 radial = vx * cos(theta) + vy * sin(theta);
vec3 p_outer = p + radial;

const float inner_frac = 0.5;
vec3 p_inner = p + inner_frac * radial;

// Compute surface normals.
//
vec3 tangial = -omega * vx * sin(theta) + omega * vy * cos(theta);
vec3 tang = v12 + tangial;
vec3 tang_inner = v12 + inner_frac * tangial;

/// SOLUTION
//
//  Compute information for the four vertices.
//
//  Indexing:  [level (upper/lower)] [ radius (inner/outer) ]

vec3 v12n = normalize(v12);
vec3 depth_vector = 0.1f * v12n;

// Compute the four vertex coordinates in object space.
//
vec3 pos_o[2][2];
pos_o[0][0] = p_inner;
pos_o[0][1] = p_outer;
pos_o[1][0] = p_inner + depth_vector;
pos_o[1][1] = p_outer + depth_vector;

// Transform the object-space coordinates to clip and eye space.
//
for ( int l=0; l<2; l++ )
for ( int r=0; r<2; r++ )
{
vec4 position_o = vec4( pos_o[l][r], 1 );
vertex_c[l][r] = gl_ModelViewProjectionMatrix * position_o;
vertex_e[l][r] = gl_ModelViewMatrix * position_o;
}

normal_e[0] = gl_NormalMatrix * norm_inner;
normal_e[1] = gl_NormalMatrix * norm;

// Amount by which to zoom the texture.
//
float tex_zoom = 0.5;

// Uncomment the line below to use "debug_float" to zoom text.
// tex_zoom /= debug_float;

const float du = 0.5 * tex_zoom / uc.chain_length;
const float u = float(bidx) * du;

texCoord[0].x = texCoord[1].x = tex_zoom * t;
texCoord[0].y = 0.18 + u;
texCoord[1].y = 0.18 + u + du;

}

#endif

layout ( location = 0 ) in Data_to_GS
{
/// SOLUTION
//
//  See "out Data_to_GS" for a detailed discussion.

vec4 vertex_e[2][2];  // Vertex coordinates in eye space.
vec4 vertex_c[2][2];  // Vertex coordinates in clip space.
vec2 texCoord[2];

vec3 normal_e[2];

vec3 radial_e;  // Normal for edge primitives.
} In[];

layout ( location = 0 ) out Data_to_FS
{
vec3 normal_e;
vec4 vertex_e;
vec2 texCoord;
flat int is_edge;  // True if primitive an inner or outer edge.
};

/// SOLUTION
//
//   Change input primitive type to lines.  The reason for using lines,
//   remember, is because that's the most efficient way of passing
//   the needed vertices to the geometry shader.
//
layout ( lines ) in;

/// SOLUTION
//
//   Set max_vertices.
layout ( triangle_strip, max_vertices = 16 ) out;

void
gs_main_lines()
{
/// SOLUTION
//
// Use the two sets of four vertices to construct the upper and
// lower spiral and edges. The vertices are organized into a
// two-dimensional array in such a way that the spiral and edge
// triangles could each be rendered with a single loop nest.

/// Emit the spiral triangles.
//
for ( int level=0; level<2; level++ )         // Upper / Lower
{
for ( int theta=0; theta<2; theta++ )
{
for ( int r=0; r<2; r++ )             // Inner / outer
{
normal_e       = In[theta].normal_e[r];
vertex_e       = In[theta].vertex_e[level][r];
gl_Position    = In[theta].vertex_c[level][r];
texCoord       = In[theta].texCoord[r];
is_edge = 0;
EmitVertex();
}
}
EndPrimitive();           // This completes a strip of two triangles.
}

/// Emit the edge (wall) triangles.
//
// Because we know that In[0] is in back of In[1] there is no need
// for the vertex sorting code used in the Homework 4 solution. Just
// one simple loop nest.
//
for ( int r=0; r<2; r++ )                     // Inner / outer
{
for ( int theta=0; theta<2; theta++ )
{
for ( int level=0; level<2; level++ ) // Upper / Lower
{
vertex_e    = In[theta].vertex_e[level][r];
gl_Position = In[theta].vertex_c[level][r];
is_edge = 1;
EmitVertex();
}
}
EndPrimitive();           // This completes a strip of two triangles.
}
}

#endif

layout ( location = 0 ) in Data_to_FS
{
vec3 normal_e;
vec4 vertex_e;
vec2 texCoord;
flat int is_edge;
};

layout ( binding = BIND_TEXUNIT ) uniform sampler2D tex_unit_0;
layout ( location = 0 ) out vec4 frag_color;

void
fs_main()
{
// Choose color based on which side of spiral we are rendering.
//
vec4 color =
bool(is_edge) ? uc.color_edge :
gl_FrontFacing ? uc.color_front : uc.color_back;

// Get filtered texel, unless the fragment belongs to an edge primitive.
//
vec4 texel = bool(is_edge) ? vec4(1,1,1,1) : texture(tex_unit_0,texCoord);

// If texel is too dark don't write fragment, leaving a hole.
//
bool hole = texel.r + texel.g + texel.b < 0.05;