Copyright 2008-2014 Matus Chochlik. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <vector>
namespace oglplus {
class TessellationExample : public Example
{
private:
    Context gl;
    Buffer positions, next_cps, prev_cps, tess_levels;
 
    Lazy<Uniform<Mat4f>> projection_matrix, camera_matrix;
    Lazy<Uniform<Vec2f>> viewport_dimensions;
    GLuint segments;
    GLuint instances;
    GLfloat step;
public:
    TessellationExample(void)
     : prog()
     , projection_matrix(prog, "ProjectionMatrix")
     , camera_matrix(prog, "CameraMatrix")
     , viewport_dimensions(prog, "ViewportDimensions")
     , segments(8)
     , instances(24)
     , step(10.0f)
    {
            "#version 330\n"
            "uniform mat4 CameraMatrix;"
            "uniform float Step;"
            "uniform int InstCount;"
            "in vec4 Position;"
            "in vec3 PrevCP, NextCP;"
            "in vec2 TessLevel;"
            "out vec4 vertPosition;"
            "out vec3 vertPrevCP, vertNextCP;"
            "out vec3 vertColor;"
            "out vec2 vertTessLevel;"
            "void main(void)"
            "{"
            "   float XOffs = gl_InstanceID*Step-(InstCount*Step)*0.5;"
            "   vertColor = normalize(normalize(vec3(1,1,1))-normalize(Position.xyz));"
            "   vertPosition = CameraMatrix * (Position+vec4(XOffs, 0, 0, 0));"
            "   vertPrevCP = mat3(CameraMatrix)*PrevCP;"
            "   vertNextCP = mat3(CameraMatrix)*NextCP;"
            "   vertTessLevel = TessLevel;"
            "}"
        ));
        vert.Compile();
        prog << vert;
            "#version 330\n"
            "#extension ARB_tessellation_shader : enable\n"
            "#define ID gl_InvocationID\n"
            "layout(vertices = 4) in;"
            "layout(vertices = 4) out;"
            "in vec4 vertPosition[];"
            "in vec3 vertPrevCP[], vertNextCP[];"
            "in vec3 vertColor[];"
            "in vec2 vertTessLevel[];"
            "patch out vec3 tecoPosition[4], tecoPrevCP[4], tecoNextCP[4], tecoColor[4];"
            "float calc_tl(vec3 v)"
            "{"
            "   float d = pow(dot(vec3(0.0, 0.0,-1.0), normalize(v)), 3);"
            "   return clamp(d/pow(abs(v.z)*0.1+1.5, 1.5), 0.0, 1.0);"
            "}"
            "vec3 mid(int id1, int id2)"
            "{"
            "   return mix(vertPosition[id1].xyz, vertPosition[id2].xyz, 0.5);"
            "}"
            "void main(void)"
            "{"
            "   int NextID = (ID+2)%4;"
            "   int PrevID = (ID+3)%4;"
            "   if(ID == 0 || ID == 1)"
            "   {"
            "       gl_TessLevelInner[ID] = int("
            "           (vertTessLevel[ID].y+vertTessLevel[NextID].y)*"
            "           calc_tl(mix(mid(0,2), mid(1,3), 0.5))"
            "       );"
            "   }"
            "   gl_TessLevelOuter[ID] = 1+int(vertTessLevel[PrevID].x*calc_tl(mid(PrevID, ID)));"
            "   tecoPosition[ID] = vertPosition[ID].xyz;"
            "   tecoPrevCP[ID] = vertPrevCP[ID];"
            "   tecoNextCP[ID] = vertNextCP[ID];"
            "   tecoColor[ID] = vertColor[ID];"
            "}"
        ));
        teco.Compile();
        prog << teco;
            "#version 330\n"
            "#extension ARB_tessellation_shader : enable\n"
            "layout(quads, equal_spacing, ccw) in;"
            "uniform mat4 ProjectionMatrix;"
            "patch in vec3 tecoPosition[4], tecoPrevCP[4], tecoNextCP[4], tecoColor[4];"
            "out vec4 teevPatchDistance;"
            "out vec3 teevColor;"
            "vec3 teevPosition[16] = vec3[16]("
            "   tecoPosition[0],"
            "   tecoPosition[0]+tecoNextCP[0],"
            "   tecoPosition[1]+tecoPrevCP[1],"
            "   tecoPosition[1],"
            "   tecoPosition[0]+tecoPrevCP[0],"
            "   tecoPosition[0]+tecoPrevCP[0]+tecoNextCP[0],"
            "   tecoPosition[1]+tecoPrevCP[1]+tecoNextCP[1],"
            "   tecoPosition[1]+tecoNextCP[1],"
            "   tecoPosition[3]+tecoNextCP[3],"
            "   tecoPosition[3]+tecoPrevCP[3]+tecoNextCP[3],"
            "   tecoPosition[2]+tecoPrevCP[2]+tecoNextCP[2],"
            "   tecoPosition[2]+tecoPrevCP[2],"
            "   tecoPosition[3],"
            "   tecoPosition[3]+tecoPrevCP[3],"
            "   tecoPosition[2]+tecoNextCP[2],"
            "   tecoPosition[2] "
            ");"
            "const mat4 B = mat4("
            "   -1, 3,-3, 1,"
            "    3,-6, 3, 0,"
            "   -3, 3, 0, 0,"
            "    1, 0, 0, 0 "
            ");"
            "mat4 Px, Py, Pz;"
            "void main(void)"
            "{"
            "   float u = gl_TessCoord.x;"
            "   float v = gl_TessCoord.y;"
            "   for(int j=0; j!=4; ++j)"
            "   for(int i=0; i!=4; ++i)"
            "   {"
            "       int k = j*4+i;"
            "       Px[j][i] = teevPosition[k].x;"
            "       Py[j][i] = teevPosition[k].y;"
            "       Pz[j][i] = teevPosition[k].z;"
            "   }"
            "   mat4 Cx = B * Px * B;"
            "   mat4 Cy = B * Py * B;"
            "   mat4 Cz = B * Pz * B;"
            "   vec4 up = vec4(u*u*u, u*u, u, 1);"
            "   vec4 vp = vec4(v*v*v, v*v, v, 1);"
            "   vec4 tempPosition = vec4(dot(Cx * vp, up), dot(Cy * vp, up), dot(Cz * vp, up), 1.0);"
            "   gl_Position = ProjectionMatrix * tempPosition;"
            "   teevPatchDistance = vec4(u, v, 1.0-u, 1.0-v);"
            "   teevColor = mix("
            "       mix(tecoColor[0], tecoColor[1], u),"
            "       mix(tecoColor[3], tecoColor[2], u),"
            "       v"
            "   );"
            "}"
        ));
        teev.Compile();
        prog << teev;
            "#version 330\n"
            "layout(triangles) in;"
            "layout(triangle_strip, max_vertices = 3) out;"
            "uniform vec2 ViewportDimensions;"
            "in vec4 teevPatchDistance[3];"
            "in vec3 teevColor[3];"
            "out vec3 geomEdge;"
            "out vec3 geomColor;"
            "void main(void)"
            "{"
            "   vec2 ScreenPos[3];"
            "   vec4 PatchDist[3];"
            "   for(int v=0; v!=3; ++v)"
            "   {"
            "       ScreenPos[v] ="
            "           ViewportDimensions*"
            "           gl_in[v].gl_Position.xy/"
            "           gl_in[v].gl_Position.w;"
            "       PatchDist[v] = vec4("
            "           teevPatchDistance[v].x < 1.0 ? 0.0 : 1.0,"
            "           teevPatchDistance[v].y < 1.0 ? 0.0 : 1.0,"
            "           teevPatchDistance[v].z < 1.0 ? 0.0 : 1.0,"
            "           teevPatchDistance[v].w < 1.0 ? 0.0 : 1.0 "
            "       );"
            "   }"
            "   vec2 TempVect[3];"
            "   for(int v=0; v!=3; ++v)"
            "   {"
            "       TempVect[v] ="
            "           ScreenPos[(v+2)%3]-"
            "           ScreenPos[(v+1)%3];"
            "   }"
            "   for(int v=0; v!=3; ++v)"
            "   {"
            "       float Dist = abs("
            "           TempVect[(v+1)%3].x * TempVect[(v+2)%3].y-"
            "           TempVect[(v+1)%3].y * TempVect[(v+2)%3].x "
            "       ) / length(TempVect[v]);"
            "       float Width = 0.6+dot(PatchDist[(v+1)%3], PatchDist[(v+2)%3])*1.4;"
            "       geomEdge = vec3(0, 0, 0);"
            "       geomEdge[v] = Dist/Width;"
            "       geomColor = teevColor[v];"
            "       gl_Position = gl_in[v].gl_Position;"
            "       EmitVertex();"
            "   }"
            "   EndPrimitive();"
            "}"
        ));
        geom.Compile();
        prog << geom;
            "#version 330\n"
            "in vec3 geomEdge;"
            "in vec3 geomColor;"
            "out vec3 fragColor;"
            "void main(void)"
            "{"
            "   float EdgeDist = min(min(geomEdge.x, geomEdge.y), geomEdge.z);"
            "   float EdgeAlpha = exp2(-pow(EdgeDist, 2.0));"
            "   vec3 EdgeColor = vec3(0.0, 0.0, 0.0);"
            "   fragColor = mix(geomColor, EdgeColor, EdgeAlpha);"
            "}"
        ));
        frag.Compile();
        prog << frag;
        prog.Link();
        prog.Use();
        vao.Bind();
        GLfloat r_min = 2.0f;
        GLfloat r_max = 4.5f;
        GLfloat seg_step = 1.0f/segments;
        std::vector<GLfloat> pos_data(segments*4*3);
        std::vector<GLfloat> pcp_data(pos_data.size());
        std::vector<GLfloat> ncp_data(pos_data.size());
        std::vector<GLfloat> ptl_data(segments*4*2);
        GLfloat kpa = (4.0f/segments)*(4.0f/3.0f)*(sqrt(2.0f)-1.0f)/sqrt(2.0f);
        GLuint k_pos=0, k_pcp=0, k_ncp=0, k_ptl=0;
        for(GLuint s=0; s!=segments;++s)
        {
            
            pos_data[k_pos++] = +r_min*Cos(a1);
            pos_data[k_pos++] = 0.0f;
            pos_data[k_pos++] = -r_min*Sin(a1);
            pcp_data[k_pcp++] = (+r_min*Cos(a2)-r_min*Cos(a0))*kpa;
            pcp_data[k_pcp++] = 0.0f;
            pcp_data[k_pcp++] = (-r_min*Sin(a2)+r_min*Sin(a0))*kpa;
            ncp_data[k_ncp++] = (+r_max*Cos(a1)-r_min*Cos(a1))/3.0f;
            ncp_data[k_ncp++] = 0.0f;
            ncp_data[k_ncp++] = (-r_max*Sin(a1)+r_min*Sin(a1))/3.0f;
            ptl_data[k_ptl++] = 3.0;
            ptl_data[k_ptl++] = 3.0;
            
            pos_data[k_pos++] = +r_max*Cos(a1);
            pos_data[k_pos++] = 0.0f;
            pos_data[k_pos++] = -r_max*Sin(a1);
            pcp_data[k_pcp++] = (+r_min*Cos(a1)-r_max*Cos(a1))/3.0f;
            pcp_data[k_pcp++] = 0.0f;
            pcp_data[k_pcp++] = (-r_min*Sin(a1)+r_max*Sin(a1))/3.0f;
            ncp_data[k_ncp++] = (+r_max*Cos(a2)-r_max*Cos(a0))*kpa;
            ncp_data[k_ncp++] = 0.0f;
            ncp_data[k_ncp++] = (-r_max*Sin(a2)+r_max*Sin(a0))*kpa;
            ptl_data[k_ptl++] = 20.0*r_max/r_min;
            ptl_data[k_ptl++] = 10.0*r_max/r_min;
            
            pos_data[k_pos++] = +r_max*Cos(a2);
            pos_data[k_pos++] = 0.0f;
            pos_data[k_pos++] = -r_max*Sin(a2);
            pcp_data[k_pcp++] = (+r_max*Cos(a1)-r_max*Cos(a3))*kpa;
            pcp_data[k_pcp++] = 0.0f;
            pcp_data[k_pcp++] = (-r_max*Sin(a1)+r_max*Sin(a3))*kpa;
            ncp_data[k_ncp++] = (+r_min*Cos(a2)-r_max*Cos(a2))/3.0f;
            ncp_data[k_ncp++] = 0.0f;
            ncp_data[k_ncp++] = (-r_min*Sin(a2)+r_max*Sin(a2))/3.0f;
            ptl_data[k_ptl++] = 3.0;
            ptl_data[k_ptl++] = 3.0;
            
            pos_data[k_pos++] = +r_min*Cos(a2);
            pos_data[k_pos++] = 0.0f;
            pos_data[k_pos++] = -r_min*Sin(a2);
            pcp_data[k_pcp++] = (+r_max*Cos(a2)-r_min*Cos(a2))/3.0f;
            pcp_data[k_pcp++] = 0.0f;
            pcp_data[k_pcp++] = (-r_max*Sin(a2)+r_min*Sin(a2))/3.0f;
            ncp_data[k_ncp++] = (+r_min*Cos(a1)-r_min*Cos(a3))*kpa;
            ncp_data[k_ncp++] = 0.0f;
            ncp_data[k_ncp++] = (-r_min*Sin(a1)+r_min*Sin(a3))*kpa;
            ptl_data[k_ptl++] = 20.0;
            ptl_data[k_ptl++] = 10.0;
        }
        positions.Bind(Buffer::Target::Array);
        Buffer::Data(Buffer::Target::Array, pos_data);
        VertexArrayAttrib position_attr(prog, "Position");
        position_attr.Setup<
Vec3f>();
        position_attr.Enable();
        prev_cps.Bind(Buffer::Target::Array);
        Buffer::Data(Buffer::Target::Array, pcp_data);
        VertexArrayAttrib prev_cp_attr(prog, "PrevCP");
        prev_cp_attr.Setup<
Vec3f>();
        prev_cp_attr.Enable();
        next_cps.Bind(Buffer::Target::Array);
        Buffer::Data(Buffer::Target::Array, ncp_data);
        VertexArrayAttrib next_cp_attr(prog, "NextCP");
        next_cp_attr.Setup<
Vec3f>();
        next_cp_attr.Enable();
        tess_levels.Bind(Buffer::Target::Array);
        Buffer::Data(Buffer::Target::Array, ptl_data);
        VertexArrayAttrib tess_lvl_attr(prog, "TessLevel");
        tess_lvl_attr.Setup<
Vec2f>();
        tess_lvl_attr.Enable();
        Uniform<GLfloat>(prog, 
"Step").
Set(step);
        Uniform<GLint>(prog, 
"InstCount").
Set(instances);
        gl.ClearColor(0.9f, 0.9f, 0.9f, 0.0f);
        gl.ClearDepth(1.0f);
    }
    void Reshape(GLuint width, GLuint height)
    {
        gl.Viewport(width, height);
        projection_matrix.Set(
                Degrees(70),
                double(width)/height,
                1, 200
            )
        );
        viewport_dimensions.Set(width, height);
    }
    {
        gl.Clear().ColorBuffer().DepthBuffer();
        GLfloat a = std::pow(
SineWave(
float(time) / 51.7f), 3.0f);
        camera_matrix.Set(
                    0.55f*a*instances*step,
                    step*0.5+std::pow(b, 4)*80.0f,
                    SineWave(time / 7.1)*step*(1.0-std::pow(b, 2)*0.9)
 
                ),
                    0.45f*a*instances*step,
                    -step+std::pow(b, 2)*30.0f,
                    0.0f
                )
            )
        );
        gl.DrawArraysInstanced(
            0,
            segments*4,
            instances
        );
    }
    {
        return time < 60.0;
    }
};
void setupExample(ExampleParams& ){ }
std::unique_ptr<ExampleThread> makeExampleThread(
    Example& ,
    unsigned ,
    const ExampleParams& 
){ return std::unique_ptr<ExampleThread>(); }
std::unique_ptr<Example> makeExample(const ExampleParams& )
{
    return std::unique_ptr<Example>(new TessellationExample);
}
}