Skip to content
Snippets Groups Projects
Select Git revision
  • 2c5cdac4429a3af2c3e55930ef6f92591f77bd0c
  • main default protected
2 results

ProjectorModule.cpp

Blame
  • ProjectorModule.cpp 94.26 KiB
    /***************************************************************************
     *  ProjectorModule.cpp
     *  Part of Reveal
     *  2015-  Florent Berthaut
     *  hitmuri.net
     ****************************************************************************/
    /*
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 59 Temple Place - Suite 130, Boston, MA 02111-1307, USA.
     */
    
    /*////////////
    Calibration heavily relies on code from :
    https://github.com/Kj1/ofxProjectorKinectCalibration
    https://github.com/kylemcdonald/ofxCv
    */////////////////////
    
    #include "ProjectorModule.hpp"
    
    #include <iostream>
    
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/calib3d/calib3d.hpp"
    
    #include "../Reveal.hpp"
    #include "DepthCamModule.hpp"
    #include "SpaceModule.hpp"
    #include "RevealedModule.hpp"
    
    
    using namespace std;
    using namespace glm;
    using namespace cv;
    
    ProjectorModule::ProjectorModule(): Module() {
        m_type="Projector";
        m_name="projector";
    
        m_space=NULL;
        m_width=640;
        m_height=480;
        m_posX=0;
        m_posY=0;
        m_processOutput=true;
        m_active=true;
        m_postFilter=0;
    	m_transparentBackground=false;
    
        //create window
        m_projWindow = glfwCreateWindow(m_width, m_height,
                                        "Rivill - Projector", NULL, NULL);
        if(!m_projWindow) {
            glfwTerminate();
            exit(EXIT_FAILURE);
        }
        m_rttWidth=1920;
        m_rttHeight=1080;
        glfwSetWindowUserPointer(m_projWindow, this);
        glfwSetWindowSizeCallback(m_projWindow, windowSizeCallback);
        glfwSetWindowPosCallback(m_projWindow, windowPositionCallback);
        glfwSetFramebufferSizeCallback(m_projWindow, framebufferSizeCallback);
    	glfwSetInputMode(m_projWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
        addNameAttribute();
        addRemoveAttribute();
        addAttribute(new Attribute("active",
                                    Attribute::BOOL_ATTRIBUTE,
                                    activeCallback, this, 1,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("window_dimensions",
                                    Attribute::INT_ATTRIBUTE,
                                    windowDimensionsCallback, this, 4,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("fullscreen_monitor",
                                    Attribute::STRING_ATTRIBUTE,
                                    fullscreenMonitorCallback, this, 1,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("view_matrix",
                                    Attribute::FLOAT_ATTRIBUTE,
                                    viewMatrixCallback, this, 16,
                                    Attribute::HIDDEN));
        addAttribute(new Attribute("projection_matrix",
                                    Attribute::FLOAT_ATTRIBUTE,
                                    projectionMatrixCallback, this, 16,
                                    Attribute::HIDDEN));
    
        addAttribute(new Attribute("mirrored",
                                    Attribute::BOOL_ATTRIBUTE,
                                    mirroredCallback, this, 1,
                                    Attribute::LOCAL));
    
        addTransformationAttributes();
        m_attributesMap["scale"]->setAccessibility(Attribute::HIDDEN);
        vector<float> posVec(3,0);
        posVec[2]=-700;
        m_attributesMap["position"]->setFloats(posVec);
    
        addAttribute(new Attribute("attached_to_camera",
                                    Attribute::STRING_ATTRIBUTE,
                                    attachToCamCallback, this, 1,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("calibrate_with_camera",
                                    Attribute::ACTION_STRING_ATTRIBUTE,
                                    calibrateCallback, this, 1,
                                    Attribute::LOCAL,
                                    false));
        addAttribute(new Attribute("output_revealed",
                                    Attribute::BOOL_ATTRIBUTE,
                                    outputRevealedCallback, this, 1,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("post_filter",
                                    Attribute::INT_ATTRIBUTE,
                                    postFilterCallback, this, 1,
                                    Attribute::LOCAL));
        addAttribute(new Attribute("transparent_window",
                                    Attribute::BOOL_ATTRIBUTE,
                                    transparentWindowCallback, this, 1,
                                    Attribute::LOCAL));
    
        //initialize attributes
        vector<string> labels;
        labels.push_back("none");
        m_attributesMap["attached_to_camera"]
                ->editStringValuesChoices().assign(1, labels);
        m_attributesMap["attached_to_camera"]
                ->initStrings(vector<string>(1, labels[0]));
        m_attributesMap["calibrate_with_camera"]
                ->editStringValuesChoices().assign(1, labels);
        m_attributesMap["calibrate_with_camera"]
                ->initStrings(vector<string>(1, labels[0]));
        refreshMonitors();
        m_attributesMap["fullscreen_monitor"]
                ->initStrings(vector<string>(1, "windowed"));
        m_attachedToCam=NULL;
        m_attributesMap["output_revealed"]->initBools(vector<bool>(1, true));
        m_attributesMap["post_filter"]->initInts(vector<int>(1, 0));
        m_attributesMap["active"]->initBools(vector<bool>(1, true));
    
        m_mirrored=false;
    
        //initialize matrices
        m_viewMat = lookAt(vec3(0.0, 0.0, 0.0),
                           vec3(0.0, 0.0, 1.0),
                           vec3(0.0, 1.0, 0.0));
        mat4 glCoordsMat(-1, 0, 0, 0,
                          0, 1, 0, 0,
                          0, 0, 1, 0,
                          0, 0, 0, 1);
        m_viewMat=glCoordsMat*m_viewMat;
        m_attributesMap["view_matrix"]->setFloats(vector<float>(
                                                          value_ptr(m_viewMat),
                                                          value_ptr(m_viewMat)+16));
        m_projMat = perspective(45.0, 4.0/3.0, 100.0, 10000.0);
        m_attributesMap["projection_matrix"]->setFloats(vector<float>(
                                                          value_ptr(m_projMat),
                                                          value_ptr(m_projMat)+16));
    
        vector<int> dims(4,0);
        dims[0]=m_posX;
        dims[1]=m_posY;
        dims[2]=m_width;
        dims[3]=m_height;
        m_attributesMap["window_dimensions"]->setInts(dims);
    
        //calibration
        m_calibrated=false;
        m_patternSquareWidth = 40;
        m_patternPosition = Size(100, 200);
        m_patternSize = Size(6,4);
        m_patternBrightness = 100;
        m_patternPosRange = Size(m_width, m_height);
        m_patternPosOffset = Size(0,0);
    
        Reveal::getInstance()->addContextHandler(this);
    
        m_initialized=false;
    }
    
    ProjectorModule::~ProjectorModule() {}
    
    void ProjectorModule::draw() {
        if(!m_active) {
            return;
        }
    
        glfwMakeContextCurrent(m_projWindow);
    
        if(!m_initialized) {
            initGL();
            m_initialized=true;
        }
    
        //slice to texture
        glBindFramebuffer(GL_FRAMEBUFFER, m_sliceFBO);
        glViewport(0, 0, m_rttWidth, m_rttHeight);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        glDisable(GL_CULL_FACE);
        glDisable(GL_BLEND);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(m_programs[Reveal::SLICEPROG]);
        glUniform1f(m_uniforms[Reveal::SLICEPROG][Reveal::MIRRORED],
                    float(m_mirrored));
        glUniformMatrix4fv(m_uniforms[Reveal::SLICEPROG][Reveal::VIEWPROJMAT], 1,
                           GL_FALSE, value_ptr(m_viewProjMat));
        glUniformMatrix4fv(m_uniforms[Reveal::SLICEPROG][Reveal::VIEWMAT], 1,
                           GL_FALSE, value_ptr(m_transViewMat));
        const vector<Geometry*>& depthGeoms = m_space->getGeoms();
        vector<Geometry*>::const_iterator itDGeom=depthGeoms.begin();
        for(; itDGeom!=depthGeoms.end(); ++itDGeom) {
            (*itDGeom)->draw(m_contextHandlerID,
                             Reveal::SLICEPROG,
                             m_uniforms[Reveal::SLICEPROG],
                             m_visibilityMask);
        }
    
    	//select to texture for 3D models only (GEOM_MODEL)
        glBindFramebuffer(GL_FRAMEBUFFER, m_selectFBO);
        glViewport(0, 0, m_rttWidth, m_rttHeight);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        glDisable(GL_CULL_FACE);
        glEnable(GL_BLEND);
        glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
        glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(m_programs[Reveal::SELECTPROG]);
        glUniform1f(m_uniforms[Reveal::SELECTPROG][Reveal::MIRRORED],
                    float(m_mirrored));
        glUniformMatrix4fv(m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPROJMAT], 1,
                           GL_FALSE, value_ptr(m_viewProjMat));
        glUniformMatrix4fv(m_uniforms[Reveal::SELECTPROG][Reveal::VIEWMAT], 1,
                           GL_FALSE, value_ptr(m_transViewMat));
        glUniform1f(m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPORTWIDTH],
                    m_rttWidth);
        glUniform1f(m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPORTHEIGHT],
                    m_rttHeight);
        glUniform1i(m_uniforms[Reveal::SELECTPROG][Reveal::SLICETEX], 0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_sliceRttTex);
        const vector<Geometry*>& sceneGeoms = Reveal::getInstance()->getGeoms();
        for(auto& geom : sceneGeoms) {
    		if(geom->getGeomID()>=Reveal::GEOM_MODEL) {
    			geom->draw(m_contextHandlerID,
    					   Reveal::SELECTPROG,
    					   m_uniforms[Reveal::SELECTPROG],
    					   m_visibilityMask);
    		}
        }
    
    
        //RENDER TO SCREEN
        if(m_postFilter>0) {
            glBindFramebuffer(GL_FRAMEBUFFER, m_renderFBO);
            glViewport(0, 0, m_rttWidth, m_rttHeight);
        }
        else {
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glViewport(0, 0, m_width, m_height);
        }
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        glEnable(GL_CULL_FACE);
    	if(m_mirrored) {
            glCullFace(GL_FRONT);
        }
        else {
            glCullFace(GL_BACK);
        }
        glEnable(GL_BLEND);
        glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
        glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE);
        glClearColor(0.0, 0.0, 0.0, !m_transparentBackground);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        glUseProgram(m_programs[Reveal::RENDERPROG]);
        glUniform1f(m_uniforms[Reveal::RENDERPROG][Reveal::MIRRORED],
                    float(m_mirrored));
        glUniformMatrix4fv(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPROJMAT], 1,
                           GL_FALSE, value_ptr(m_viewProjMat));
        glUniformMatrix4fv(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWMAT], 1,
                GL_FALSE, value_ptr(m_transViewMat));
        if(m_postFilter>0) {
            glUniform1f(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTWIDTH],
                        m_rttWidth);
            glUniform1f(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTHEIGHT],
                        m_rttHeight);
        }
        else {
            glUniform1f(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTWIDTH],
                        m_width);
            glUniform1f(m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTHEIGHT],
                        m_height);
        }
        glActiveTexture(GL_TEXTURE0);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::SLICETEX], 0);
        glBindTexture(GL_TEXTURE_2D, m_sliceRttTex);
        glActiveTexture(GL_TEXTURE1);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::SELECTTEX], 1);
        glBindTexture(GL_TEXTURE_2D, m_selectTex);
    #ifdef GL42
        glActiveTexture(GL_TEXTURE7);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::OUTPUTTEX], 7);
        glBindImageTexture(7, m_outputTex, 0, GL_FALSE, 0,
                           GL_READ_WRITE, GL_R32UI);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::OUTSHAPESIZE], 
                    m_outShapeSize);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::OUTDEPTHSIZE], 
                    m_outDepthSize);
        glUniform1i(m_uniforms[Reveal::RENDERPROG][Reveal::OUTNBDEPTH], 
                    m_outNbDepth);
    #endif
        for(auto& geom : sceneGeoms) {
            geom->draw(m_contextHandlerID,
    				   Reveal::RENDERPROG,
    				   m_uniforms[Reveal::RENDERPROG],
    				   m_visibilityMask);
        }
    
        //POST PROCESSING
        if(m_postFilter>0) {
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glViewport(0, 0, m_width, m_height);
            glEnable(GL_CULL_FACE);
            glCullFace(GL_BACK);
            glDisable(GL_BLEND);
            glClearColor(0.0, 0.0, 0.0, !m_transparentBackground);
            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
            glUseProgram(m_programs[Reveal::POSTPROG]);
            glActiveTexture(GL_TEXTURE0);
            glUniform1i(m_uniforms[Reveal::POSTPROG][Reveal::RENDERTEX], 0);
            glBindTexture(GL_TEXTURE_2D, m_renderTex);
            glUniform1f(m_uniforms[Reveal::POSTPROG][Reveal::VIEWPORTWIDTH],
                        m_width);
            glUniform1f(m_uniforms[Reveal::POSTPROG][Reveal::VIEWPORTHEIGHT],
                        m_height);
            glUniform1f(m_uniforms[Reveal::POSTPROG][Reveal::POSTFILTER],
                        m_postFilter);
            m_calibGeom->drawDirect(m_contextHandlerID);
        }
    
        //SWAP
        glfwSwapInterval(0);
        glfwSwapBuffers(m_projWindow);
    
        //PROCESS OUTPUT
    #ifdef GL42
        if(m_processOutput) {
            processOutput();
        }
    #endif
    }
    
    void ProjectorModule::initGL() {
        #ifdef OSX
            glewExperimental = GL_TRUE;
        #endif
        glewInit();
    
    
        //POSTPROCESSING
        {
        const char * vertSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "in vec3 vertex; \n"
        " \n"
        "void main(void) {\n"
        "   gl_Position = vec4(vertex, 1.0);\n"
        "}\n"};
    
        const char * fragSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "uniform float viewportWidth;\n"
        "uniform float viewportHeight;\n"
        "uniform float postFilter;\n"
    #ifdef GL42
        "layout(binding=0) uniform sampler2D renderTex;\n"
    #else
        "uniform sampler2D renderTex;\n"
    #endif
        " \n"
        "out vec4 color;\n"
        " \n"
        "void main(void) {\n"
        "   float coordX = gl_FragCoord.x;\n"
        "   float coordY = gl_FragCoord.y;\n"
        "   ivec2 patSize = textureSize(renderTex, 0);\n"
        "   vec3 sumCol = vec3(0,0,0);\n"
        "   float cnt=0;\n"
        "   for(float dX = coordX-postFilter; dX <= coordX+postFilter; ++dX) {\n"
        "       for(float dY = coordY-postFilter; dY <= coordY+postFilter; ++dY) {\n"
        "           if(dX>=0 && dX<viewportWidth \n"
        "                   && dY>=0 && dY<viewportHeight) {\n"
        "               sumCol+=texelFetch(renderTex, \n"
        "                                  ivec2((dX-0.5)/viewportWidth*patSize.x,\n"
        "                                     (dY-0.5)/viewportHeight*patSize.y),\n"
        "                                  0).xyz; \n"
        "               cnt+=1;\n"
        "           }\n"
        "       }\n"
        "   }\n"
        "   color=vec4(sumCol.xyz/cnt, 1.0);\n"
        "}\n"};
        m_programs[Reveal::POSTPROG]
            = Reveal::getInstance()->createProgram(vertSource, fragSource);
        m_uniforms[Reveal::POSTPROG]=map<Reveal::REVIL_UNIFORM, GLint>();
        m_uniforms[Reveal::POSTPROG][Reveal::VIEWPORTWIDTH]
            = glGetUniformLocation(m_programs[Reveal::POSTPROG],"viewportWidth");
        m_uniforms[Reveal::POSTPROG][Reveal::VIEWPORTHEIGHT]
            = glGetUniformLocation(m_programs[Reveal::POSTPROG],"viewportHeight");
        m_uniforms[Reveal::POSTPROG][Reveal::POSTFILTER]
            = glGetUniformLocation(m_programs[Reveal::POSTPROG],"postFilter");
        m_uniforms[Reveal::POSTPROG][Reveal::RENDERTEX]
            = glGetUniformLocation(m_programs[Reveal::POSTPROG],"renderTex");
        }
    
    
    
        //RENDER
        {
        const char * vertSource = {
        #ifdef GL42
        "#version 430 \n"
        #else
        "#version 410 \n"
        #endif
        " \n"
        "in vec3 vertex; \n"
        "in vec2 texCoords;\n"
        " \n"
        "uniform mat4 modelMat;\n"
        "uniform mat4 viewMat;\n"
        "uniform mat4 viewProjMat;\n"
        "uniform float mirrored;\n"
        " \n"
        "out vec3 viewRay;\n"
        "out vec2 uvCoords;\n"
        " \n"
        "void main(void) {\n"
        "   vec4 posWS = modelMat * vec4(vertex, 1.0);\n"
        "   if(mirrored<1) {\n"
        "       posWS.x = -posWS.x;\n"
        "   }\n"
        "   gl_Position = viewProjMat * posWS;\n"
        "   viewRay = posWS.xyz - vec3(viewMat[3][0],\n"
        "                              viewMat[3][1],\n"
        "                              viewMat[3][2]);\n"
        "   uvCoords=texCoords;\n"
        "}\n"};
    
        const char * fragSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        "\n"//defined in Reveal.hpp
    	"const int BOX=0;\n"
    	"const int SPHERE=1;\n"
    	"const int TUBE=2;\n"
    	"const int CONE=3;\n"
    	"const int FRAME=4;\n"
    	"const int PATH=5;\n"
    	"const int MODEL=10;\n"
    	"\n"
        "in vec3 viewRay;\n"
        "in vec2 uvCoords;\n"
        " \n"
    #ifdef GL42
        "layout(binding=0) uniform sampler2D sliceTex;\n"
        "layout(binding=1) uniform sampler2D selectTex;\n"
        "layout(binding=2) uniform sampler3D insideTex;\n"
        "layout(binding=3) uniform sampler2D surfaceTex;\n"
        "layout(binding=4) uniform sampler3D reactTex;\n"
        "layout(binding=5) uniform sampler3D surfDistTex;\n"
        "layout(binding=6) uniform sampler2D gradientTex;\n"
        "layout(r32ui, binding=7) uniform coherent uimage2D outputTex;\n"
    #else
        "uniform sampler2D sliceTex;\n"
        "uniform sampler2D selectTex;\n"
        "uniform sampler3D insideTex;\n"
        "uniform sampler2D surfaceTex;\n"
        "uniform sampler3D reactTex;\n"
        "uniform sampler3D surfDistTex;\n"
        "uniform sampler2D gradientTex;\n"
    #endif
        "uniform float viewportWidth;\n"
        "uniform float viewportHeight;\n"
        "uniform float mirrored;\n"
        "uniform float shapeID;\n"
        "uniform float shapeIDBit;\n"
    	"uniform int shapeGeom;\n"
        "uniform float subShapeID;\n"
        "uniform float subShapesNb;\n"
        "uniform mat4 subShapeInvMat;\n"
        "uniform mat4 viewMat;\n"
        "uniform mat4 modelMat;\n"
        "uniform mat4 invModelMat;\n"
        "uniform vec3 modelScale;\n"
        "uniform int revealedBy;\n"
    	"\n"
        "uniform vec3 shapeColor;\n"
        //0: none, 1:color, 2:texture
        "uniform int surface;\n"
        "uniform vec3 surfaceColor;\n"
    	"uniform int insideOutputOnly;\n"
    	"uniform int reactivity;\n"
        "uniform float thickness;\n"
    	"\n"
        "uniform int insideVisible;\n"
        //0:from center, 1:along x, 2: along y, 3:along z, 4 :from surface
        "uniform int insideStructure;\n"
        "uniform float structureRatio;\n"
    	"\n"
        "uniform float gradientAlpha;\n"
        //0:greyscale, 1:texture
        "uniform int gradientType;\n"
        "uniform int gradientSteps;\n"
        "uniform float gradientCurveRatio;\n"
    	"\n"
        "uniform float densityAlpha;\n"
        //0:layers, 1:grid, 2:pointcloud
        "uniform int densityType;\n"
        "uniform float densityRatio;\n"
        "uniform float densitySize;\n"
        "uniform float densityCurveRatio;\n"
    	"\n"
        "uniform float texAlpha;\n"
        "uniform vec3 texOffset;\n"
        "uniform vec3 texScale;\n"
        "uniform int texGray;\n"
    #ifdef GL42
        "uniform int revSize;\n"
        "uniform int revSurface;\n"
        "uniform int revInside;\n"
        "uniform int revCenter;\n"
        "uniform int revCursor;\n"
        "uniform int revColor;\n"
        "uniform int revHisto;\n"
        "uniform int revVoxels;\n"
        "uniform float revSubShapeID;\n"
        "uniform int shapeOutSize;\n"
        "uniform int histoOutSize;\n"
        "uniform int voxelOutSize;\n"
        "uniform int depthOutSize;\n"
        "uniform int depthOutNb;\n"
    	"uniform vec4 boundingRect;\n"
    #endif
        " \n"
        "vec3 rgb2hsv(vec3 c) {\n"
        "	vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"
        "	vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"
        "	vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"
    	"	float d = q.x - min(q.w, q.y);\n"
        "	float e = 1.0e-10;\n"
        "	return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),d/(q.x + e), q.x);\n"
    	"}\n"
    
    	"float random (vec3 st) {\n"
        "	return fract(sin(dot(st.xyz,\n"
        "                     vec3(12.9898,78.233, 102.234)))*\n"
        "    43758.5453123);\n"
    	"}\n"
    
    	"out vec4 color;\n"
        " \n"
        "void main(void) {\n"
    		//get fragment coordinates
        "   float coordX = (gl_FragCoord.x-0.5)/viewportWidth;\n"
        "   float coordY = (gl_FragCoord.y-0.5)/viewportHeight;\n"
        "   ivec2 sliSize = textureSize(sliceTex, 0);\n"
    		//retrieve pixel in slicing texture
        "   vec4 sli = texelFetch(sliceTex,\n"
        "                         ivec2(coordX*float(sliSize.x), \n"
        "                               coordY*float(sliSize.y)), \n"
        "                         0);\n"
        "   float pixZ=abs(gl_FragCoord.z);\n"
        "   float cutZ=abs(sli.z);\n"
        "   int depthID=int(sli.y)-1;\n"
    #ifdef GL42
        "   int outCols=imageSize(outputTex).x;\n"
    	"   int outOffset=(int(shapeID-1)*depthOutNb+(depthID-1))*revSize;\n"
    #endif
        "   int rendered=0;\n"
        "   vec3 finalColor = shapeColor;\n"
        "   float alpha=0;\n"
    	"	vec3 pixelPosW;\n"
    	"	vec3 pixelPos = vec3(0.0, 0.0, 1.0);\n"
    	"	vec3 ratioWithinBox = vec3(0.0, 0.0, 0.0);\n"
    	"	vec3 outRatioWithinBox = vec3(0.0, 0.0, 0.0);\n"
    		//if there is a slicing pixel aligned with our coordinate
    		//and we are revealed by this depth module
    	"	if(cutZ>0 && (revealedBy & (1<<(1+depthID)))>0 ) { \n"
    			//compute the slicing pixel world position
        "   	pixelPosW = vec3(viewMat[3][0], \n"
    	"                       viewMat[3][1], \n"
        "                       viewMat[3][2])\n"
        "                  + vec3(normalize(viewRay) * sli.w);\n"
        "       pixelPosW.x = pixelPosW.x*(-2.0*(1-mirrored)+1.0);\n"
    			//compute the slicing pixel position in model coords
    	"		pixelPos = (invModelMat * vec4(pixelPosW,1.0)).xyz;\n"
    	"\n"
    			//depending on type, test if slicing pixel inside 
    			//and retrieve distance to the surface on each axis
    	"		vec3 diffMin = vec3(1.0);\n" 
    	"		vec3 diffMax = vec3(1.0);\n"
    	"		switch(shapeGeom) {\n"
    	"			case PATH : {\n"//use min/max/size of current segment
    	"				pixelPos = (subShapeInvMat*vec4(pixelPos,1.0)).xyz;\n"
    	"				if(length(vec2(pixelPos.x, pixelPos.y))<=0.5\n"
    	"						&& (pixelPos.z>-0.5 && pixelPos.z<0.5)) { \n"
    	"					float xx = (pixelPos.x)*(pixelPos.x);\n"
    	"					float yy = (pixelPos.y)*(pixelPos.y);\n"
    	"					float ax = sqrt(0.25-yy);\n"
    	"					diffMin.x = pixelPos.x+ax;\n"
    	"					diffMax.x = ax-pixelPos.x;\n"
    	"					float ay = sqrt(0.25-xx);\n"
    	"					diffMin.y = pixelPos.y+ay;\n"
    	"					diffMax.y = ay-pixelPos.y;\n"
    	"					diffMin.z = pixelPos.z+0.5;\n"
    	"					diffMax.z = 0.5-pixelPos.z;\n"
        "          			rendered=1;\n"
    	"				}\n"
    	"			}break;\n"
    	"			case CONE : {\n"
    	"				if(pixelPos.y<0.5 && pixelPos.y>-0.5) {\n"
    	"					float xx = (pixelPos.x)*(pixelPos.x);\n"
    	"					float yy = (pixelPos.y-0.5)*(pixelPos.y-0.5);\n"
    	"					float zz = (pixelPos.z)*(pixelPos.z);\n"
    	"					if((xx+zz)/yy<0.25){ \n"
    	"						float ax = sqrt(0.25*yy-zz);\n"
    	"						diffMin.x = pixelPos.x+ax;\n"
    	"						diffMax.x = ax-pixelPos.x;\n"
    	"						float ay = sqrt((yy+xx)/0.25);\n"
    	"						diffMin.y = pixelPos.y+0.5;\n"
    	"						diffMax.y = 0.5-pixelPos.y;\n"
    	"						float az = sqrt(0.25*yy-xx);\n"
    	"						diffMin.z = pixelPos.z+az;\n"
    	"						diffMax.z = az-pixelPos.z;\n"
        "          				rendered=1;\n"
    	"					}\n"
    	"				}\n"
    	"			} break;\n"
    	"			case TUBE : {\n"
    	"				if(length(vec2(pixelPos.x, pixelPos.z))<=0.5\n"
    	"						&& (pixelPos.y>-0.5 && pixelPos.y<0.5)) { \n"
    	"					float xx = (pixelPos.x)*(pixelPos.x);\n"
    	"					float zz = (pixelPos.z)*(pixelPos.z);\n"
    	"					float ax = sqrt(0.25-zz);\n"
    	"					diffMin.x = pixelPos.x+ax;\n"
    	"					diffMax.x = ax-pixelPos.x;\n"
    	"					float az = sqrt(0.25-xx);\n"
    	"					diffMin.z = pixelPos.z+az;\n"
    	"					diffMax.z = az-pixelPos.z;\n"
    	"					diffMin.y = pixelPos.y+0.5;\n"
    	"					diffMax.y = 0.5-pixelPos.y;\n"
        "          			rendered=1;\n"
    	"				}\n"
    	"			} break;\n"
    	"			case SPHERE : {\n"
    	"				if(length(pixelPos)<=0.5) { \n"
    	"					float xx = (pixelPos.x)*(pixelPos.x);\n"
    	"					float yy = (pixelPos.y)*(pixelPos.y);\n"
    	"					float zz = (pixelPos.z)*(pixelPos.z);\n"
    	"					float ax = sqrt(0.25-yy-zz);\n"
    	"					diffMin.x = pixelPos.x+ax;\n"
    	"					diffMax.x = ax-pixelPos.x;\n"
    	"					float ay = sqrt(0.25-xx-zz);\n"
    	"					diffMin.y = pixelPos.y+ay;\n"
    	"					diffMax.y = ay-pixelPos.y;\n"
    	"					float az = sqrt(0.25-xx-yy);\n"
    	"					diffMin.z = pixelPos.z+az;\n"
    	"					diffMax.z = az-pixelPos.z;\n"
        "          			rendered=1;\n"
    	"				}\n"
    	"			} break;\n"
    	"			case BOX : {\n"
    	"				diffMin = pixelPos - vec3(-0.5, -0.5, -0.5);\n"
    	"				diffMax = vec3(0.5, 0.5, 0.5) - pixelPos;\n"
        "       		if(diffMin.x>0 && diffMax.x>0\n"
    	"						&& diffMin.y>0 && diffMax.y>0\n"
        "          				&& diffMin.z>0 && diffMax.z>0){\n"
    	"					rendered=1;\n" 
    	"				} \n"
    	"			} break;\n"
    	"			case FRAME : {\n"
    	"				diffMin = pixelPos - vec3(-0.5, -0.5, -0.5);\n"
    	"				diffMax = vec3(0.5, 0.5, 0.5) - pixelPos;\n"
        "       		if(diffMin.x>0 && diffMax.x>0\n"
    	"						&& diffMin.y>0 && diffMax.y>0\n"
        "          				&& diffMin.z>0 && diffMax.z>0){\n"
    	"					rendered=1;\n" 
    	"				} \n"
    	"			} break;\n"
    	"			case MODEL : {\n"
        "   			ivec2 selSize = textureSize(selectTex, 0);\n"
        "   			vec4 sel = texelFetch(selectTex,\n"
        "                         ivec2(coordX*float(selSize.x), \n"
        "                               coordY*float(selSize.y)), \n"
        "                         0);\n"
            			//get front surfaces before depth map
        "   			if(pixZ<=cutZ+thickness/1000.0) { \n"
    	"					if(sel.z==0) {\n"
    	"						if(sel.x==shapeID) {\n"
    	"							rendered=1;\n" 
    	"						}\n"
    	"						else if((int(shapeIDBit)&int(sel.w))>0) {\n"
    	"							diffMin = pixelPos - vec3(-0.5, -0.5, -0.5);\n"
    	"							diffMax = vec3(0.5, 0.5, 0.5) - pixelPos;\n"
        "       					if(diffMin.x>0 && diffMax.x>0\n"
    	"									&& diffMin.y>0 && diffMax.y>0\n"
        "          							&& diffMin.z>0 && diffMax.z>0){\n"
    	"								rendered=1;\n" 
    	"							}\n"
    	"						}\n"
    	"					}\n"
        "       			if(surface>0 \n"
        "               		&& (pixZ>cutZ \n"
        "                      || (sel.z>0 && (int(shapeIDBit)&int(sel.w))>0))) {\n"
    	"							rendered=2;\n"
    	"					}\n"
    	"				}\n"
    	"			} break;\n"
    	"		}\n"
    	"		if(rendered>0) {\n"
    	"			ratioWithinBox = (pixelPos)+0.5;\n"
    				//test if on the surface
    	"			if(diffMin.x*modelScale.x<thickness || \n"
    	"							diffMax.x*modelScale.x<thickness || \n"
    	"							diffMin.y*modelScale.y<thickness || \n"
    	"							diffMax.y*modelScale.y<thickness || \n"
    	"							diffMin.z*modelScale.z<thickness || \n"
    	"							diffMax.z*modelScale.z<thickness) { \n"
    	"					rendered=2;\n" 
    	"			} \n"
                        //extend ratiowithinbox to multiplesubshapes
                        //and get gradient ratio according to type
        "               float gradRatio = 0;\n"
        "               vec3 subAdd;\n"
        "               vec3 subDiv;\n"
        "               if(insideStructure==0) {\n"//from center
        "                   subAdd = vec3(subShapeID, subShapeID, subShapeID);\n"
        "                   subDiv = vec3(subShapesNb, subShapesNb, subShapesNb);\n"
        "                  ratioWithinBox.xyz=(ratioWithinBox.xyz+subAdd)/subDiv;\n"
        "                  	outRatioWithinBox.xyz=ratioWithinBox.xyz;\n"
        "                   gradRatio = length(ratioWithinBox.xyz\n"
        "                                     - vec3(0.5,0.5,0.5))/0.5;\n"
        "               }\n"
        "               else {\n"//from surface or along axes
        "                   subAdd = vec3(0,0, subShapeID);\n"
        "                   subDiv = vec3(1,1, subShapesNb);\n"
        "                  	ratioWithinBox.xyz=(ratioWithinBox.xyz+subAdd)/subDiv;\n"
        "                  	outRatioWithinBox.xyz=ratioWithinBox.xyz;\n"
    	"					if(insideStructure==1) {\n"
    	"						ratioWithinBox.x=0.5;\n"
    	"					}\n"
    	"					else if(insideStructure==2) {\n"
    	"						ratioWithinBox.y=0.5;\n"
    	"					}\n"
    	"					else if(insideStructure==3 && shapeGeom!=PATH) {\n"
    	"						ratioWithinBox.z=0.5;\n"
    	"					}\n"
    	"					switch(shapeGeom) {\n"
    	"						case BOX : {\n"
    	"							vec3 q = abs(ratioWithinBox.xyz\n"
    	"										-vec3(0.5,0.5,0.5))*2.0\n" 
    	"									- vec3(1.0,1.0,1.0);\n"
      	"							gradRatio = 1.0-abs(length(max(q,0.0)) \n"
    	"									+ min(max(q.x,max(q.y,q.z)),0.0));\n"
    	"						} break;\n"
    	"						case SPHERE : {\n"
        "                   		gradRatio = length(ratioWithinBox.xyz\n"
        "                       	              - vec3(0.5,0.5,0.5))/0.5;\n"
    	"						} break;\n"
    	"						case PATH  : {\n"
    	"							vec2 d = abs(vec2(length((ratioWithinBox.xy-vec2(0.5, 0.5))*2.0), \n"
    	"												  (ratioWithinBox.z-0.5)*2.0)) \n"
    	"											- vec2(1.0,1.0);\n"
    	"						 	gradRatio = 1.0 - abs(min(max(d.x,d.y),0.0)\n"
    	"										+ length(max(d,0.0)));\n"
    	"						} break;\n"
    	"						case TUBE  : {\n"
    	"							vec2 d = abs(vec2(length((ratioWithinBox.xz-vec2(0.5, 0.5))*2.0), \n"
    	"												  (ratioWithinBox.y-0.5)*2.0)) \n"
    	"											- vec2(1.0,1.0);\n"
    	"						 	gradRatio = 1.0 - abs(min(max(d.x,d.y),0.0)\n"
    	"										+ length(max(d,0.0)));\n"
    	"						} break;\n"
    	"						case CONE : {\n" 
    	"							float q = length((ratioWithinBox.xz\n"
    	"											- vec2(0.5,0.5))*2.0);\n"
      	"							gradRatio = 1.0-abs(max(dot(vec2(0.5,0.5),\n"
    	"										vec2(q,(ratioWithinBox.y-1.0))),\n"
    	"										-1.0-(ratioWithinBox.y-1.0)));\n"
    	"						} break;\n"
    	"						case FRAME : {\n" //FIXME compute frame SDF
    	"							vec3 q = abs(ratioWithinBox.xyz\n"
    	"										-vec3(0.5,0.5,0.5))*2.0\n" 
    	"									- vec3(1.0,1.0,1.0);\n"
      	"							gradRatio = 1.0-abs(length(max(q,0.0)) \n"
    	"									+ min(max(q.x,max(q.y,q.z)),0.0));\n"
    	"						} break;\n"
    	"						case MODEL : {\n" 
        "                   		gradRatio = texture(surfDistTex, \n"
    	"                             					ratioWithinBox).r\n"
    	"										* structureRatio;\n"
    	"						} break;\n"
    	"						default :{\n"//defaults to sphere
        "                   		gradRatio = length(ratioWithinBox.xyz\n"
        "                       	              - vec3(0.5,0.5,0.5))/0.5;\n"
    	"						} break;\n"
        "               	}\n"
        "               }\n"
                        //texture coordinates
        "               vec3 texCoords = vec3(1-ratioWithinBox.x,\n"
        "                                     1-ratioWithinBox.y,\n"
        "                                     ratioWithinBox.z);\n"
    	"\n"
    					//start with shapeColor
    	"				finalColor = shapeColor;\n"
    					//test gradient
    	"				if(gradientAlpha>0) {\n"
    	"					float gradColor = \n"
    	"						pow(gradientCurveRatio<0?1.0-gradRatio:gradRatio,\n" 
    	"							abs(gradientCurveRatio));\n"
    	"					if(gradientSteps>1) {\n"
    	"						gradColor = floor(gradColor*gradientSteps)\n"
    	"										/ gradientSteps;\n"
    	"					}\n"
    	"					gradColor*=(gradRatio<=1.0)?1.0:0.0;\n"
    	"					if(gradientType>0) {\n"//gradient texture
    	"						finalColor=finalColor*(1.0-gradientAlpha) \n"
    	"								+ finalColor*gradientAlpha\n"
        "									* texture(gradientTex, \n"
        "										vec2(gradRatio, 0.0)).xyz;\n"
    	"					}\n"
    	"					else {\n"
    	"						finalColor=finalColor*(1.0-gradientAlpha) \n"
    	"								+ finalColor*gradColor*gradientAlpha;\n"
    	"					}\n"
    	"				}\n"
    					//test density
    	"				if(densityAlpha>0) {\n"
    	"					float densColor = \n"
    	"						pow(densityCurveRatio<0?1.0-gradRatio:gradRatio,\n" 
    	"							abs(densityCurveRatio));\n"
        "                   if(densityType==0 && \n" //layers
    	"							length(mod(densColor*100.0, \n"
    	"									   densitySize*100.0))\n"
    	"								< densityRatio*densitySize*100.0) {\n"
    	"						densColor = 1.0;\n"
    	"						densColor*=(gradRatio<=1.0)?1.0:0.0;\n"
        "					}\n" 
        "                   else if(densityType==1\n" //grid
    	"							&& (length(mod(ratioWithinBox.x*100,\n"
        "										densitySize*100.0))\n"
    	"								<densitySize*densityRatio*100.0 \n"
        "                   		|| length(mod(ratioWithinBox.y*100,\n"
        "										densitySize*100.0))\n"
    	"								<densitySize*densityRatio*100.0 \n"
        "                   		|| length(mod(ratioWithinBox.z*100,\n"
        "										densitySize*100.0))\n"
    	"								<densitySize*densityRatio*100.0)) { \n"
    	"							densColor = 1.0;\n"
        "                   }\n" 
    	"					else if(densityType==2  \n" //pointcloud
    	"						   && floor(random(ratioWithinBox.xyz)\n"
    	"										*floor(100.0))>100.0*densColor) {\n"
    	"							densColor = 1.0; \n"
    	"						densColor*=(gradRatio<=1.0)?1.0:0.0;\n"
    	"					}\n"
    	"					else {\n"
    	"						densColor = 0.0;\n"
    	"					}\n"
    	"					finalColor=finalColor*(1.0-densityAlpha) \n"
    	"								+ finalColor*densColor*densityAlpha;\n"
    	"				}\n"
    					//test texture
    	"				if(texAlpha>0) {\n"
        "                   vec3 texColor = texture(insideTex, \n"
        "                             texOffset+texScale*texCoords).xyz;\n"
    	"					if(texGray>0) {\n"
    	"						texColor=vec3((texColor.r+texColor.g+texColor.b)\n"
    	"									/ 3.0);\n"
    	"					}\n"
    	"					finalColor=finalColor*(1.0-texAlpha) \n"
    	"								+ finalColor*texColor*texAlpha;\n"
    	"				}\n"
    #ifdef GL42
    					//if revealing something, output values
        "               if(sli.x>=1.0 && depthID<=depthOutNb) {\n"
                            //inside output
        "                   if(revInside>0 && rendered==1) {\n"
        "                       imageAtomicMax(outputTex, \n"
        "                               ivec2(mod(outOffset+revInside,outCols), \n"
        "                                 floor((outOffset+revInside))/outCols),\n"
        "                               1);\n"
        "                   }\n"
        "                   outRatioWithinBox.x=clamp(outRatioWithinBox.x, 0, 1);\n"
        "                   outRatioWithinBox.y=clamp(outRatioWithinBox.y, 0, 1);\n"
        "                   outRatioWithinBox.z=clamp(outRatioWithinBox.z, 0, 1);\n"
                            //compute min,max on the three axes
        "                   int xk=int(outRatioWithinBox.x*1000.0);\n"
        "                   int yk=int(outRatioWithinBox.y*1000.0);\n"
        "                   int zk=int(outRatioWithinBox.z*1000.0);\n"
                            //output min/max ratios
        "                   if(revCenter>0) {\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+0,outCols), \n"
        "                                floor((outOffset+revCenter+0)/outCols)),\n"
        "                           1000-xk);\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+1,outCols), \n"
        "                                floor((outOffset+revCenter+1)/outCols)),\n"
        "                          xk);\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+2,outCols), \n"
        "                                floor((outOffset+revCenter+2)/outCols)),\n"
        "                         1000-yk);\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+3,outCols), \n"
        "                                floor((outOffset+revCenter+3)/outCols)),\n"
        "                          yk);\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+4,outCols), \n"
        "                                floor((outOffset+revCenter+4)/outCols)),\n"
        "                          1000-zk);\n"
        "                       imageAtomicMax(outputTex, \n"
        "                          ivec2(mod(outOffset+revCenter+5,outCols), \n"
        "                                floor((outOffset+revCenter+5)/outCols)),\n"
        "                          zk);\n"
        "                   }\n"
    						//convert from rgb to hsv
    	"					vec3 hsv = rgb2hsv(finalColor);\n"
                            //Sum color channels
        "                   if(revColor>0) {\n"
                                //count nb revealed points
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revColor+0,outCols), \n"
        "                                floor((outOffset+revColor+0)/outCols)),\n"
        "                          1);\n"
                                //sum point h
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revColor+1,outCols), \n"
        "                                floor((outOffset+revColor+1)/outCols)),\n"
        "                          clamp(int(hsv.x*1000.0),0,1000));\n"
                                //sum s
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revColor+2,outCols), \n"
        "                                floor((outOffset+revColor+2)/outCols)),\n"
        "                          clamp(int(hsv.y*1000.0),0,1000));\n"
                                //sum v
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revColor+3,outCols), \n"
        "                                floor((outOffset+revColor+3)/outCols)),\n"
        "                          clamp(int(hsv.z*1000.0),0,1000));\n"
        "                   }\n"
    						//histo : accumulate on luminance bins
        "                   if(revHisto>0) {\n"
                                //count nb revealed points
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revHisto,outCols), \n"
        "                                floor((outOffset+revHisto)/outCols)),\n"
        "                          1);\n"
    	"						int bin = int(hsv.z*float(histoOutSize-1));\n"
        "                       imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(outOffset+revHisto+1+bin,outCols), \n"
        "                                floor((outOffset+revHisto+1+bin)/outCols)),\n"
        "                          1);\n"
    	"					}\n"
    
                            //2D grid of projected voxels (nbvox, 3D coords, lum)
        "                   if(revVoxels>0) {\n"
    							//compute pixel ratio within 2D bounding rect
    	"						vec2 pixRatio = (vec2(coordX, coordY)\n"
    	"											- boundingRect.xy) / \n"
    	"									   (boundingRect.zw-boundingRect.xy);\n"
    	"						pixRatio.x = clamp(pixRatio.x, 0.0, 1.0);\n"
    	"						pixRatio.y = clamp(pixRatio.y, 0.0, 1.0);\n"
    							//compute offset in voxel output
    	"						int voxOff = outOffset+revVoxels;\n"
    	"						voxOff+=5*(int(pixRatio.x*voxelOutSize)\n"
    	"							+ int((1.0-pixRatio.y)*voxelOutSize)\n"
    	"									*voxelOutSize);\n"
    							//accumulate points
    	"						imageAtomicAdd(outputTex,\n"
        "                          ivec2(mod(voxOff,outCols), \n"
        "                                floor((voxOff)/outCols)),\n"
    	"							1);\n"
    							//accumulate coords
    	"						imageAtomicAdd(outputTex, \n"
        "                          ivec2(mod(voxOff+1,outCols), \n"
        "                                floor((voxOff+1)/outCols)),\n"
        "                          clamp(int(outRatioWithinBox.x*1000.0),0,1000));\n"
    	"						imageAtomicAdd(outputTex, \n"
        "                          ivec2(mod(voxOff+2,outCols), \n"
        "                                floor((voxOff+2)/outCols)),\n"
        "                          clamp(int(outRatioWithinBox.y*1000.0),0,1000));\n"
    	"						imageAtomicAdd(outputTex, \n"
        "                          ivec2(mod(voxOff+3,outCols), \n"
        "                                floor((voxOff+3)/outCols)),\n"
        "                          clamp(int(outRatioWithinBox.z*1000.0),0,1000));\n"
    							//accumulate lum
    	"						imageAtomicAdd(outputTex, \n"
        "                          ivec2(mod(voxOff+4,outCols), \n"
        "                                floor((voxOff+4)/outCols)),\n"
        "                          clamp(int(hsv.z*1000.0),0,1000));\n"
        "                   }\n"
        "               }\n"
    #endif
        "       }\n"
    			//SURFACE test
        "       if(rendered>1) { \n"
    	"			finalColor = surfaceColor;\n"
    #ifdef GL42
        "           if(revSurface>0 && sli.x>=1.0 && depthID<=depthOutNb) {\n"
        "               imageAtomicMax(outputTex,\n"
        "                         ivec2(mod(outOffset+revSurface,outCols), \n"
        "                               floor((outOffset+revSurface)/outCols)),\n"
        "                         1);\n"
        "           }\n"
    #endif
        "       }\n"
        "   }\n"
    		//final test, output if rendered or not, output final color
        "   if(rendered>0 && length(finalColor)>0) { \n"
    #ifdef GL42
    	"		if(revSize>0) {\n"
        "   		imageAtomicOr(outputTex, \n"
        "           				ivec2(mod(outOffset,outCols), \n"
        "             					floor((outOffset))/outCols),\n"
        "           				rendered);\n"
    	"		}\n"
    #endif
        "       if((insideVisible>0 && rendered==1) \n"
    	"				|| (surface>0 && rendered==2)) {\n"
    	"			color = vec4(finalColor, 1.0);\n"//display
    	//"			color = vec4(finalColor, sli.x);\n"//display
    	/*
    	"			if(sli.x>0.5) {\n"
    	"				color = vec4(1.0, 0.0, 0.0, 1.0);\n"//display
    	"			}\n"
    	"			else {\n"
    	"				color = vec4(0.0, 1.0, 0.0, 1.0);\n"//display
    	"			}\n"
    	*/
    	"		}\n"
    	"		else {\n"
    	"			color = vec4(0.0, 0.0, 0.0, 1.0);\n"
    	"		}\n"
        //"       color = vec4((pixelPos+0.5)/2.0, 1.0);\n"//debug pix coords
        //"       color = vec4(cutZ*10.0, pixZ*10.0, 0.0, 1.0);\n"//debug slice texture
    	//"		  color = vec4(texture(surfDistTex, ratioWithinBox).r*10.0, 1.0,1.0,1.0);\n"
    	//"		  color = vec4(texture(insideTex, ratioWithinBox).r*10.0, 1.0,1.0,1.0);\n"
    	//"		  color = vec4(ratioWithinBox.xyz, 1.0);\n"
        "   }\n"
        "   else {\n" //DISCARD
    	//"		color = vec4(1.0,0.0,0.0,1.0);\n"
        "       discard;\n"
        "   }\n"
        "}\n"};
        m_programs[Reveal::RENDERPROG]
            = Reveal::getInstance()->createProgram(vertSource, fragSource);
        m_uniforms[Reveal::RENDERPROG] = map<Reveal::REVIL_UNIFORM, GLint>();
        m_uniforms[Reveal::RENDERPROG][Reveal::MIRRORED]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "mirrored");
        m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPROJMAT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "viewProjMat");
        m_uniforms[Reveal::RENDERPROG][Reveal::VIEWMAT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "viewMat");
        m_uniforms[Reveal::RENDERPROG][Reveal::MODELMAT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "modelMat");
        m_uniforms[Reveal::RENDERPROG][Reveal::INVMODELMAT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "invModelMat");
        m_uniforms[Reveal::RENDERPROG][Reveal::MODELSCALE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "modelScale");
        m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTWIDTH]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "viewportWidth");
        m_uniforms[Reveal::RENDERPROG][Reveal::VIEWPORTHEIGHT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG],"viewportHeight");
        m_uniforms[Reveal::RENDERPROG][Reveal::SLICETEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "sliceTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::SELECTTEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "selectTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::INSIDETEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "insideTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::COORDSTEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "coordsTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::TEXGRAY]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "texGray");
        m_uniforms[Reveal::RENDERPROG][Reveal::REACTTEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "reactTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::SURFDISTEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "surfDistTex");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::SHAPEID]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "shapeID");
        m_uniforms[Reveal::RENDERPROG][Reveal::SHAPEIDBIT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "shapeIDBit");
        m_uniforms[Reveal::RENDERPROG][Reveal::SHAPEGEOM]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "shapeGeom");
        m_uniforms[Reveal::RENDERPROG][Reveal::SHAPECOLOR]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "shapeColor");
        m_uniforms[Reveal::RENDERPROG][Reveal::SUBSHAPEID]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "subShapeID");
        m_uniforms[Reveal::RENDERPROG][Reveal::SUBSHAPESNB]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "subShapesNb");
        m_uniforms[Reveal::RENDERPROG][Reveal::SUBSHAPEINVMAT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG],"subShapeInvMat");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVEALEDBY]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revealedBy");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::SURFACE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "surface");
        m_uniforms[Reveal::RENDERPROG][Reveal::SURFACECOLOR]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "surfaceColor");
        m_uniforms[Reveal::RENDERPROG][Reveal::SURFACETHICKNESS]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "thickness");
        m_uniforms[Reveal::RENDERPROG][Reveal::SURFACETEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "surfaceTex");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::INSIDEVISIBLE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "insideVisible");
        m_uniforms[Reveal::RENDERPROG][Reveal::INSIDESTRUCT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "insideStructure");
        m_uniforms[Reveal::RENDERPROG][Reveal::STRUCTRATIO]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "structureRatio");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::GRADIENTALPHA]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "gradientAlpha");
        m_uniforms[Reveal::RENDERPROG][Reveal::GRADIENTTYPE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "gradientType");
        m_uniforms[Reveal::RENDERPROG][Reveal::GRADIENTSTEPS]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "gradientSteps");
        m_uniforms[Reveal::RENDERPROG][Reveal::GRADIENTCURVERATIO]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "gradientCurveRatio");
        m_uniforms[Reveal::RENDERPROG][Reveal::GRADIENTTEXTURE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "gradientTex");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::DENSITYALPHA]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "densityAlpha");
        m_uniforms[Reveal::RENDERPROG][Reveal::DENSITYTYPE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "densityType");
        m_uniforms[Reveal::RENDERPROG][Reveal::DENSITYRATIO]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "densityRatio");
        m_uniforms[Reveal::RENDERPROG][Reveal::DENSITYSIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "densitySize");
        m_uniforms[Reveal::RENDERPROG][Reveal::DENSITYCURVERATIO]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "densityCurveRatio");
    
        m_uniforms[Reveal::RENDERPROG][Reveal::TEXALPHA]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "texAlpha");
        m_uniforms[Reveal::RENDERPROG][Reveal::TEXOFFSET]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "texOffset");
        m_uniforms[Reveal::RENDERPROG][Reveal::TEXSCALE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "texScale");
        m_uniforms[Reveal::RENDERPROG][Reveal::REACTIVITY]
          = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "reactivity");
    
    
        m_uniforms[Reveal::RENDERPROG][Reveal::BBOXMIN]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "bboxMin");
        m_uniforms[Reveal::RENDERPROG][Reveal::BBOXSIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "bboxSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::BBOXLOCALSIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "bboxLocalSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::BBOXROT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "bboxRot");
    #ifdef GL42
        //output texture
        m_outputImg = new unsigned int[m_outputImgSize*m_outputImgSize];
        m_outputImgInit = new unsigned int[m_outputImgSize*m_outputImgSize];
        memset(&m_outputImgInit[0], 0, m_outputImgSize*m_outputImgSize*4);
        glGenTextures(1, &m_outputTex);
        glBindTexture(GL_TEXTURE_2D, m_outputTex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI,
                     m_outputImgSize, m_outputImgSize, 0,
                     GL_RED_INTEGER, GL_UNSIGNED_INT, m_outputImgInit);
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTPUTTEX]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "outputTex");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVSIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVSURFACE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revSurface");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVINSIDE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revInside");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVCENTER]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revCenter");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVCURSOR]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revCursor");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVCOLOR]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revColor");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVHISTO]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revHisto");
        m_uniforms[Reveal::RENDERPROG][Reveal::REVVOXELS]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "revVoxels");
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTSHAPESIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "shapeOutSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTHISTOSIZE]
         = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "histoOutSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTVOXELSIZE]
         = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "voxelOutSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTDEPTHSIZE]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "depthOutSize");
        m_uniforms[Reveal::RENDERPROG][Reveal::OUTNBDEPTH]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "depthOutNb");
        m_uniforms[Reveal::RENDERPROG][Reveal::BRECT]
            = glGetUniformLocation(m_programs[Reveal::RENDERPROG], "boundingRect");
        createRTT(m_renderTex, m_renderFBO, m_rttWidth, m_rttHeight, false);
    #endif
        }
    
       {
        //select prog
        const char * vertSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "in vec3 vertex;\n"
        " \n"
        "uniform mat4 modelMat;\n"
        "uniform mat4 viewMat;\n"
        "uniform mat4 viewProjMat;\n"
        "uniform float mirrored;\n"
        " \n"
        "void main(void) {\n"
        "   vec4 posWS = modelMat * vec4(vertex, 1.0);\n"
        "   if(mirrored<1) {\n"
        "       posWS.x = -posWS.x;\n"
        "   }\n"
        "   gl_Position = viewProjMat * posWS;\n"
        "}\n"};
    
        const char * fragSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
    #ifdef GL42
        "layout(binding=0) uniform sampler2D sliceTex;\n"
    #else
        "uniform sampler2D sliceTex;\n"
    #endif
        "uniform float viewportWidth;\n"
        "uniform float viewportHeight;\n"
        "uniform float shapeID;\n"
        "uniform float shapeIDBit;\n"
        "uniform float mirrored;\n"
        "uniform float thickness;\n"
        "uniform int surface;\n"
        " \n"
        "out vec4 color;\n"
        " \n"
        "void main(void) {\n"
        "   float coordX = (gl_FragCoord.x-0.5)/viewportWidth;\n"
        "   float coordY = (gl_FragCoord.y-0.5)/viewportHeight;\n"
        "   ivec2 sliSize = textureSize(sliceTex, 0);\n"
        "   vec4 sli = texelFetch(sliceTex,\n"
        "                         ivec2(coordX*float(sliSize.x),\n"
        "                               coordY*float(sliSize.y)),\n"
        "                         0);\n"
        "   float pixZ=abs(gl_FragCoord.z);\n"
        "   float cutZ=abs(sli.z);\n"
        //  keep all ids of back surfaces after depth map
        "   if(pixZ>cutZ && cutZ>0) {\n"
        "       if((mirrored<1 && !gl_FrontFacing)\n"
        "               || (mirrored>0 && gl_FrontFacing)) {\n"
        "           color = vec4(shapeID, 0, \n"
        "                        (abs(pixZ-cutZ)<thickness/1000.0 && surface>0)?1:0, \n"
        "                        shapeIDBit);\n"
        "       }\n"
        "       else {\n" //occlude back when front face also behind
        "           color = vec4(-shapeID, 0, 0, -shapeIDBit);\n"
        "       }\n"
        "   }\n"
        "   else {\n"
        "       discard;\n"
        "   }\n"
        "}\n"};
        m_programs[Reveal::SELECTPROG]
            = Reveal::getInstance()->createProgram(vertSource, fragSource);
        m_uniforms[Reveal::SELECTPROG]=map<Reveal::REVIL_UNIFORM, GLint>();
        m_uniforms[Reveal::SELECTPROG][Reveal::MIRRORED]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "mirrored");
        m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPROJMAT]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "viewProjMat");
        m_uniforms[Reveal::SELECTPROG][Reveal::VIEWMAT]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "viewMat");
        m_uniforms[Reveal::SELECTPROG][Reveal::MODELMAT]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "modelMat");
        m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPORTWIDTH]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "viewportWidth");
        m_uniforms[Reveal::SELECTPROG][Reveal::VIEWPORTHEIGHT]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG],"viewportHeight");
        m_uniforms[Reveal::SELECTPROG][Reveal::SLICETEX]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "sliceTex");
        m_uniforms[Reveal::SELECTPROG][Reveal::SHAPEID]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "shapeID");
        m_uniforms[Reveal::SELECTPROG][Reveal::SHAPEIDBIT]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "shapeIDBit");
        m_uniforms[Reveal::SELECTPROG][Reveal::SURFACETHICKNESS]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "thickness");
        m_uniforms[Reveal::SELECTPROG][Reveal::SURFACE]
            = glGetUniformLocation(m_programs[Reveal::SELECTPROG], "surface");
        createRTT(m_selectTex, m_selectFBO, m_rttWidth, m_rttHeight, false);
        }
    
        //SLICE
        {
        const char * vertSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "#define s2(a, b) temp = a; a = min(a, b); b = max(temp, b);\n"
        "#define mn3(a, b, c)  s2(a, b); s2(a, c);\n"
        "#define mx3(a, b, c)  s2(b, c); s2(a, c);\n"
        "#define mnmx3(a, b, c) mx3(a, b, c); s2(a, b);\n"
        "#define mnmx4(a, b, c, d) s2(a, b); s2(c, d); s2(a, c); s2(b, d);\n"
        "#define mnmx5(a, b, c, d, e) s2(a,b); s2(c,d);mn3(a,c,e);mx3(b,d,e);\n"
       "#define mnmx6(a,b,c,d,e,f) s2(a,d);s2(b,e);s2(c,f);mn3(a,b,c);mx3(d,e,f);\n"
        "in vec3 vertex; \n"
        " \n"
    #ifdef GL42
        "layout(binding=0) uniform sampler2D depthCamTex;\n"
        "layout(binding=1) uniform sampler2D depthCamTexFil;\n"
        "layout(binding=2) uniform sampler2D markersTex;\n"
        //"layout(r32ui, binding=2) uniform coherent uimage2D outputTex;\n"
    #else
        "uniform sampler2D depthCamTex;\n"
        "uniform sampler2D depthCamTexFil;\n"
        "uniform sampler2D markersTex;\n"
    #endif
        "uniform mat4 modelMat;\n"
        "uniform mat4 viewMat;\n"
        "uniform mat4 viewProjMat;\n"
        "uniform float camXReso;\n"
        "uniform float camYReso;\n"
        "uniform float camXZFactor;\n"
        "uniform float camYZFactor;\n"
        "uniform int camFilter;\n"
        "uniform int background;\n"
        "uniform float camContThresh;\n"
        "uniform float camMarkers;\n"
        "uniform int depthType;\n"
        "uniform int depthID;\n"
        "uniform float outputCurs;\n"
        "uniform float mirrored;\n"
        " \n"
        "out vec3 viewRay;\n"
        "out float kept;\n"
        "flat out int outDepthID;\n"
        " \n"
        "void main(void) {\n"
        "   vec4 depthPos = vec4(0, 0, 0, 1);\n"
        "   vec4 posWS = vec4(0, 0, 0, 1);\n"
            //depth camera
        "   if(depthType==0) {\n"
        "	    vec2 coords = vertex.xy;\n"
                //get marker if any
        "       if(camMarkers>0) {\n"
        "           outDepthID = int(float(texture(markersTex,\n"
        "                                  vec2(coords.x/camXReso,\n"
        "                                       coords.y/camYReso)).r)\n"
        "                           * 255.0);\n"
        "       }\n"
        "       else {\n"
        "           outDepthID=depthID+1;\n"
        "       }\n"
                //get depth pixel in camera texture
        "       float depth = \n"
        "                     float(texture(depthCamTex,\n"
        "                                   vec2(coords.x/camXReso,\n"
        "                                        coords.y/camYReso)).r)\n"
        "                     * 65535.0;\n"
        "       kept=1.0;\n"
        "       float backgroundDepth=0;\n"
        "       if(background>0) {\n"
        "           float backDepth = float(texture(depthCamTex,\n"
        "                                   vec2(coords.x/camXReso,\n"
        "                                        coords.y/camYReso)).r)\n"
        "                                   * 65535.0;\n"
        "           if(backDepth<depth) {\n"
        "               depth=backDepth;\n"
        "               backgroundDepth=1;\n"
        "           }\n"
        "       }\n"
        "       if(depth<=500.0 || depth>8000.0) {\n"
        "           kept=0.0;\n"
        "       }\n"
        "       else if(backgroundDepth==0) {\n"
                    //filter out contours
        "           float maxD=camContThresh;\n" 
        "           bool contour=false;\n"
        "           for(int dX = -1; dX <= 1; ++dX) {\n"
        "               for(int dY = -1; dY <= 1; ++dY) {\n"
        "                   if(abs(float(texture(depthCamTex,\n"
        "                                   vec2((coords.x+dX)/camXReso,\n"
        "                                        (coords.y+dY)/camYReso)).r)\n"
        "                               * 65535.0\n"
        "                           - depth) > maxD) {\n"
        "                       contour=true;\n"
        "                   }\n"
        "               }\n"
        "           }\n"
        "           if(contour) {\n"
        "               kept=0.0;\n"
        "           }\n"
                    //see if needs filtering
        "           if(camFilter>0) {\n"
        "               float filtDepth = float(texture(depthCamTexFil,\n"
        "                                       vec2(coords.x/camXReso,\n"
        "                                        	 coords.y/camYReso)).r)\n"
        "                                   * 65535.0;\n"
                        //select either filtered version or current one
                        //depending on movement 
        "               if(abs(filtDepth-depth)<camContThresh) {\n"
        "                   float filRatio = abs(filtDepth-depth)/camContThresh;\n"
        "                   depth=(1-filRatio)*filtDepth+(filRatio)*depth;\n"
        "               }\n"
        "           }\n"
        "       }\n"
                //test distance again
        "       if(depth<=500.0 || depth>8000.0) {\n"
        "           kept=0.0;\n"
        "       }\n"
                //compute position in world units
        "       depthPos = vec4((vertex.x/camXReso-0.5)\n"
        "                           *depth*camXZFactor,\n"
        "                        (0.5-(vertex.y/camYReso))\n"
        "                           *depth*camYZFactor,\n"
        "                        depth,\n"
        "                        1.0);\n"
                //get world pos
        "       posWS = modelMat * depthPos;\n"
        "   }\n"
            //other slicing shapes
        "   else {\n"
        "       outDepthID=depthID+1;\n"
        "       kept=1.0;\n"
        "       depthPos=vec4(vertex, 1.0);\n"
        "       posWS = modelMat * depthPos;\n"
        "       if(mirrored<1) {\n"
        "           posWS.x = -posWS.x;\n"
        "       }\n"
        "   }\n"
    #ifdef GL42
    /*
            //output minimum point on z axis if output is active
        "   if(outputCurs>0 && kept>0.0) {\n"
        "       imageAtomicMin(outputTex,ivec2(0,int(depthID)+500),\n"
        "                                  int(floor(1000.0+posWS.z/10.0)*1000000\n"
        "                                      +floor(posWS.y/10.0+500.0)*1000\n"
        "                                      +floor(posWS.x/10.0+500.0)));\n"
        //"       imageAtomicMin(outputTex,ivec2(0, int(depthID)+500),4400600);\n"
        //"       imageAtomicMin(outputTex,ivec2(0, int(depthID)+500),6400600);\n"
        "   }\n"
    */
    #endif
            //keep/interpolate ray from view to each vertex
        "   viewRay = (posWS.xyz - vec3(viewMat[3][0],\n"
        "                               viewMat[3][1],\n"
        "                               viewMat[3][2]));\n"
        "   gl_Position = viewProjMat * posWS;\n"
        "}\n"};
        const char * fragSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "in float kept;\n"
        "in vec3 viewRay;\n"
        "flat in int outDepthID;\n"
        " \n"
        "out vec4 color;\n"
        " \n"
        "void main(void) {\n"
        "   float thresh=0.8;\n"
        "   if(kept>=thresh) {\n"
        "       color = vec4((kept-thresh)/(1.0-thresh),\n"
        "                     float(outDepthID),\n"
        "                     gl_FragCoord.z,\n"
        "                     length(viewRay));\n"
        "   }\n"
        "   else {\n"
        "       discard;\n"
        "   }\n"
    //    "   color = vec4(1.0, 1.0, 0.0, 1.0);\n"
        "}\n"};
    
        m_programs[Reveal::SLICEPROG]
            = Reveal::getInstance()->createProgram(vertSource, fragSource);
        m_uniforms[Reveal::SLICEPROG]=map<Reveal::REVIL_UNIFORM, GLint>();
        m_uniforms[Reveal::SLICEPROG][Reveal::BACKGROUND]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "background");
        m_uniforms[Reveal::SLICEPROG][Reveal::MIRRORED]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "mirrored");
        m_uniforms[Reveal::SLICEPROG][Reveal::VIEWPROJMAT]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "viewProjMat");
        m_uniforms[Reveal::SLICEPROG][Reveal::VIEWMAT]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "viewMat");
        m_uniforms[Reveal::SLICEPROG][Reveal::MODELMAT]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "modelMat");
        m_uniforms[Reveal::SLICEPROG][Reveal::DEPTHTYPE]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "depthType");
        m_uniforms[Reveal::SLICEPROG][Reveal::DEPTHID]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "depthID");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMXRESO]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camXReso");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMYRESO]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camYReso");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMXZFACTOR]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camXZFactor");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMYZFACTOR]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camYZFactor");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMTEX]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "depthCamTex");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMTEXFIL]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "depthCamTexFil");
        m_uniforms[Reveal::SLICEPROG][Reveal::MARKTEX]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "markersTex");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMFILTER]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camFilter");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMCONTOURTHRESH]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camContThresh");
        m_uniforms[Reveal::SLICEPROG][Reveal::CAMMARKERS]
            = glGetUniformLocation(m_programs[Reveal::SLICEPROG], "camMarkers");
        createRTT(m_sliceRttTex, m_sliceFBO, m_rttWidth, m_rttHeight, false);
        }
    
        //Calibration
        {
        const char * vertSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "in vec3 vertex; \n"
        " \n"
        "void main(void) {\n"
        "   gl_Position = vec4(vertex, 1.0);\n"
        "}\n"};
    
        const char * fragSource = {
    #ifdef GL42
        "#version 430 \n"
    #else
        "#version 410 \n"
    #endif
        " \n"
        "uniform float viewportWidth;\n"
        "uniform float viewportHeight;\n"
        "uniform float patW;\n"
        "uniform float patPosX;\n"
        "uniform float patPosY;\n"
        "uniform float patNbX;\n"
        "uniform float patNbY;\n"
        "uniform float patBright;\n"
        "uniform vec2 patOffset;\n"
        "uniform vec2 patRange;\n"
    #ifdef GL42
        "layout(binding=0) uniform sampler2D patternTex;\n"
    #else
        "uniform sampler2D patternTex;\n"
    #endif
        " \n"
        "out vec4 color;\n"
        " \n"
        "void main(void) {\n"
        /*
        "   float coordX = (gl_FragCoord.x-0.5)/viewportWidth;\n"
        "   float coordY = (gl_FragCoord.y-0.5)/viewportHeight;\n"
        "   ivec2 patSize = textureSize(patternTex, 0);\n"
        "   float pat = texelFetch(patternTex,\n"
        "                          ivec2(coordX*float(patSize.x),\n"
        "                                (1.0-coordY)*float(patSize.y)),\n"
        "                          0).r;\n"
        "   color=vec4(pat, pat, pat, 1.0);\n"
        //"   color=vec4(1.0,1.0,1.0,1.0);\n"
        */
        //pixel is white by default
        "   color = vec4(patBright);\n"
        "   float offX = gl_FragCoord.x - patPosX;\n"
        "   float offY = (viewportHeight-gl_FragCoord.y) - patPosY;\n"
        //black squares
        "   if(offX>=0 && offX<patW*(patNbX+1)\n"
        "           && offY>=0 && offY<patW*(patNbY+1)) {\n"
        "       if((mod(offX,patW*2)<patW && mod(offY,patW*2)<patW) \n"
        "          || (mod(offX,patW*2)>patW && mod(offY,patW*2)>patW)) {\n"
        "           color = vec4(0, 0, 0, 1);\n"
        "       }\n"
        "   }\n"
        //color in red the pattern range border
        "   float thick=2;\n"
        "   float coordY = viewportHeight-gl_FragCoord.y;\n"
        "   if(((abs(gl_FragCoord.x-patOffset.x)<thick\n"
        "               || abs(gl_FragCoord.x-(patOffset.x+patRange.x))<thick)\n"
        "           && coordY>patOffset.y\n" 
        "           && coordY<(patOffset.y+patRange.y))\n"
        "       ||((abs(coordY-patOffset.y)<thick\n"
        "               || abs(coordY-(patOffset.y+patRange.y))<thick)\n"
        "           && gl_FragCoord.x>patOffset.x\n" 
        "           && gl_FragCoord.x<(patOffset.x+patRange.x))) {\n"
        "       color=vec4(1,0,0,1);\n"
        "   }\n"
        "}\n"};
        m_programs[Reveal::CALIBPROG]
            = Reveal::getInstance()->createProgram(vertSource, fragSource);
        m_uniforms[Reveal::CALIBPROG]=map<Reveal::REVIL_UNIFORM, GLint>();
        m_uniforms[Reveal::CALIBPROG][Reveal::VIEWPORTWIDTH]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"viewportWidth");
        m_uniforms[Reveal::CALIBPROG][Reveal::VIEWPORTHEIGHT]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"viewportHeight");
        
        /*
        m_uniforms[Reveal::CALIBPROG][Reveal::PATTERNTEX]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patternTex");
        */
        
        
        m_uniforms[Reveal::CALIBPROG][Reveal::PATW]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patW");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATPOSX]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patPosX");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATPOSY]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patPosY");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATNBX]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patNbX");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATNBY]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patNbY");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATRANGE]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patRange");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATOFFSET]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patOffset");
        m_uniforms[Reveal::CALIBPROG][Reveal::PATBRIGHT]
            = glGetUniformLocation(m_programs[Reveal::CALIBPROG],"patBright");
            
            
        m_calibGeom = new QuadGeometry();
        }
    }
    
    void ProjectorModule::createRTT(GLuint& tex, GLuint& fbo, 
                                    const int& width, const int& height,
                                    bool filter) {
        glGenTextures(1, &tex);
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height,
                     0, GL_RGBA, GL_FLOAT, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
                        filter?GL_LINEAR:GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                        filter?GL_LINEAR:GL_NEAREST);
        glGenFramebuffers(1, &fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
        GLenum drawBuffers[1] = {GL_COLOR_ATTACHMENT0};
        glDrawBuffers(1, drawBuffers);
        GLuint idDepthrenderbuffer;
        glGenRenderbuffers(1, &idDepthrenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, idDepthrenderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
                              width, height);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                  GL_RENDERBUFFER, idDepthrenderbuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    
    void ProjectorModule::deleteModule() {
        if(m_space) {
            m_space->removeProj(this);
        }
        glfwDestroyWindow(m_projWindow);
        Module::deleteModule();
    }
    
    void ProjectorModule::setSpace(SpaceModule* space) {
        m_space=space;
    }
    
    void ProjectorModule::refreshDepthCamList(const vector<DepthCamModule*> cams) {
        vector<string> labels;
        labels.push_back("none");
        vector<DepthCamModule*>::const_iterator itCam=cams.begin();
        for(; itCam!=cams.end(); ++itCam) {
            labels.push_back((*itCam)->getName());
        }
        m_attributesMap["attached_to_camera"]
                ->editStringValuesChoices().assign(1, labels);
        m_attributesMap["calibrate_with_camera"]
                ->editStringValuesChoices().assign(1, labels);
    }
    
    void ProjectorModule::attachToCam(const string& camName) {
        if(m_space) {
            if(m_attachedToCam) {
                m_attachedToCam->detachProj(this);
            }
            m_attachedToCam = m_space->getDepthCamModule(camName);
            if(m_attachedToCam) {
                m_attachedToCam->attachProj(this);
            }
        }
    }
    
    void ProjectorModule::setWindowDimensions(const int& x, const int& y,
                                              const int& w, const int& h) {
        glfwSetWindowPos(m_projWindow, x, y);
        glfwSetWindowSize(m_projWindow, w, h);
        m_width=w;
        m_height=h;
    }
    
    void ProjectorModule::setWindowDecoration(bool d) {
        //m_window->setWindowDecoration(d);
    }
    
    void ProjectorModule::fullscreenMonitor(const std::string& monitorName) {
        int count;
        GLFWmonitor** monitors = glfwGetMonitors(&count);
    
        GLFWmonitor* monitor  = NULL;
        for(int m=0; m<count; ++m) {
            if(monitorName.compare(glfwGetMonitorName(monitors[m]))==0) {
                monitor=monitors[m];
            }
        }
    
        int posX=0;
        int posY=0;
        m_width=640;
        m_height=480;
        if(monitor) {
            const GLFWvidmode* mode = glfwGetVideoMode(monitor);
            m_width=mode->width;
            m_height=mode->height;
            glfwGetMonitorPos(monitor, &posX, &posY);
        }
        m_posX=posX;
        m_posY=posY;
        glfwSetWindowMonitor(m_projWindow, monitor, 0, 0,
                             m_width, m_height, GLFW_DONT_CARE);
        updateWindowDims();
        refreshMonitors();
    }
    
    void ProjectorModule::refreshMonitors(){
        int count;
        GLFWmonitor** monitors = glfwGetMonitors(&count);
        vector<string> monitorNames;
        monitorNames.push_back("windowed");
        for(int m=0; m<count; ++m) {
            monitorNames.push_back(glfwGetMonitorName(monitors[m]));
        }
        m_attributesMap["fullscreen_monitor"]
            ->editStringValuesChoices().assign(count, monitorNames);
    }
    
    void ProjectorModule::setViewMatrix(const vector<float>& vals) {
        m_viewMat = mat4(vals[0], vals[1], vals[2], vals[3],
                         vals[4], vals[5], vals[6], vals[7],
                         vals[8], vals[9], vals[10], vals[11],
                         vals[12], vals[13], vals[14], vals[15]);
        updateViewProjMatrix();
    }
    
    void ProjectorModule::setProjectionMatrix(const vector<float>& vals) {
        m_projMat = mat4(vals[0], vals[1], vals[2], vals[3],
                         vals[4], vals[5], vals[6], vals[7],
                         vals[8], vals[9], vals[10], vals[11],
                         vals[12], vals[13], vals[14], vals[15]);
        updateViewProjMatrix();
    }
    
    void ProjectorModule::updateModelMatrix() {
        if(!m_attachedToCam) {
            Module::updateModelMatrix();
        }
        updateViewProjMatrix();
    }
    
    void ProjectorModule::setModelMat(const glm::mat4& mat) {
        Module::setModelMat(mat);
        updateViewProjMatrix();
    }
    
    void ProjectorModule::updateViewProjMatrix() {
        m_transViewMat = m_modelMat * inverse(m_viewMat);
        m_viewProjMat = m_projMat * inverse(m_transViewMat);
    }
    
    void ProjectorModule::setModuleName(const string& name) {
        Module::setModuleName(name);
        glfwSetWindowTitle(m_projWindow, name.c_str());
        if(m_space) {
            m_space->refreshProjList();
        }
    }
    
    void ProjectorModule::outputRevealed(bool output) {
        m_processOutput=output;
    }
    
    void ProjectorModule::postFilter(const int& filt) {
        m_postFilter=filt;
    }
    
    void ProjectorModule::setTransparentWindow(const bool& transp) {
        m_transparentBackground=transp;
    }
    
    void ProjectorModule::processOutput() {
        bool outputProcessed=false;
    
        //retrieve active revealed modules
        vector<RevealedModule*>& revModules
            = Reveal::getInstance()->editRegisteredOutputRevealed();
        vector<DepthCamModule*>& camModules
            = Reveal::getInstance()->editRegisteredOutputDepthCams();
    
        if(revModules.size()>0 || camModules.size()>0) {
            //retrieve output image
            glBindTexture(GL_TEXTURE_2D, m_outputTex);
            glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, 
                          GL_UNSIGNED_BYTE,
                          m_outputImg);
        }
    
    
        //process shape revealing output
        if(revModules.size()>0) {
    		m_outNbDepth = m_space->getNbDepthModulesFromID();
            //get output values for revealed modules with active output
            vector<RevealedModule*>::iterator itSh = revModules.begin();
            for(; itSh!=revModules.end(); ++itSh) {
    			//test if shape is visible from this projector
    			if((*itSh)->getVisibilityMask() & m_visibilityMask) {
    				for(int d=1; d<m_outNbDepth+1; ++d) {
    					DepthModule* mod = m_space->getDepthModuleFromID(d);
    					if(mod) {
    						if(mod->isDepthVisible(d)) {
    							(*itSh)->processReveal(d, 
    												   mod->getName(), 
    												   m_outNbDepth,
    												   m_outputImg);
    							outputProcessed=true;
    						}
    					}
    				}
    			}
            }
        }
    
        /*
        //process cam
        if(camModules.size()>0) {
            unsigned int curs;
            vector<float> pos(3,0);
            vector<DepthCamModule*>::iterator itCa = camModules.begin();
            for(; itCa!=camModules.end(); ++itCa) {
                extractOutputValue((*itCa)->getDepthID()+500, 0, curs);
                pos[0] = (int(curs) - int(curs/1000)*1000)*10-5000;
                pos[1] = (int(curs/1000) - int(curs/1000000)*1000)*10-5000;
                pos[2] = 10000-int(curs/1000000)*10;
                //cout<<"got cursor "<<curs<<" from cam at pos "
                //    <<pos[0]<<" "<<pos[1]<<" "<<pos[2]<<endl;
                (*itCa)->processCursor(pos);
            }
            outputProcessed=true;
        }
        */
    
        if(outputProcessed) {
            //reset output image to 0
            glBindTexture(GL_TEXTURE_2D, m_outputTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
                         m_outputImgSize, m_outputImgSize, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, m_outputImgInit);
        }
    }
    
    void ProjectorModule::updateWindowSize(const int& w, const int& h) {
        m_height=h;
        m_width=w;
        updateWindowDims();
    }
    
    void ProjectorModule::updateWindowPos(const int& x, const int& y) {
        m_posX=x;
        m_posY=y;
        updateWindowDims();
    }
    
    void ProjectorModule::updateWindowDims() {
        vector<int> dims(4,0);
        dims[0]=m_posX;
        dims[1]=m_posY;
        dims[2]=m_width;
        dims[3]=m_height;
        m_attributesMap["window_dimensions"]->initInts(dims);
        m_patternPosRange = Size(m_width, m_height);
    }
    
    //-------------------------Calibration----------------------------------
    
    /*
    static void cbPatternSquareWidth(int w, void* mod) {
        if(w>10) {
            ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
            proj->setPatternSquareWidth(w);
        }
    }*/
    
    void ProjectorModule::setPatternSquareWidth(const int& w) {
        m_patternSquareWidth=w;
        updateCalibrationPattern();
    }
    
    static void cbPatternPosOX(int x, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setPatternPosOX(x);
    }
    
    void ProjectorModule::setPatternPosOX(const int& x) {
        m_patternPosOffset.width=x;
        updateCalibrationPattern();
    }
    
    static void cbPatternPosOY(int y, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setPatternPosOY(y);
    }
    
    void ProjectorModule::setPatternPosOY(const int& y) {
        m_patternPosOffset.height=y;
        updateCalibrationPattern();
    }
    
    static void cbPatternPosRX(int x, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setPatternPosRX(x);
    }
    
    void ProjectorModule::setPatternPosRX(const int& x) {
        m_patternPosRange.width=x;
        updateCalibrationPattern();
    }
    
    static void cbPatternPosRY(int y, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setPatternPosRY(y);
    }
    
    void ProjectorModule::setPatternPosRY(const int& y) {
        m_patternPosRange.height=y;
        updateCalibrationPattern();
    }
    
    static void cbCalib(int c, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setCalib(c);
    }
    
    void ProjectorModule::setCalib(const int& c) {
        if(m_calibrating==1 && c==0) {
            m_calibrationStopped=true;
        }
        m_calibrating=c;
    }
    
    static void cbPatternBrightness(int b, void* mod) {
        ProjectorModule* proj = static_cast<ProjectorModule*>(mod);
        proj->setPatternBrightness(b);
    }
    
    void ProjectorModule::setPatternBrightness(const int& b) {
        m_patternBrightness=b;
    }
    
    void ProjectorModule::updateCalibrationPattern() {
        m_generatedCorners.clear();
        for(int j=1; j<m_patternSize.height+1; ++j) {
            for(int i=1; i<m_patternSize.width+1; ++i) {
                int cornerX=m_patternSquareWidth*i+m_patternPosition.width;
                int cornerY=m_patternSquareWidth*j+m_patternPosition.height;
                m_generatedCorners.push_back(Point2f(cornerX, cornerY));
            }
        }
    }
    
    void ProjectorModule::calibrate(const std::string& camName) {
        //get depth camera module and activate color image
        DepthCamModule* cam = NULL;
        if(m_space) {
            cam = m_space->getDepthCamModule(camName);
        }
        if(cam==NULL) {
            cout<<"No camera attached to projector"<<endl;
            return;
        }
    
        //close cam and reopen in calibration mode
        cam->closeDevice();
        cam->openDevice(cam->getDeviceID(), true);
    
        //check cam is open
        if(!cam->isOpen()) {
            cout<<"Camera "<<cam->getName()<<"is not open"<<endl;
            return;
        }
    
        cout<<"Calibrating projector ..."<<endl;
    
        //create opencv windows, trackbars and initialize variables
        m_imageSize = cv::Size(640 ,480); //fixme, get from cam color image size
        m_calibrating=0;
        m_calibrationStopped=false;
        m_neededFramesNb=9;
        cv::Mat kinectRgbImg = Mat::zeros(m_imageSize, CV_8UC3);
        namedWindow("DepthCamRGB");
        namedWindow("Controls");
        createTrackbar("Pattern Offset X", "Controls",
                        &m_patternPosOffset.width, 1000, cbPatternPosOX, this);
        createTrackbar("Pattern Offset Y", "Controls",
                        &m_patternPosOffset.height, 500, cbPatternPosOY, this);
        createTrackbar("Pattern Range X", "Controls",
                        &m_patternPosRange.width, 1920, cbPatternPosRX, this);
        createTrackbar("Pattern Range Y", "Controls",
                        &m_patternPosRange.height, 1080, cbPatternPosRY, this);
        createTrackbar("Pattern Brightness", "Controls",
                        &m_patternBrightness, 100, cbPatternBrightness, this);
        createTrackbar("Calibrate (1:Start, 0:Cancel)", "Controls",
                        NULL, 1, cbCalib, this);
        vector<vector<Point3f> > objectPoints;
        vector<vector<Point2f> > imagePoints;
        Mat cameraMatrix = Mat::eye(3, 3, CV_64F);
        Mat distCoeffs = Mat::zeros(8, 1, CV_64F);
        vector<Mat> rvecs;
        vector<Mat> tvecs;
    
        cam->activateColor();
        for(int f=0; f<m_neededFramesNb && !m_calibrationStopped;) {
            //update pattern position
            m_patternSquareWidth = m_height/20;
    
            m_patternPosition.width = (m_patternPosRange.width-m_patternSquareWidth)
                                            / sqrt(m_neededFramesNb)
                                        *(fmod(f,sqrt(m_neededFramesNb)))
                                      +m_patternSquareWidth
                                      +m_patternPosOffset.width;
    
            m_patternPosition.height=(m_patternPosRange.height-m_patternSquareWidth)
                                            / sqrt(m_neededFramesNb)
                                        *(floor(f/sqrt(m_neededFramesNb)))
                                      +m_patternSquareWidth
                                      +m_patternPosOffset.height;
            updateCalibrationPattern();
    
            //display frame with calibration pattern
            glfwMakeContextCurrent(m_projWindow);
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glViewport(0, 0, m_width, m_height);
            glDisable(GL_CULL_FACE);
            glClearColor(0.0, 0.0, 0.0, 1.0);
            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
            glUseProgram(m_programs[Reveal::CALIBPROG]);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::VIEWPORTWIDTH],
                        m_width);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::VIEWPORTHEIGHT],
                        m_height);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATW],
                        m_patternSquareWidth);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATPOSX],
                        m_patternPosition.width);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATPOSY],
                        m_patternPosition.height);
            glUniform2f(m_uniforms[Reveal::CALIBPROG][Reveal::PATOFFSET],
                        m_patternPosOffset.width, m_patternPosOffset.height);
            glUniform2f(m_uniforms[Reveal::CALIBPROG][Reveal::PATRANGE],
                        m_patternPosRange.width, m_patternPosRange.height);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATNBX],
                        m_patternSize.width);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATNBY],
                        m_patternSize.height);
            glUniform1f(m_uniforms[Reveal::CALIBPROG][Reveal::PATBRIGHT],
                        float(m_patternBrightness)/100.0);
            m_calibGeom->drawDirect(m_contextHandlerID);
            glfwSwapBuffers(m_projWindow);
    
            waitKey(10);
            sleep(1);
    
            //grab frame
            if(cam->getFrames()) {
                openni::VideoFrameRef depthFrame = cam->getDepthFrame();
                openni::VideoFrameRef colorFrame = cam->getColorFrame();
    
                //copy to opencv mat
                for(int j=0; j<m_imageSize.height; ++j) {
                    for(int i=0; i<m_imageSize.width; ++i) {
                        cv::Vec3b& pix = kinectRgbImg.at<cv::Vec3b>(j,i);
                        int coord=0;
    					coord=j*m_imageSize.width+i;
                        openni::RGB888Pixel colorPix =
                            ((openni::RGB888Pixel*) colorFrame.getData())[coord];
                        pix[0]=colorPix.r;
                        pix[1]=colorPix.g;
                        pix[2]=colorPix.b;
                    }
                }
    
    
                imshow("DepthCamRGB", kinectRgbImg);
                if(m_calibrating) {
                    m_foundCorners.clear();
                    //find chessboard in image
                    bool patternFound = findChessboardCorners(
                                                      kinectRgbImg,
                                                      m_patternSize,
                                                      m_foundCorners,
                                                      CALIB_CB_ADAPTIVE_THRESH);
                    //if we found it
                    if(patternFound) {
                        //refine
                        Mat gray;
                        cvtColor(kinectRgbImg, gray, COLOR_RGB2GRAY);
                        cornerSubPix(gray, m_foundCorners,
                                 cv::Size(1, 1), cv::Size(-1, -1),
                                 TermCriteria(TermCriteria::EPS,
                                              30, 0.1));
    
    
                        bool validDepths=true;
                        //retrieve the corresponding position and depth
                        vector<Point3f> worldCorners;
                        vector<Point2f>::iterator itPnt=m_foundCorners.begin();
                        for(; itPnt!=m_foundCorners.end(); ++itPnt) {
                            float posX, posY, posZ;
    						Point2f coord = Point2f((*itPnt).x,(*itPnt).y);
    						Point2f depthCoord = coord;
    
                            openni::DepthPixel depthPix =
                                ((openni::DepthPixel*)depthFrame.getData())[int(
                                        depthCoord.y*m_imageSize.width +
                                        depthCoord.x)];
                            posZ=depthPix;
    
                            //filter the depth
                            int size=6;
                            vector<double> vals;
                            for(int x=depthCoord.x-size/2;
                                        x<depthCoord.x+size/2; ++x) {
                                for(int y=depthCoord.y-size/2;
                                        y<depthCoord.y+size/2; ++y) {
                                    if(x>=0 && x<m_imageSize.width &&
                                            y>=0 && y<m_imageSize.height) {
                                        vals.push_back(
                                        ((openni::DepthPixel*)depthFrame.getData())
                                            [int(y*m_imageSize.width + x)]);
                                    }
                                }
                            }
                            sort(vals.begin(), vals.end());
                            posZ=vals[vals.size()/2];
                            depthPix=posZ;
    
                            openni::CoordinateConverter::convertDepthToWorld(
                                                        cam->getDepthStream(),
                                                        int(depthCoord.x),
                                                        int(depthCoord.y),
                                                        depthPix,
                                                        &posX, &posY, &posZ);
                            //if(reversed) {
                            //    posX=-posX;
                            //    posY=-posY;
                            //}
                            if(posZ<=500.0 || posZ>4000.0) {
                                validDepths=false;
                            }
                            else {
                                worldCorners.push_back(Point3f(posX, posY, posZ));
                            }
                        }
    
                        //if everything is valid
                        if(validDepths) {
                            cout<<"Pattern found "<<f<<"/"<<m_neededFramesNb<<endl;
                            //add the generated corners
                            imagePoints.push_back(m_generatedCorners);
                            cout<<m_generatedCorners[0]<<endl;
                            //add the detected world corners
                            objectPoints.push_back(worldCorners);
                            cout<<worldCorners[0]<<endl;
    
                            //cout<<m_generatedCorners<<endl;
                            //cout<<worldCorners<<endl;
    
                            //draw corners
                            drawChessboardCorners(kinectRgbImg, m_patternSize,
                                                  m_foundCorners, patternFound);
    
    
                            imshow("DepthCamRGB", kinectRgbImg);
                            waitKey(10);
    
                            //wait
                            #ifdef POSIX
                                sleep(1);
                            #else
                                Sleep(1000);
                            #endif
    
                            //increase frame count
                            ++f;
                         }
                    }
                }
                waitKey(10);
            }
        }
    
        double rms=0;
    
        if(objectPoints.size()>0) {
    
            //put the image and object points in the right order for opencv
            vector<vector<Point3f> > vvo(1); //object points
            vector<vector<Point2f> > vvi(1); //image points
            for (unsigned int i=0; i<objectPoints.size(); ++i) {
                for (unsigned int j = 0; j<objectPoints[i].size(); j++) {
                    vvo[0].push_back(objectPoints[i][j]);
                    vvi[0].push_back(imagePoints[i][j]);
                }
            }
    
            //when enough points found, get the extrinsics/intrisics parameter
            float w = m_width;
            float h = m_height;
            cameraMatrix = (Mat1d(3, 3) <<  w, 0, w/2.,
                                            0, h, h / 2.,
                                            0, 0, 1);
            distCoeffs = Mat::zeros(8, 1, CV_64F);
            rms=calibrateCamera(vvo, vvi,
                                   Size(w, h), cameraMatrix,
                                   distCoeffs, rvecs, tvecs,
                                   CALIB_FIX_K1+
                                   CALIB_FIX_K2+
                                   CALIB_FIX_K3+
                                   CALIB_FIX_K4+
                                   CALIB_FIX_K5+
                                   CALIB_FIX_K6+
                                   CALIB_ZERO_TANGENT_DIST+
                                   CALIB_USE_INTRINSIC_GUESS);
    
            cout<<"... done , RMS="<<rms<<endl;
    
            //Build the opengl view matrix
            Mat rot;
            rvecs[0].copyTo(rot);
            Mat rotMat = Mat::zeros(3, 3, CV_64F);
            Rodrigues(rot, rotMat);
    
            mat4 viewMatrix(rotMat.at<double>(0,0), rotMat.at<double>(1,0),
                                rotMat.at<double>(2,0), 0,
                            rotMat.at<double>(0,1), rotMat.at<double>(1,1),
                                rotMat.at<double>(2,1), 0,
                            rotMat.at<double>(0,2), rotMat.at<double>(1,2),
                                rotMat.at<double>(2,2), 0,
                            tvecs[0].at<double>(0,0), tvecs[0].at<double>(1,0),
                                tvecs[0].at<double>(2,0), 1);
    
            mat4 glCoordsMat(-1, 0, 0, 0,
                              0, 1, 0, 0,
                              0, 0, 1, 0,
                              0, 0, 0, 1);
            m_viewMat=glCoordsMat*viewMatrix;
    
            m_attributesMap["view_matrix"]->setFloats(
                                                vector<float>(
                                                   value_ptr(m_viewMat),
                                                   value_ptr(m_viewMat)+16));
            //build the opengl projection matrix
            double fx=cameraMatrix.at<double>(0,0);
            double cx=cameraMatrix.at<double>(0,2);
            double fy=cameraMatrix.at<double>(1,1);
            double cy=cameraMatrix.at<double>(1,2);
            double l=0;
            double r=w;
            double b=h;
            double t=0;
            double n=100;
            double f=10000;
            mat4 projMatrix(
                    2.0*fx/w,       0,                      0,              0,
                    0,              2.0*fy/h,               0,              0,
                    1.0-2.0*(cx)/w,-1.0+(2.0*(cy)+2.0)/h,   (f+n)/(n-f),    -1,
                    0,              0,                      (2.0*f*n)/(n-f), 0);
            m_attributesMap["projection_matrix"]->setFloats(
                        vector<float>(value_ptr(projMatrix),
                                      value_ptr(projMatrix)+16));
            m_attributesMap["attached_to_camera"]
                ->setStrings(vector<string>(1, cam->getName()));
        }
    
        ostringstream oss;
        oss<<rms;
    
        cv::Mat reconstructionImg = Mat::zeros(Size(m_width, m_height), CV_8UC3);
    
        //project reconstructed kinect
        while(!m_calibrationStopped) {
            if(cam->getFrames()) {
                openni::VideoFrameRef depthFrame = cam->getDepthFrame();
                openni::VideoFrameRef colorFrame = cam->getColorFrame();
    
                //get points from the kinect and their actual 3D positions
                reconstructionImg.setTo(cv::Vec3b(0,0,0));
                vector<Point3f> objPnts;
                vector<cv::Vec3b> kinPnts;
                vector<Point2f> imgPnts;
                for(int j=0; j<m_imageSize.height; ++j) {
                    for(int i=0; i<m_imageSize.width; ++i) {
                        float posX, posY, posZ;
                        int coord = m_imageSize.width*j+i;
                        openni::DepthPixel depthPix =
                            ((openni::DepthPixel*)depthFrame.getData())[coord];
                        openni::CoordinateConverter::convertDepthToWorld(
                                                            cam->getDepthStream(),
                                                            i, j, depthPix,
                                                            &posX, &posY, &posZ);
                        objPnts.push_back(Point3f(posX,posY,posZ));
                        const openni::RGB888Pixel& colorPix =
                            ((openni::RGB888Pixel*) colorFrame.getData())[coord];
                        kinPnts.push_back(cv::Vec3b(colorPix.r,
                                                colorPix.g,
                                                colorPix.b));
                    }
                }
                projectPoints(objPnts, rvecs[0], tvecs[0],
                              cameraMatrix, distCoeffs, imgPnts);
    
                //for each of the image points
                vector<Point2f>::iterator itPnt=imgPnts.begin();
                vector<cv::Vec3b>::iterator itKin=kinPnts.begin();
                vector<Point3f>::iterator itObj=objPnts.begin();
                for(; itPnt!=imgPnts.end() && itKin!=kinPnts.end();
                        ++itPnt, ++itKin, ++itObj) {
                    if((*itPnt).x<reconstructionImg.cols &&
                            (*itPnt).y<reconstructionImg.rows &&
                            (*itPnt).x>0 &&
                            (*itPnt).y>0) {
                        reconstructionImg.at<cv::Vec3b>(
                                        (*itPnt).y,
                                        (*itPnt).x)
                            =(*itKin);
                    }
                }
    
                putText(reconstructionImg, "RMS="+oss.str(), Point(0,50), 
                        cv::FONT_HERSHEY_SIMPLEX, 2, 
                        Scalar(255, 255, 255));
                imshow( "DepthCamRGB", reconstructionImg);
            }
            waitKey(10);
        }
    
        //close opencv windows
        destroyWindow("DepthCamRGB");
        destroyWindow("Controls");
        waitKey(10);
    
        //reopen cam in normal mode
        cam->closeDevice();
        cam->openDevice(cam->getDeviceID());
    }
    
    void ProjectorModule::calibrateCamWithMarker(const std::string& camName) {
    
    
    
    }