#version 330

/// (C) Stratasys 2014-2015
///  Shaded Toolpath Rendering Shader for INSIGHT software 
///  Takes in line (GL_LINES) as input primitive and generates vertices 
///        for a triangle strip (GL_TRIANGLE_STRIP) for a circular line-offset geometry with end-caps.
///     Currently only takes the first (i-1 = 0) and second vertex (i=1) for geometry offset (i.e. it does not work for strips)
///  HARDWARE LIMITATIONS:
///        Different hardware were found to have different limits for max_vertices and total # of components output.
///        CPU code now checks for this limitation (GLEngine33.cpp) and avoids using shaders if the hardware does not have 
///     the necessary  vertex emit capability required by this shader.
///        This limit is derived from hardware limit as well as the number of per-vertex attributes sent 
///        to the fragment shader. Currently, we only send per-vertex Color attributes and Position ( 8 components).
///     Example:
///          On GeForce GTX550 Ti card, the GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS is 544. Max Emit vertices = 544 /8 = 68
///           On Quadro K2000 card, GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS is 1024. Max emit vertices = 1024/8 = 128
///        

layout (lines) in;
layout (triangle_strip, max_vertices = 66) out;

layout (std140) uniform Matrices {
    mat4 m_pvm;
    mat3 m_normal;    
    mat4 m_model;
    mat4 m_viewModel;
};

layout (std140) uniform Materials {
	vec4 diffuse;
	vec4 ambient;
	vec4 specular;
	vec4 emissive;
	float shininess;	
};

in vData
{
    vec3 norm;
    vec4 colr;
    float beadsize;
} vertices[];

out fData
{
    vec4  _colr;
} frag;

const float PI = 3.1415926;

// drawTrapeziodOffsets draws a trapezoid from vertex0 to vertex1 
// the offset amount at each vertex being corresponding the beadSize
//
// [in]     overrideColor   Use white color to draw trapezoid
// 
void drawTrapeziodOffsets ( )
{
    float line_offset = vertices[0].beadsize;
    float line_offset_1 = vertices[1].beadsize;
    
     mat4 m_pvm_inv = inverse(m_pvm);
    
        //get line segment's direction unit vector
    vec4 lin_dir =   (gl_in[1].gl_Position - gl_in[0].gl_Position );
    lin_dir = normalize(m_pvm_inv *lin_dir);
    
        //get line segment's normal direction
    vec4 lin_norm = vec4( lin_dir.y, -lin_dir.x, lin_dir.z, 0.0) ;
        
        /////   STRAIGHT OFFSETS
    gl_Position =  gl_in[0].gl_Position - m_pvm * (lin_norm*line_offset);    
    frag._colr = vertices[0].colr;
    EmitVertex();
        
    gl_Position =  gl_in[1].gl_Position - m_pvm *(lin_norm*line_offset_1);
    frag._colr = vertices[1].colr;
    EmitVertex();
        
    gl_Position =  gl_in[0].gl_Position ;
    frag._colr = vertices[0].colr;
    EmitVertex();
        
    gl_Position =  gl_in[1].gl_Position ;
    frag._colr = vertices[1].colr;
    EmitVertex();
        
    gl_Position =  gl_in[0].gl_Position + m_pvm *(lin_norm*line_offset);
    frag._colr = vertices[0].colr;
    EmitVertex();
        
    gl_Position =  gl_in[1].gl_Position + m_pvm * (lin_norm*line_offset_1);
    frag._colr = vertices[1].colr;
    EmitVertex();
        
    EndPrimitive();
    return;
}

// drawCircle draws a circle around a given vertex i as the center
//
// [in]     vInd       vertex index, index into the vertices array
// [in]     line_offset   Use white color to draw trapezoid
// [in]     direction   determines the direction for drawing the semi-circle
//                      true signifies you are drawing the setment start point, 
//                      false signifiies drawing semi-circle at the segment end point in the opposite 
//                      direction.
void drawSemiCircle ( int vInd, float radius, bool direction)
{
     mat4 m_pvm_inv = inverse(m_pvm);
    
        //get line segment's direction unit vector
    vec4 lin_dir =   (gl_in[1].gl_Position - gl_in[0].gl_Position );
    
    lin_dir = normalize(m_pvm_inv *lin_dir);
    
        //get line segment's normal direction
    vec4 line_norm = vec4( lin_dir.y, -lin_dir.x, lin_dir.z, 0.0) ;
           
    const int nSectors = 8;
    float delta = PI / nSectors;
    float start_ang = atan ( line_norm.y , line_norm.x ) + PI;
    if ( direction ) start_ang += PI ;
    
    float end_ang = PI ;
    
    float inc = 0.0; 
    bool terminate = false;
    
    gl_Position =  gl_in[vInd].gl_Position ;
    frag._colr = vertices[vInd].colr;
    EmitVertex();
        
        // this loop draws offset point of each sector
        // of the circle and the center point
    while ( !terminate )  //generate vertices at positions on the circumference from 0 to pi
    {   
        vec4 off = vec4( 
                            radius*cos( start_ang + inc ),
                            radius*sin( start_ang + inc ),
                            0.0, 0.0
                        );
        
        gl_Position =  gl_in[vInd].gl_Position +  m_pvm *off;
        frag._colr =vertices[vInd].colr;
        EmitVertex();
        
        // Draw 
        gl_Position =  gl_in[vInd].gl_Position ;
        frag._colr = vertices[vInd].colr;
        EmitVertex();
    
        inc += delta;
        
        if ( inc > end_ang )
            terminate = true;
    }
    
    EndPrimitive();
}

// Draw circular end caps for the toolpaths
void drawEndCaps ( )
{
    drawSemiCircle ( 1, vertices[1].beadsize, true );
    drawSemiCircle ( 0, vertices[0].beadsize, false );
}

// Main drawing routine for shaded toolpaths 
void main()
{
    drawTrapeziodOffsets ();
    drawEndCaps( );
    
}
