Subversion Repositories gelsvn

Rev

Rev 601 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* ----------------------------------------------------------------------- *
 * This file is part of GEL, http://www.imm.dtu.dk/GEL
 * Copyright (C) the authors and DTU Informatics
 * For license and list of authors, see ../../doc/intro.pdf
 * ----------------------------------------------------------------------- */

#include "IDBufferWireFrameRenderer.h"

#include "../CGLA/Vec4f.h"
#include "../CGLA/Vec2f.h"
#include "../CGLA/Vec3f.h"
#include "../HMesh/Manifold.h"
#include "../HMesh/AttributeVector.h"

#include "../GLGraphics/draw.h"

#include "glsl_shader.h"

using namespace std;
using namespace CGLA;
using namespace GLGraphics;
using namespace HMesh;

namespace
{
    string line_atten_frag = 
        "uniform int ATTEN_MODE;\n"
        "uniform float THICKNESS;\n"
        "uniform sampler2DRect IDMAP;\n"
        "uniform float TRANSITION;\n"
        "\n"
        "varying vec3 _id;\n"
        "varying vec2 p_2d_0;\n"
        "varying vec2 p_2d_1;\n"
        "\n"
        "const int LINEAR_ATTENUATION = 1;\n"
        "const int HERMITE_ATTENUATION = 2;\n"
        "\n"
        "const float HERMITE_COL_ATTEN_BIAS = 0.25;\n"
        "const float LINEAR_COL_ATTEN_BIAS = 0.4;\n"
        "const float HERMITE_ATTEN_BEGIN = 0.75;\n"
        "const float HERMITE_ATTEN_END = 1.0;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "       vec2 dir = (p_2d_1-p_2d_0);\n"
        "       float sq_len01 = dot(dir,dir);\n"
        "       vec2 v0 = (gl_FragCoord.xy)-p_2d_0;\n"
        "       vec2 v1 = (gl_FragCoord.xy)-p_2d_1;\n"
        "       float t = dot(dir, v0);\n"
        "       float d;\n"
        "       \n"
        "       if(t<=0.0)\n"
        "                       d = length(v0);\n"
        "       else if(t>= sq_len01)\n"
        "                       d = length(v1);\n"
        "       else\n"
        "                       d = length(dir * t / sq_len01 - v0);\n"
        "       \n"
        "       vec3 iddiff = texture2DRect(IDMAP,gl_FragCoord.xy).xyz - _id;\n"
        "       if(dot(iddiff, iddiff)<1e-6)\n"
        "               gl_FragDepth = 0.0;\n"
        "       else    \n"
        "               gl_FragDepth = gl_FragCoord.z;\n"
        "       \n"
        "       \n"
        "       float t1 = THICKNESS;\n"
        "       if(ATTEN_MODE == HERMITE_ATTENUATION)\n"
        "               {\n"
        "                       float width_atten = smoothstep(HERMITE_ATTEN_END,HERMITE_ATTEN_BEGIN,\n"
        "                                                                                                                                                gl_FragCoord.z);\n"
        "                       t1 *= width_atten;\n"
        "               }\n"
        "       else if(ATTEN_MODE == LINEAR_ATTENUATION)\n"
        "               {\n"
        "                       float width_atten = 1.0-gl_FragCoord.z;\n"
        "                       t1 *= width_atten;\n"
        "               }                       \n"
        "       float t2 = t1+TRANSITION;\n"
        "\n"
        "       float I;\n"
        "       if(d<t1) \n"
        "                       I = 1.0;\n"
        "       else\n"
        "       {\n"
        "                       float x = (d-t1);\n"
        "                       I = exp2(-x*x*2);\n"
        "       }\n"
        "       gl_FragColor.rgb = gl_Color.rgb;\n"
        "       gl_FragColor.a = I;\n"
        "}\n";

    string line_frag = 
        "uniform sampler2DRect IDMAP;\n"
        "uniform float TRANSITION;\n"
        "       \n"
        "varying vec3 _id;\n"
        "varying vec2 p_2d_0;\n"
        "varying vec2 p_2d_1;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "       vec2 dir = (p_2d_1-p_2d_0);\n"
        "       float sq_len01 = dot(dir,dir);\n"
        "       vec2 v0 = gl_FragCoord.xy-p_2d_0;\n"
        "       vec2 v1 = gl_FragCoord.xy-p_2d_1;\n"
        "       float t = dot(dir, v0);\n"
        "       float sq_d;\n"
        "       \n"
        "       if(t<=0.0)\n"
        "               sq_d = dot(v0,v0);\n"
        "       else if(t>= sq_len01)\n"
        "               sq_d = dot(v1,v1);\n"
        "       else\n"
        "               {\n"
        "                       float area = (v0.x*v1.y - v0.y * v1.x);\n"
        "                       sq_d = area*area/sq_len01;\n"
        "               }\n"
        "\n"
        "       vec3 iddiff = texture2DRect(IDMAP,gl_FragCoord.xy).xyz -_id;\n"
        "       if(dot(iddiff, iddiff)<1e-6)\n"
        "               gl_FragDepth = 0.0;\n"
        "       else    \n"
        "               gl_FragDepth = gl_FragCoord.z;\n"
        "       \n"
        "       gl_FragColor.a = exp2(-sq_d*2.0);\n"
        "       gl_FragColor.rgb = gl_Color.rgb;\n"
        "}\n";

    string line_vert = 
        "uniform float THICKNESS;\n"
        "uniform vec2 WIN_SCALE;\n"
        "uniform vec2 INV_WIN_SCALE;\n"
        "uniform float TRANSITION;\n"
        "\n"
        "attribute vec4 id;\n"
        "attribute vec4 opp_vertex;\n"
        "attribute vec2 displace;\n"
        "\n"
        "varying vec3 _id;\n"
        "varying vec2 p_2d_0;\n"
        "varying vec2 p_2d_1;\n"
        "\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "       vec4 p0 = gl_ModelViewProjectionMatrix * opp_vertex;\n"
        "       float w0 = p0.w; \n"
        "\n"
        "       vec4 p1 = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
        "       float w1 = p1.w;\n"
        "               \n"
        "       p_2d_0 = (p0.xy/w0+vec2(1,1)) * WIN_SCALE;\n"
        "       p_2d_1 = (p1.xy/w1+vec2(1,1)) * WIN_SCALE;\n"
        "       \n"
        "       vec2 a = normalize(p_2d_1 - p_2d_0);\n"
        "       vec2 a_h = vec2(-a.y, a.x);\n"
        "       vec2 scale = (TRANSITION+THICKNESS)*INV_WIN_SCALE;\n"
        "\n"
        "       gl_Position = (displace.x>0.0) ? p1 : p0;\n"
        "       vec2 offset = a * displace.x + a_h * displace.y;\n"
        "       gl_Position.xy += scale * gl_Position.w * offset;\n"
        "\n"
        "       _id = id.rgb;\n"
        "       gl_FrontColor = gl_Color;\n"
        "       gl_FrontSecondaryColor = gl_SecondaryColor;\n"
        "}\n";

}

namespace GLGraphics
{
    IDBufferWireframeRenderer::~IDBufferWireframeRenderer()
    {
        glDeleteShader(vs);     
        glDeleteShader(fs);
        glDeleteProgram(line_prog);
        glDeleteTextures(1, &idmap);
        glDeleteBuffers(1, &vertex_buffername);
        glDeleteBuffers(1, &colors_buffername);

        glDeleteBuffers(1, &line_id_attrib);
        glDeleteBuffers(1, &line_vertex_pos);
        glDeleteBuffers(1, &line_disp_attrib);                  
        glDeleteBuffers(1, &line_opp_attrib);
    }

    IDBufferWireframeRenderer::IDBufferWireframeRenderer(int _XSZ, int _YSZ,
        HMesh::Manifold& _mesh, 
        float _thickness, 
        float _transition, 
        int atten_mode): 
    mesh(&_mesh), XSZ(_XSZ), YSZ(_YSZ), thickness(_thickness), transition(_transition)
    {

        if(atten_mode == 0 && thickness == 0.0)
        {
            vs = create_glsl_shader(GL_VERTEX_SHADER, line_vert);
            fs = create_glsl_shader(GL_FRAGMENT_SHADER, line_frag);
        }
        else
        {
            vs = create_glsl_shader(GL_VERTEX_SHADER, line_vert);
            fs = create_glsl_shader(GL_FRAGMENT_SHADER, line_atten_frag);
        }

        line_prog = glCreateProgram();
        glAttachShader(line_prog, vs);
        glAttachShader(line_prog, fs);
        glLinkProgram(line_prog);
        glUseProgram(line_prog);

        glUniform1f(glGetUniformLocation(line_prog,"THICKNESS"), 
            thickness);
        glUniform1f(glGetUniformLocation(line_prog,"TRANSITION"), 
            transition);
        glUniform1i(glGetUniformLocation(line_prog,"ATTEN_MODE"),
            atten_mode);
        glUniform2f(glGetUniformLocation(line_prog,"WIN_SCALE"), 
            XSZ/2.0, YSZ/2.0);
        glUniform2f(glGetUniformLocation(line_prog,"INV_WIN_SCALE"), 
            2.0/XSZ, 2.0/YSZ);
        glUniform1i(glGetUniformLocation(line_prog,"IDMAP"), 0);

        id_attrib = glGetAttribLocation(line_prog, "id");
        popp_attrib = glGetAttribLocation(line_prog, "opp_vertex");
        disp_attrib = glGetAttribLocation(line_prog, "displace");
        glUseProgram(0);

        glGenTextures(1, &idmap);
        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T,GL_CLAMP);

        //create the texture
        glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, XSZ, YSZ,
            0, GL_RGB, GL_FLOAT, 0);


        glGenBuffers(1, &vertex_buffername);
        glGenBuffers(1, &colors_buffername);

        glGenBuffers(1, &line_id_attrib);
        glGenBuffers(1, &line_vertex_pos);
        glGenBuffers(1, &line_disp_attrib);
        glGenBuffers(1, &line_opp_attrib);

        triangles = static_cast<int>(mesh->no_faces());
        vector<Vec3f> verts;
        vector<Vec3f> cols;

        unsigned int k = 0;
        for(FaceIDIterator f = mesh->faces_begin(); f != mesh->faces_end(); ++f, ++k){
            Vec3uc idv(id_get(k));
            Vec3f idvec(idv[0]/255.0, idv[1]/255.0, idv[2]/255.0);
            for(Walker w = mesh->walker(*f); !w.full_circle(); w = w.circulate_face_ccw()){
                cols.push_back(idvec);
                verts.push_back(Vec3f(mesh->pos(w.vertex())));
            }
        }
        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*verts.size(),
            (float*)&verts[0],GL_STATIC_DRAW);



        glBindBuffer(GL_ARRAY_BUFFER, colors_buffername);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*cols.size(),
            (float*)&cols[0],GL_STATIC_DRAW);


        vector<Vec3f> line_ids;
        vector<Vec3f> vertex_positions;
        vector<Vec2f> displacements;
        vector<Vec3f> opposite_positions;


        quads = 0;
        unsigned int i = 0;
        for(FaceIDIterator f = mesh->faces_begin(); f != mesh->faces_end(); ++f,++i){
            for(Walker w = mesh->walker(*f); !w.full_circle(); w = w.circulate_face_ccw()){
                ++quads;
                Vec3uc idv(id_get(i));
                Vec3f v0(mesh->pos(w.next().vertex()));
                Vec3f v1(mesh->pos(w.next().opp().vertex()));
                Vec3f idvec(idv[0]/255.0, idv[1]/255.0, idv[2]/255.0);

                line_ids.push_back(idvec);
                opposite_positions.push_back(v0);
                displacements.push_back(Vec2f(1,-1));
                vertex_positions.push_back(v1);


                line_ids.push_back(idvec);
                opposite_positions.push_back(v0);
                displacements.push_back(Vec2f(1, 1));
                vertex_positions.push_back(v1);


                line_ids.push_back(idvec);
                opposite_positions.push_back(v0);
                displacements.push_back(Vec2f(-1,1));
                vertex_positions.push_back(v1);


                line_ids.push_back(idvec);
                opposite_positions.push_back(v0);
                displacements.push_back(Vec2f(-1,-1));
                vertex_positions.push_back(v1);
            }
        }

        glBindBuffer(GL_ARRAY_BUFFER, line_id_attrib);

        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*line_ids.size(),
            (float*)&line_ids[0],GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, line_opp_attrib);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*opposite_positions.size(),
            (float*)&opposite_positions[0],GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, line_disp_attrib);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*displacements.size(),
            (float*)&displacements[0],GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, line_vertex_pos);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*vertex_positions.size(),
            (float*)&vertex_positions[0],GL_STATIC_DRAW);



    }


    void IDBufferWireframeRenderer::draw(const Vec3f& color, const Vec3f& clear_color)
    {
        // push those attributes we change.
        glPushAttrib(GL_COLOR_BUFFER_BIT|
            GL_CURRENT_BIT|
            GL_TRANSFORM_BIT|
            GL_DEPTH_BUFFER_BIT);

        // Store information about whether we use lighting
        GLboolean lights_on;
        glGetBooleanv(GL_LIGHTING, &lights_on);

        // Store color information
        Vec4f current_color;
        glGetFloatv(GL_CURRENT_COLOR, &current_color[0]);

        // Store the current draw buffer 
        GLint _currentDrawbuf;
        glGetIntegerv(GL_DRAW_BUFFER, &_currentDrawbuf); 

        // Enable depth testing
        glEnable(GL_DEPTH_TEST);

        // ------------------------------
        // Pass 1: Draw the ID map
        // Each polygon has a unique ID which is coded as a colour and drawn
        // into the ID buffer
        glDisable(GL_LIGHTING);
        glClearColor(0,0,0,0);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
        glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
        glEnableClientState(GL_VERTEX_ARRAY);

        glBindBuffer(GL_ARRAY_BUFFER, colors_buffername);
        glColorPointer(3,GL_FLOAT,0,static_cast<char*>(0));
        glEnableClientState(GL_COLOR_ARRAY);

        int vertex_idx = 0;
        for(FaceIDIterator f = mesh->faces_begin(); f != mesh->faces_end(); ++f){
            glBegin(GL_POLYGON);
            for(Walker w = mesh->walker(*f); !w.full_circle(); w = w.circulate_face_ccw())
                 glArrayElement(vertex_idx++);
            glEnd();
        }
        glFinish();
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);

        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
        glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0,0,0,0,XSZ, YSZ);

        // Clear color buffer but retain depth buffer.
        glClear(GL_COLOR_BUFFER_BIT);

        // Enable blending for all subsequent passes
        glEnable(GL_BLEND);

        // ------------------------------
        // PASS 3: Draw lines using ID map texture.
        // For each polygon, all edges are drawn as prefiltered
        // lines. A given fragment belonging to a line will be written
        // to the framebuffer if either of the following three conditions are met
        // - its ID matches the contents of the ID  buffer for the corresponding 
        // pixel (in the ID buffer)
        // - The ID of the corresponding pixel is 0 - meaning we are outside.
        // - The depth test is passed.
        // The final condition ensures that line pixels which are on an interior
        // contour will be drawn.
        //
        // If the line fragment is written into the framebuffer - two values are
        // actually written: The colour of the line and an alpha value which
        // corresponds to the value of the filter function.
        //
        // During this pass, blending is enabled and the blending equation is set
        // to max. Since the alpha values are the values of the filter (large if 
        // close to line) this means that we replace a pixel value with an 
        // incoming fragment if the incoming fragment is closer to a line
        // than the pixel value.
        //
        // The depth values are not changed during this pass.
        glEnable(GL_TEXTURE_RECTANGLE_ARB);
        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
        glDepthMask(GL_FALSE);
        glBlendEquation(GL_MAX);
        glUseProgram(line_prog);
        glColor3fv(color.get());
        glSecondaryColor3fv(clear_color.get());

        float lw;
        glGetFloatv(GL_LINE_WIDTH, &lw);
        glLineWidth(ceil(2.0*(thickness+transition)));


        glBindBuffer(GL_ARRAY_BUFFER, line_id_attrib);
        glVertexAttribPointer(id_attrib, 3,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
        glEnableVertexAttribArray(id_attrib);


        glBindBuffer(GL_ARRAY_BUFFER, line_disp_attrib);
        glVertexAttribPointer(disp_attrib, 2,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
        glEnableVertexAttribArray(disp_attrib);


        glBindBuffer(GL_ARRAY_BUFFER, line_opp_attrib);
        glVertexAttribPointer(popp_attrib, 3,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
        glEnableVertexAttribArray(popp_attrib);


        glBindBuffer(GL_ARRAY_BUFFER, line_vertex_pos);
        glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
        glEnableClientState(GL_VERTEX_ARRAY);

        glDrawArrays(GL_QUADS, 0, quads*4);

        glDisableVertexAttribArray(id_attrib);
        glDisableVertexAttribArray(disp_attrib);
        glDisableVertexAttribArray(popp_attrib);
        glDisableClientState(GL_VERTEX_ARRAY);


        glLineWidth(lw);

        glUseProgram(0);
        glDisable(GL_TEXTURE_RECTANGLE_ARB);
        glDepthMask(GL_TRUE);

        // ------------------------------
        // Pass 4: Draw with shading
        // In this pass we draw the shaded model. At this point, the framebuffer
        // contains alpha values and line colours and also depth values.
        //
        // The depth test is set to `equal' and the shaded fragments from the 
        // filled polygons are combined with the line colours using the alpha 
        // values already stored in the frame buffer.
        //
        // The framebuffer lines along the contour also need to be blended. 
        // Hence, a screen filling quad is drawn. 
        glDepthFunc(GL_LEQUAL);
        glBlendEquation(GL_FUNC_ADD);
        glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, 
            GL_ZERO, GL_ONE);
        if(lights_on)
        {
            glEnable(GL_LIGHTING);
            glEnable(GL_LIGHT0);
        }else
            glColor4fv(current_color.get());

        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
        glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
        glEnableClientState(GL_VERTEX_ARRAY);

        vertex_idx = 0;
        for(FaceIDIterator f = mesh->faces_begin(); f != mesh->faces_end(); ++f){
                        Vec3f n(normal(*mesh, *f));
            glNormal3fv(n.get());
            glBegin(GL_POLYGON);
            for(Walker w = mesh->walker(*f); !w.full_circle(); w = w.circulate_face_ccw())
                 glArrayElement(vertex_idx++);
            glEnd();
        }
        glDisableClientState(GL_VERTEX_ARRAY);


        glDisable(GL_LIGHTING);


        double mvmat[16];
        glGetDoublev(GL_MODELVIEW_MATRIX, mvmat);
        double prjmat[16];
        glGetDoublev(GL_PROJECTION_MATRIX, prjmat);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glColor3fv(clear_color.get());
        glBegin(GL_QUADS);
        glVertex3f(-1,-1,1);
        glVertex3f( 1,-1,1);
        glVertex3f( 1, 1,1);
        glVertex3f(-1, 1,1);
        glEnd();



        glMatrixMode(GL_PROJECTION);
        glLoadMatrixd(prjmat);
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixd(mvmat);

        glPopAttrib();
        if(lights_on)
            glEnable(GL_LIGHTING);

        //                      cout << gluErrorString(glGetError()) << endl;
    }
}