/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/

#include <osgShadow/ShadowMap>
#include <osgShadow/ShadowedScene>
#include <osg/Notify>
#include <osg/ComputeBoundsVisitor>
#include <osg/PolygonOffset>
#include <osg/CullFace>
#include <osg/io_utils>

using namespace osgShadow;

#include <iostream>
//for debug
#include <osg/LightSource>
#include <osg/PolygonMode>
#include <osg/Geometry>
#include <osgDB/ReadFile>
#include <osgText/Text>

#define IMPROVE_TEXGEN_PRECISION 1

//////////////////////////////////////////////////////////////////
// fragment shader
//
static const char fragmentShaderSource_noBaseTexture[] =
    "uniform sampler2DShadow osgShadow_shadowTexture; \n"
    "uniform vec2 osgShadow_ambientBias; \n"
    "\n"
    "void main(void) \n"
    "{ \n"
    "    gl_FragColor = gl_Color * (osgShadow_ambientBias.x + shadow2DProj( osgShadow_shadowTexture, gl_TexCoord[0] ) * osgShadow_ambientBias.y); \n"
    "}\n";

//////////////////////////////////////////////////////////////////
// fragment shader
//
static const char fragmentShaderSource_withBaseTexture[] =
    "uniform sampler2D osgShadow_baseTexture; \n"
    "uniform sampler2DShadow osgShadow_shadowTexture; \n"
    "uniform vec2 osgShadow_ambientBias; \n"
    "\n"
    "void main(void) \n"
    "{ \n"
    "    vec4 color = gl_Color * texture2D( osgShadow_baseTexture, gl_TexCoord[0].xy ); \n"
    "    gl_FragColor = color * (osgShadow_ambientBias.x + shadow2DProj( osgShadow_shadowTexture, gl_TexCoord[1] ) * osgShadow_ambientBias.y); \n"
    "}\n";

//////////////////////////////////////////////////////////////////
// fragment shader
//
static const char fragmentShaderSource_debugHUD[] =
    "uniform sampler2D osgShadow_shadowTexture; \n"
    " \n"
    "void main(void) \n"
    "{ \n"
    "   vec4 texResult = texture2D(osgShadow_shadowTexture, gl_TexCoord[0].st ); \n"
    "   float value = texResult.r; \n"
    "   gl_FragColor = vec4( value, value, value, 0.8 ); \n"
    "} \n";

ShadowMap::ShadowMap():
    _baseTextureUnit(0),
    _shadowTextureUnit(1),
    _polyOffset(1.0,1.0),
    _ambientBias(0.5f,0.5f),
    _textureSize(1024,1024)
{
}

ShadowMap::ShadowMap(const ShadowMap& copy, const osg::CopyOp& copyop):
ShadowTechnique(copy,copyop),
    _baseTextureUnit(copy._baseTextureUnit),
    _shadowTextureUnit(copy._shadowTextureUnit),
    _polyOffset(copy._polyOffset),
    _ambientBias(copy._ambientBias),
    _textureSize(copy._textureSize)
{
}

void ShadowMap::setTextureUnit(unsigned int unit)
{
    _shadowTextureUnit = unit;
}

void ShadowMap::setPolygonOffset(const osg::Vec2& polyOffset)
{
    _polyOffset = polyOffset;
}

void ShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
{
    _ambientBias = ambientBias;
    if (_ambientBiasUniform.valid()) _ambientBiasUniform->set(_ambientBias);
}

void ShadowMap::setTextureSize(const osg::Vec2s& textureSize)
{
    _textureSize = textureSize;
    dirty();
}

void ShadowMap::setLight(osg::Light* light)
{
    _light = light;
}


void ShadowMap::setLight(osg::LightSource* ls)
{
    _ls = ls;
    _light = _ls->getLight();
}

void ShadowMap::createUniforms()
{
    _uniformList.clear();

    osg::Uniform* baseTextureSampler = new osg::Uniform("osgShadow_baseTexture",(int)_baseTextureUnit);
    _uniformList.push_back(baseTextureSampler);

    osg::Uniform* shadowTextureSampler = new osg::Uniform("osgShadow_shadowTexture",(int)_shadowTextureUnit);
    _uniformList.push_back(shadowTextureSampler);

    _ambientBiasUniform = new osg::Uniform("osgShadow_ambientBias",_ambientBias);
    _uniformList.push_back(_ambientBiasUniform.get());

}

void ShadowMap::createShaders()
{
    // if we are not given shaders, use the default
    if( _shaderList.empty() )
    {
        if (_shadowTextureUnit==0)
        {
            osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture);
            _shaderList.push_back(fragment_shader);
        }
        else
        {
            osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture);
            _shaderList.push_back(fragment_shader);

        }
    }
}

void ShadowMap::init()
{
    if (!_shadowedScene) return;

    _texture = new osg::Texture2D;
    _texture->setTextureSize(_textureSize.x(), _textureSize.y());
    _texture->setInternalFormat(GL_DEPTH_COMPONENT);
    _texture->setShadowComparison(true);
    _texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
    _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
    _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);

    // the shadow comparison should fail if object is outside the texture
    _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
    _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
    _texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));

    // set up the render to texture camera.
    {
        // create the camera
        _camera = new osg::Camera;

        _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);

        _camera->setCullCallback(new CameraCullCallback(this));

        _camera->setClearMask(GL_DEPTH_BUFFER_BIT);
        //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        _camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
        _camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);

        // set viewport
        _camera->setViewport(0,0,_textureSize.x(),_textureSize.y());

        // set the camera to render before the main camera.
        _camera->setRenderOrder(osg::Camera::PRE_RENDER);

        // tell the camera to use OpenGL frame buffer object where supported.
        _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
        //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);

        // attach the texture and use it as the color buffer.
        _camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());

        osg::StateSet* stateset = _camera->getOrCreateStateSet();


#if 1
        // cull front faces so that only backfaces contribute to depth map


        osg::ref_ptr<osg::CullFace> cull_face = new osg::CullFace;
        cull_face->setMode(osg::CullFace::FRONT);
        stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
        stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

        // negative polygonoffset - move the backface nearer to the eye point so that backfaces
        // shadow themselves
        float factor = -_polyOffset[0];
        float units =  -_polyOffset[1];

        osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
        polygon_offset->setFactor(factor);
        polygon_offset->setUnits(units);
        stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
        stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
#else
        // disabling cull faces so that only front and backfaces contribute to depth map
        stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);

        // negative polygonoffset - move the backface nearer to the eye point
        // so that front faces do not shadow themselves.
        float factor = _polyOffset[0];
        float units =  _polyOffset[1];

        osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
        polygon_offset->setFactor(factor);
        polygon_offset->setUnits(units);
        stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
        stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
#endif
    }

    {
        _stateset = new osg::StateSet;
        _stateset->setTextureAttributeAndModes(_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
        _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
        _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
        _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
        _stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);

        _texgen = new osg::TexGen;

        // add Program, when empty of Shaders then we are using fixed functionality
        _program = new osg::Program;
        _stateset->setAttribute(_program.get());

        // create default shaders if needed
        createShaders();

        // add the shader list to the program
        for(ShaderList::const_iterator itr=_shaderList.begin();
            itr!=_shaderList.end();
            ++itr)
        {
            _program->addShader(itr->get());
        }

        // create own uniforms
        createUniforms();

        // add the uniform list to the stateset
        for(UniformList::const_iterator itr=_uniformList.begin();
            itr!=_uniformList.end();
            ++itr)
        {
            _stateset->addUniform(itr->get());
        }

        {
            // fake texture for baseTexture, add a fake texture
            // we support by default at least one texture layer
            // without this fake texture we can not support
            // textured and not textured scene

            // TODO: at the moment the PSSM supports just one texture layer in the GLSL shader, multitexture are
            //       not yet supported !

            osg::Image* image = new osg::Image;
            // allocate the image data, noPixels x 1 x 1 with 4 rgba floats - equivalent to a Vec4!
            int noPixels = 1;
            image->allocateImage(noPixels,1,1,GL_RGBA,GL_FLOAT);
            image->setInternalTextureFormat(GL_RGBA);
            // fill in the image data.
            osg::Vec4* dataPtr = (osg::Vec4*)image->data();
            osg::Vec4 color(1,1,1,1);
            *dataPtr = color;
            // make fake texture
            osg::Texture2D* fakeTex = new osg::Texture2D;
            fakeTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_EDGE);
            fakeTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_EDGE);
            fakeTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
            fakeTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
            fakeTex->setImage(image);
            // add fake texture
            _stateset->setTextureAttribute(_baseTextureUnit,fakeTex,osg::StateAttribute::ON);
            _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_2D,osg::StateAttribute::ON);
            _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_3D,osg::StateAttribute::OFF);
            #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
                _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_1D,osg::StateAttribute::OFF);
            #endif
        }
    }

    _dirty = false;
}


void ShadowMap::update(osg::NodeVisitor& nv)
{
    _shadowedScene->osg::Group::traverse(nv);
}

void ShadowMap::cull(osgUtil::CullVisitor& cv)
{
    // record the traversal mask on entry so we can reapply it later.
    unsigned int traversalMask = cv.getTraversalMask();

    osgUtil::RenderStage* orig_rs = cv.getRenderStage();

    // do traversal of shadow receiving scene which does need to be decorated by the shadow map
    {
        cv.pushStateSet(_stateset.get());

        _shadowedScene->osg::Group::traverse(cv);

        cv.popStateSet();

    }

    // need to compute view frustum for RTT camera.
    // 1) get the light position
    // 2) get the center and extents of the view frustum

    const osg::Light* selectLight = 0;
    osg::Vec4 lightpos;
    osg::Vec3 lightDir;

    //MR testing giving a specific light
    osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
    for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
        itr != aml.end();
        ++itr)
    {
        const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
        if (light)
        {
            if( _light.valid()) {
                if( _light.get() == light )
                    selectLight = light;
                else
                    continue;
            }
            else
                selectLight = light;

            osg::RefMatrix* matrix = itr->second.get();
            if (matrix)
            {
                lightpos = light->getPosition() * (*matrix);
                lightDir = osg::Matrix::transform3x3( light->getDirection(), *matrix );
            }
            else
            {
                lightpos = light->getPosition();
                lightDir = light->getDirection();
            }

        }
    }

    osg::Matrix eyeToWorld;
    eyeToWorld.invert(*cv.getModelViewMatrix());

    lightpos = lightpos * eyeToWorld;
    lightDir = osg::Matrix::transform3x3( lightDir, eyeToWorld );
    lightDir.normalize();

    if (selectLight)
    {

        // set to ambient on light to black so that the ambient bias uniform can take it's affect
        const_cast<osg::Light*>(selectLight)->setAmbient(osg::Vec4(0.0f,0.0f,0.0f,1.0f));

        //std::cout<<"----- VxOSG::ShadowMap selectLight spot cutoff "<<selectLight->getSpotCutoff()<<std::endl;

        float fov = selectLight->getSpotCutoff() * 2;
        if(fov < 180.0f)   // spotlight, then we don't need the bounding box
        {
            osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
            _camera->setProjectionMatrixAsPerspective(fov, 1.0, 0.1, 1000.0);
            _camera->setViewMatrixAsLookAt(position,position+lightDir,computeOrthogonalVector(lightDir));
        }
        else
        {
            // get the bounds of the model.
            osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
            cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());

            _shadowedScene->osg::Group::traverse(cbbv);

            osg::BoundingBox bb = cbbv.getBoundingBox();

            if (lightpos[3]!=0.0)   // point light
            {
                osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());

                float centerDistance = (position-bb.center()).length();

                float znear = centerDistance-bb.radius();
                float zfar  = centerDistance+bb.radius();
                float zNearRatio = 0.001f;
                if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;

                float top   = (bb.radius()/centerDistance)*znear;
                float right = top;

                _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
                _camera->setViewMatrixAsLookAt(position,bb.center(),computeOrthogonalVector(bb.center()-position));
            }
            else    // directional light
            {
                // make an orthographic projection
                osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
                lightDir.normalize();

                // set the position far away along the light direction
                osg::Vec3 position = bb.center() + lightDir * bb.radius() * 2;

                float centerDistance = (position-bb.center()).length();

                float znear = centerDistance-bb.radius();
                float zfar  = centerDistance+bb.radius();
                float zNearRatio = 0.001f;
                if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;

                float top   = bb.radius();
                float right = top;

                _camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
                _camera->setViewMatrixAsLookAt(position,bb.center(),computeOrthogonalVector(lightDir));
            }


        }

        cv.setTraversalMask( traversalMask &
            getShadowedScene()->getCastsShadowTraversalMask() );

        // do RTT camera traversal
        _camera->accept(cv);

        _texgen->setMode(osg::TexGen::EYE_LINEAR);

#if IMPROVE_TEXGEN_PRECISION
        // compute the matrix which takes a vertex from local coords into tex coords
        // We actually use two matrices one used to define texgen
        // and second that will be used as modelview when appling to OpenGL
        _texgen->setPlanesFromMatrix( _camera->getProjectionMatrix() *
                                      osg::Matrix::translate(1.0,1.0,1.0) *
                                      osg::Matrix::scale(0.5f,0.5f,0.5f) );

        // Place texgen with modelview which removes big offsets (making it float friendly)
        osg::RefMatrix * refMatrix = new osg::RefMatrix
            ( _camera->getInverseViewMatrix() * *cv.getModelViewMatrix() );

        cv.getRenderStage()->getPositionalStateContainer()->
             addPositionedTextureAttribute( _shadowTextureUnit, refMatrix, _texgen.get() );
#else
        // compute the matrix which takes a vertex from local coords into tex coords
        // will use this later to specify osg::TexGen..
        osg::Matrix MVPT = _camera->getViewMatrix() *
               _camera->getProjectionMatrix() *
               osg::Matrix::translate(1.0,1.0,1.0) *
               osg::Matrix::scale(0.5f,0.5f,0.5f);

        _texgen->setPlanesFromMatrix(MVPT);

        orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_shadowTextureUnit, cv.getModelViewMatrix(), _texgen.get());
#endif
    } // if(selectLight)


    // reapply the original traversal mask
    cv.setTraversalMask( traversalMask );
}

void ShadowMap::cleanSceneGraph()
{
}

///////////////////// Debug Methods

////////////////////////////////////////////////////////////////////////////////
// Callback used by debugging hud to display Shadow Map in color buffer
// OSG does not allow to use the same GL Texture Id with different glTexParams.
// Callback simply turns shadow compare mode off via GL while rendering hud and
// restores it afterwards.
////////////////////////////////////////////////////////////////////////////////
class ShadowMap::DrawableDrawWithDepthShadowComparisonOffCallback:
    public osg::Drawable::DrawCallback
{
public:
    //
    DrawableDrawWithDepthShadowComparisonOffCallback
        ( osg::Texture2D * texture, unsigned stage = 0 )
            : _texture( texture ), _stage( stage )
    {
    }

    virtual void drawImplementation
        ( osg::RenderInfo & ri,const osg::Drawable* drawable ) const
    {
        if( _texture.valid() ) {
            // make sure proper texture is currently applied
            ri.getState()->applyTextureAttribute( _stage, _texture.get() );

            // Turn off depth comparison mode
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
                             GL_NONE );
        }

        drawable->drawImplementation(ri);

        if( _texture.valid() ) {
            // Turn it back on
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
                             GL_COMPARE_R_TO_TEXTURE_ARB );
        }
    }

    osg::ref_ptr< osg::Texture2D > _texture;
    unsigned                       _stage;
};

////////////////////////////////////////////////////////////////////////////////
osg::ref_ptr<osg::Camera> ShadowMap::makeDebugHUD()
{
    // Make sure we attach initialized texture to HUD
    if( !_texture.valid() )    init();

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;

    osg::Vec2 size(1280, 1024);
    // set the projection matrix
    camera->setProjectionMatrix(osg::Matrix::ortho2D(0,size.x(),0,size.y()));

    // set the view matrix
    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    camera->setViewMatrix(osg::Matrix::identity());

    // only clear the depth buffer
    camera->setClearMask(GL_DEPTH_BUFFER_BIT);
    camera->setClearColor(osg::Vec4(0.2f, 0.3f, 0.5f, 0.2f));
    //camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    // draw subgraph after main camera view.
    camera->setRenderOrder(osg::Camera::POST_RENDER);

    // we don't want the camera to grab event focus from the viewers main camera(s).
    camera->setAllowEventFocus(false);

    osg::Geode* geode = new osg::Geode;

    osg::Vec3 position(10.0f,size.y()-100.0f,0.0f);
    osg::Vec3 delta(0.0f,-120.0f,0.0f);
    float length = 300.0f;

    // turn the text off to avoid linking with osgText
#if 0
    std::string timesFont("fonts/arial.ttf");

    {
        osgText::Text* text = new  osgText::Text;
        geode->addDrawable( text );

        text->setFont(timesFont);
        text->setPosition(position);
        text->setText("Shadow Map HUD");

        position += delta;
    }
#endif

    osg::Vec3 widthVec(length, 0.0f, 0.0f);
    osg::Vec3 depthVec(0.0f,length, 0.0f);
    osg::Vec3 centerBase( 10.0f + length/2, size.y()-length/2, 0.0f);
    centerBase += delta;

    osg::Geometry *geometry = osg::createTexturedQuadGeometry
        ( centerBase-widthVec*0.5f-depthVec*0.5f, widthVec, depthVec );

    geode->addDrawable( geometry );

    geometry->setDrawCallback
        ( new DrawableDrawWithDepthShadowComparisonOffCallback( _texture.get() ) );

    osg::StateSet* stateset = geode->getOrCreateStateSet();

    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
    stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);

    // test with regular texture
    //stateset->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile("Images/lz.rgb")));

    stateset->setTextureAttributeAndModes(0,_texture.get(),osg::StateAttribute::ON);

    //test to check the texture coordinates generated during shadow pass
#if 0
    stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
    stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
    stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
    stateset->setTextureMode(_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);

    // create TexGen node
    osg::ref_ptr<osg::TexGenNode> texGenNode = new osg::TexGenNode;
    texGenNode->setTextureUnit(_shadowTextureUnit);
    texGenNode->setTexGen(_texgen.get());
    camera->addChild(texGenNode.get());
#endif
    //shader for correct display

    osg::ref_ptr<osg::Program> program = new osg::Program;
    stateset->setAttribute(program.get());

    osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_debugHUD);
    program->addShader(fragment_shader);

    camera->addChild(geode);

    return camera;
}

//////////////////////// End Debug Section

