<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 <NAME> 
 *
 * 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 <osgUtil/SceneView>
#include <osgUtil/UpdateVisitor>
#include <osgUtil/GLObjectsVisitor>

#include <osg/Timer>
#include <osg/GLExtensions>
#include <osg/GLObjects>
#include <osg/Notify>
#include <osg/Texture>
#include <osg/AlphaFunc>
#include <osg/TexEnv>
#include <osg/ColorMatrix>
#include <osg/LightModel>
#include <osg/CollectOccludersVisitor>

#include <osg/GLU>

using namespace osg;
using namespace osgUtil;

static const GLubyte patternVertEven[] = {
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};

static const GLubyte patternVertOdd[] = {
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};

static const GLubyte patternHorzEven[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};

// 32 x 32 bit array every row is a horizontal line of pixels
//  and the (bitwise) columns a vertical line 
//  The following is a checkerboard pattern
static const GLubyte patternCheckerboard[] = {
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA};

SceneView::SceneView(DisplaySettings* ds)
{
    _displaySettings = ds;

    _fusionDistanceMode = PROPORTIONAL_TO_SCREEN_DISTANCE;
    _fusionDistanceValue = 1.0f;

    _lightingMode=NO_SCENEVIEW_LIGHT;
    
    _prioritizeTextures = false;
    
    setCamera(new Camera);
    _camera->setViewport(new Viewport);
    _camera->setClearColor(osg::Vec4(0.2f, 0.2f, 0.4f, 1.0f));
    
    _initCalled = false;

    _camera->setDrawBuffer(GL_BACK);

    _requiresFlush = true;
    
    _activeUniforms = DEFAULT_UNIFORMS;
    
    _previousFrameTime = 0;
    _previousSimulationTime = 0;
    
    _redrawInterlacedStereoStencilMask = true;
    _interlacedStereoStencilWidth = 0;
    _interlacedStereoStencilHeight = 0;

    _dynamicObjectCount = 0;
}

SceneView::SceneView(const SceneView& rhs, const osg::CopyOp& copyop):
    osg::Object(rhs,copyop),
    osg::CullSettings(rhs)
{
    _displaySettings = rhs._displaySettings;

    _fusionDistanceMode = rhs._fusionDistanceMode;
    _fusionDistanceValue = rhs._fusionDistanceValue;

    _lightingMode = rhs._lightingMode;
    
    _prioritizeTextures = rhs._prioritizeTextures;
    
    _camera = rhs._camera;
    _cameraWithOwnership = rhs._cameraWithOwnership;
    
    _initCalled = false;

    _requiresFlush = rhs._requiresFlush;
    
    _activeUniforms = rhs._activeUniforms;
    
    _previousFrameTime = 0;
    _previousSimulationTime = 0;
    
    _redrawInterlacedStereoStencilMask = rhs._redrawInterlacedStereoStencilMask;
    _interlacedStereoStencilWidth = rhs._interlacedStereoStencilWidth;
    _interlacedStereoStencilHeight = rhs._interlacedStereoStencilHeight;

    _dynamicObjectCount = 0;
}

SceneView::~SceneView()
{
}


void SceneView::setDefaults(unsigned int options)
{
    osg::CullSettings::setDefaults();

    _camera->getProjectionMatrix().makePerspective(50.0f,1.4f,1.0f,10000.0f);
    _camera->getViewMatrix().makeIdentity();

    if (!_globalStateSet) _globalStateSet = new osg::StateSet;
    else _globalStateSet->clear();

    if ((options & HEADLIGHT) || (options & SKY_LIGHT))
    {
        _lightingMode=(options&HEADLIGHT) ? HEADLIGHT : SKY_LIGHT;
        _light = new osg::Light;
        _light->setLightNum(0);
        _light->setAmbient(Vec4(0.00f,0.0f,0.00f,1.0f));
        _light->setDiffuse(Vec4(0.8f,0.8f,0.8f,1.0f));
        _light->setSpecular(Vec4(1.0f,1.0f,1.0f,1.0f));

        _globalStateSet->setAssociatedModes(_light.get(),osg::StateAttribute::ON);

        osg::LightModel* lightmodel = new osg::LightModel;
        lightmodel->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f));
        _globalStateSet->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);

        // enable lighting by default.
        _globalStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);

    }
    else
    {
        _lightingMode = NO_SCENEVIEW_LIGHT;
    }
 
    _renderInfo.setState(new State);
    
    _stateGraph = new StateGraph;
    _renderStage = new RenderStage;


    if (options & COMPILE_GLOBJECTS_AT_INIT)
    {
        GLObjectsVisitor::Mode  dlvMode = GLObjectsVisitor::COMPILE_DISPLAY_LISTS |
                                          GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES | 
                                          GLObjectsVisitor::CHECK_BLACK_LISTED_MODES;

    #ifdef __sgi
        dlvMode = GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES;
    #endif

        // sgi's IR graphics has a problem with lighting and display lists, as it seems to store 
        // lighting state with the display list, and the display list visitor doesn't currently apply
        // state before creating display lists. So will disable the init visitor default, this won't
        // affect functionality since the display lists will be created as and when needed.
        GLObjectsVisitor* dlv = new GLObjectsVisitor(dlvMode);
        dlv->setNodeMaskOverride(0xffffffff);
        _initVisitor = dlv;

    }
    
    _updateVisitor = new UpdateVisitor;

    _cullVisitor = CullVisitor::create();

    _cullVisitor->setStateGraph(_stateGraph.get());
    _cullVisitor->setRenderStage(_renderStage.get());

    _globalStateSet->setGlobalDefaults();

    // set up an texture environment by default to speed up blending operations.
     osg::TexEnv* texenv = new osg::TexEnv;
     texenv->setMode(osg::TexEnv::MODULATE);
     _globalStateSet->setTextureAttributeAndModes(0,texenv, osg::StateAttribute::ON);

    _camera->setClearColor(osg::Vec4(0.2f, 0.2f, 0.4f, 1.0f));
}

void SceneView::setCamera(osg::Camera* camera, bool assumeOwnershipOfCamera)
{
    if (camera)
    {
        _camera = camera;
    }
    else
    {
        osg::notify(osg::NOTICE)<<"Warning: attempt to assign a NULL camera to SceneView not permitted."<<std::endl;
    }
    
    if (assumeOwnershipOfCamera)
    {
        _cameraWithOwnership = _camera.get();
    }
    else
    {
        _cameraWithOwnership = 0;
    }
}

void SceneView::setSceneData(osg::Node* node)
{
    // take a temporary reference to node to prevent the possibility
    // of it getting deleted when when we do the camera clear of children. 
    osg::ref_ptr<osg::Node> temporaryRefernce = node;
    
    // remove pre existing children
    _camera->removeChildren(0, _camera->getNumChildren());
    
    // add the new one in.
    _camera->addChild(node);
}

void SceneView::init()
{
    _initCalled = true;

    // force the initialization of the OpenGL extension string
    // to try and work around a Windows NVidia driver bug circa Oct 2006.
    osg::isGLExtensionSupported(_renderInfo.getState()->getContextID(),"");

    if (_camera.valid() && _initVisitor.valid())
    {
        _initVisitor->reset();
        _initVisitor->setFrameStamp(_frameStamp.get());
        
        GLObjectsVisitor* dlv = dynamic_cast<GLObjectsVisitor*>(_initVisitor.get());
        if (dlv) dlv->setState(_renderInfo.getState());
        
        if (_frameStamp.valid())
        {
             _initVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }
        
        _camera->accept(*_initVisitor.get());
        
    } 
}

void SceneView::update()
{
    if (_camera.valid() && _updateVisitor.valid())
    { 
        _updateVisitor->reset();

        _updateVisitor->setFrameStamp(_frameStamp.get());

        // use the frame number for the traversal number.
        if (_frameStamp.valid())
        {
             _updateVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }
        
        _camera->accept(*_updateVisitor.get());
        
        // now force a recompute of the bounding volume while we are still in
        // the read/write app phase, this should prevent the need to recompute
        // the bounding volumes from within the cull traversal which may be
        // multi-threaded.
        _camera->getBound();
    }
}

void SceneView::updateUniforms()
{
    if (!_localStateSet)
    {
        _localStateSet = new osg::StateSet;
    }

    if (!_localStateSet) return;
    
    if ((_activeUniforms & FRAME_NUMBER_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_FrameNumber",osg::Uniform::INT);
        uniform->set(_frameStamp->getFrameNumber());        
    }
    
    if ((_activeUniforms & FRAME_TIME_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_FrameTime",osg::Uniform::FLOAT);
        uniform->set(static_cast<float>(_frameStamp->getReferenceTime()));
    }
    
    if ((_activeUniforms & DELTA_FRAME_TIME_UNIFORM) && _frameStamp.valid())
    {
        float delta_frame_time = (_previousFrameTime != 0.0) ? static_cast<float>(_frameStamp->getReferenceTime()-_previousFrameTime) : 0.0f;
        _previousFrameTime = _frameStamp->getReferenceTime();
        
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_DeltaFrameTime",osg::Uniform::FLOAT);
        uniform->set(delta_frame_time);
    }
    
    if ((_activeUniforms & SIMULATION_TIME_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_SimulationTime",osg::Uniform::FLOAT);
        uniform->set(static_cast<float>(_frameStamp->getSimulationTime()));
    }
    
    if ((_activeUniforms & DELTA_SIMULATION_TIME_UNIFORM) && _frameStamp.valid())
    {
        float delta_simulation_time = (_previousSimulationTime != 0.0) ? static_cast<float>(_frameStamp->getSimulationTime()-_previousSimulationTime) : 0.0f;
        _previousSimulationTime = _frameStamp->getSimulationTime();
        
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_DeltaSimulationTime",osg::Uniform::FLOAT);
        uniform->set(delta_simulation_time);
    }
    
    if (_activeUniforms & VIEW_MATRIX_UNIFORM)
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_ViewMatrix",osg::Uniform::FLOAT_MAT4);
        uniform->set(getViewMatrix());
    }

    if (_activeUniforms & VIEW_MATRIX_INVERSE_UNIFORM)
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_ViewMatrixInverse",osg::Uniform::FLOAT_MAT4);
        uniform->set(osg::Matrix::inverse(getViewMatrix()));
    }

}

osg::Matrixd SceneView::computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const
{
    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double scale_x = 1.0;
    double scale_y = 1.0;

    if (_displaySettings->getSplitStereoAutoAdjustAspectRatio())
    {
        switch(_displaySettings->getStereoMode())
        {
            case(osg::DisplaySettings::HORIZONTAL_SPLIT):
                scale_x = 2.0;
                break;
            case(osg::DisplaySettings::VERTICAL_SPLIT):
                scale_y = 2.0;
                break;
            default:
                break;
        }
    }

    if (_displaySettings->getDisplayType()==osg::DisplaySettings::HEAD_MOUNTED_DISPLAY)
    {
        // head mounted display has the same projection matrix for left and right eyes.
        return osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
    else
    {
        // all other display types assume working like a projected power wall
        // need to shjear projection matrix to account for asymetric frustum due to eye offset.
        return osg::Matrixd(1.0,0.0,0.0,0.0,
                           0.0,1.0,0.0,0.0,
                           iod/(2.0*sd),0.0,1.0,0.0,
                           0.0,0.0,0.0,1.0) *
               osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
}

osg::Matrixd SceneView::computeLeftEyeViewImplementation(const osg::Matrixd& view) const
{
    double fusionDistance = _displaySettings->getScreenDistance();
    switch(_fusionDistanceMode)
    {
        case(USE_FUSION_DISTANCE_VALUE):
            fusionDistance = _fusionDistanceValue;
            break;
        case(PROPORTIONAL_TO_SCREEN_DISTANCE):
            fusionDistance *= _fusionDistanceValue;
            break;
    }

    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double es = 0.5f*iod*(fusionDistance/sd);

    return view *
           osg::Matrixd(1.0,0.0,0.0,0.0,
                       0.0,1.0,0.0,0.0,
                       0.0,0.0,1.0,0.0,
                       es,0.0,0.0,1.0);
}

osg::Matrixd SceneView::computeRightEyeProjectionImplementation(const osg::Matrixd& projection) const
{
    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double scale_x = 1.0;
    double scale_y = 1.0;

    if (_displaySettings->getSplitStereoAutoAdjustAspectRatio())
    {
        switch(_displaySettings->getStereoMode())
        {
            case(osg::DisplaySettings::HORIZONTAL_SPLIT):
                scale_x = 2.0;
                break;
            case(osg::DisplaySettings::VERTICAL_SPLIT):
                scale_y = 2.0;
                break;
            default:
                break;
        }
    }

    if (_displaySettings->getDisplayType()==osg::DisplaySettings::HEAD_MOUNTED_DISPLAY)
    {
        // head mounted display has the same projection matrix for left and right eyes.
        return osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
    else
    {
        // all other display types assume working like a projected power wall
        // need to shjear projection matrix to account for asymetric frustum due to eye offset.
        return osg::Matrixd(1.0,0.0,0.0,0.0,
                           0.0,1.0,0.0,0.0,
                           -iod/(2.0*sd),0.0,1.0,0.0,
                           0.0,0.0,0.0,1.0) *
               osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
}

osg::Matrixd SceneView::computeRightEyeViewImplementation(const osg::Matrixd& view) const
{
    float fusionDistance = _displaySettings->getScreenDistance();
    switch(_fusionDistanceMode)
    {
        case(USE_FUSION_DISTANCE_VALUE):
            fusionDistance = _fusionDistanceValue;
            break;
        case(PROPORTIONAL_TO_SCREEN_DISTANCE):
            fusionDistance *= _fusionDistanceValue;
            break;
    }

    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double es = 0.5*iod*(fusionDistance/sd);

    return view *
           osg::Matrixd(1.0,0.0,0.0,0.0,
                       0.0,1.0,0.0,0.0,
                       0.0,0.0,1.0,0.0,
                       -es,0.0,0.0,1.0);
}

void SceneView::computeLeftEyeViewport(const osg::Viewport *viewport)
{
    if(!viewport) return;

    if(!_viewportLeft.valid()) _viewportLeft = new osg::Viewport;

    switch(_displaySettings->getStereoMode())
    {

        case(osg::DisplaySettings::HORIZONTAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoHorizontalSeparation();

            if (_displaySettings->getSplitStereoHorizontalEyeMapping()==osg::DisplaySettings::LEFT_EYE_LEFT_VIEWPORT)
            {
                Viewport::value_type left_half_width = (viewport->width()-separation)/2;
                _viewportLeft->setViewport(viewport->x(),viewport->y(),left_half_width,viewport->height());
            }
            else
            {
                Viewport::value_type right_half_begin = (viewport->width()+separation)/2;
                Viewport::value_type right_half_width = viewport->width()-right_half_begin;
                _viewportLeft->setViewport(viewport->x()+right_half_begin,viewport->y(),right_half_width,viewport->height());
            }
        }
        break;

        case(osg::DisplaySettings::VERTICAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoVerticalSeparation();

            if (_displaySettings->getSplitStereoVerticalEyeMapping()==osg::DisplaySettings::LEFT_EYE_TOP_VIEWPORT)
            {
                Viewport::value_type top_half_begin = (viewport->height()+separation)/2;
                Viewport::value_type top_half_height = viewport->height()-top_half_begin;
                _viewportLeft->setViewport(viewport->x(),viewport->y()+top_half_begin,viewport->width(),top_half_height);
            }
            else
            {
                Viewport::value_type bottom_half_height = (viewport->height()-separation)/2;
                _viewportLeft->setViewport(viewport->x(),viewport->y(),viewport->width(),bottom_half_height);
            }
        }
        break;

        default:
            _viewportLeft->setViewport(viewport->x(),viewport->y(),viewport->width(),viewport->height());
            break;
    }
}

void SceneView::computeRightEyeViewport(const osg::Viewport *viewport)
{
    if(!viewport) return;

    if(!_viewportRight.valid()) _viewportRight = new osg::Viewport;

    switch(_displaySettings->getStereoMode())
    {
        case(osg::DisplaySettings::HORIZONTAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoHorizontalSeparation();

            if (_displaySettings->getSplitStereoHorizontalEyeMapping()!=osg::DisplaySettings::LEFT_EYE_LEFT_VIEWPORT)
            {
                Viewport::value_type left_half_width = (viewport->width()-separation)/2;
                _viewportRight->setViewport(viewport->x(),viewport->y(),left_half_width,viewport->height());
            }
            else
            {
                Viewport::value_type right_half_begin = (viewport->width()+separation)/2;
                Viewport::value_type right_half_width = viewport->width()-right_half_begin;
                _viewportRight->setViewport(viewport->x()+right_half_begin,viewport->y(),right_half_width,viewport->height());
            }
        }
        break;

        case(osg::DisplaySettings::VERTICAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoVerticalSeparation();

            if (_displaySettings->getSplitStereoVerticalEyeMapping()!=osg::DisplaySettings::LEFT_EYE_TOP_VIEWPORT)
            {
                Viewport::value_type top_half_begin = (viewport->height()+separation)/2;
                Viewport::value_type top_half_height = viewport->height()-top_half_begin;
                _viewportRight->setViewport(viewport->x(),viewport->y()+top_half_begin,viewport->width(),top_half_height);
            }
            else
            {
                Viewport::value_type bottom_half_height = (viewport->height()-separation)/2;
                _viewportRight->setViewport(viewport->x(),viewport->y(),viewport->width(),bottom_half_height);
            }
        }
        break;

        default:
            _viewportRight->setViewport(viewport->x(),viewport->y(),viewport->width(),viewport->height());
            break;
    }
}

void SceneView::setLightingMode(LightingMode mode)
{
    if (mode==_lightingMode) return;
    
    if (_lightingMode!=NO_SCENEVIEW_LIGHT)
    {
        // remove GL_LIGHTING mode
        _globalStateSet->removeMode(GL_LIGHTING);

        if (_light.valid())
        {
            _globalStateSet->removeAssociatedModes(_light.get());
        }

    }

    _lightingMode = mode;

    if (_lightingMode!=NO_SCENEVIEW_LIGHT)
    {
        // add GL_LIGHTING mode
        _globalStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
        if (_light.valid()) 
        {
            _globalStateSet->setAssociatedModes(_light.get(), osg::StateAttribute::ON);
        }
    }
}

void SceneView::inheritCullSettings(const osg::CullSettings& settings, unsigned int inheritanceMask)
{
    if (_camera.valid() && _camera->getView()) 
    {
        if (inheritanceMask & osg::CullSettings::LIGHTING_MODE)
        {
            LightingMode newLightingMode = _lightingMode;
        
            switch(_camera->getView()->getLightingMode())
            {
                case(osg::View::NO_LIGHT): newLightingMode = NO_SCENEVIEW_LIGHT; break;
                case(osg::View::HEADLIGHT): newLightingMode = HEADLIGHT; break;
                case(osg::View::SKY_LIGHT): newLightingMode = SKY_LIGHT; break;
            }
            
            if (newLightingMode != _lightingMode)
            {
                setLightingMode(newLightingMode);
            }
        }
                
        if (inheritanceMask & osg::CullSettings::LIGHT)
        {
            setLight(_camera->getView()->getLight());
        }
    }
    
    osg::CullSettings::inheritCullSettings(settings, inheritanceMask);
}


void SceneView::cull()
{
    _dynamicObjectCount = 0;

    if (_camera->getNodeMask()==0) return;

    _renderInfo.setView(_camera->getView());

    // update the active uniforms
    updateUniforms();

    if (!_renderInfo.getState())
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView::_state attached, creating a default state automatically."<< std::endl;

        // note the constructor for osg::State will set ContextID to 0 which will be fine to single context graphics
        // applications which is ok for most apps, but not multiple context/pipe applications.
        _renderInfo.setState(new osg::State);
    }
    
    osg::State* state = _renderInfo.getState();

    if (!_localStateSet)
    {
        _localStateSet = new osg::StateSet;
    }
    
    // we in theory should be able to be able to bypass reset, but we'll call it just incase.
    //_state->reset();
   
    state->setFrameStamp(_frameStamp.get());
    state->setDisplaySettings(_displaySettings.get());


    if (!_cullVisitor)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView:: attached, creating a default CullVisitor automatically."<< std::endl;
        _cullVisitor = CullVisitor::create();
    }
    if (!_stateGraph)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView:: attached, creating a global default StateGraph automatically."<< std::endl;
        _stateGraph = new StateGraph;
    }
    if (!_renderStage)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView::_renderStage attached, creating a default RenderStage automatically."<< std::endl;
        _renderStage = new RenderStage;
    }

    if (_displaySettings.valid() && _displaySettings->getStereo()) 
    {

        if (_displaySettings->getStereoMode()==osg::DisplaySettings::LEFT_EYE)
        {
            // set up the left eye.
            _cullVisitor->setTraversalMask(_cullMaskLeft);
            computeLeftEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeLeftEyeProjection(getProjectionMatrix()),computeLeftEyeView(getViewMatrix()),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),_viewportLeft.get());

            if (computeNearFar)
            {
                CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
                CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }
        else if (_displaySettings->getStereoMode()==osg::DisplaySettings::RIGHT_EYE)
        {
            // set up the right eye.
            _cullVisitor->setTraversalMask(_cullMaskRight);
            computeRightEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeRightEyeProjection(getProjectionMatrix()),computeRightEyeView(getViewMatrix()),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),_viewportRight.get());

            if (computeNearFar)
            {
                CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
                CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }
        else
        {

            if (!_cullVisitorLeft.valid()) _cullVisitorLeft = dynamic_cast<CullVisitor*>(_cullVisitor->clone());
            if (!_stateGraphLeft.valid()) _stateGraphLeft = dynamic_cast<StateGraph*>(_stateGraph->cloneType());
            if (!_renderStageLeft.valid()) _renderStageLeft = dynamic_cast<RenderStage*>(_renderStage->clone(osg::CopyOp::DEEP_COPY_ALL));

            if (!_cullVisitorRight.valid()) _cullVisitorRight = dynamic_cast<CullVisitor*>(_cullVisitor->clone());
            if (!_stateGraphRight.valid()) _stateGraphRight = dynamic_cast<StateGraph*>(_stateGraph->cloneType());
            if (!_renderStageRight.valid()) _renderStageRight = dynamic_cast<RenderStage*>(_renderStage->clone(osg::CopyOp::DEEP_COPY_ALL));
            
            _cullVisitorLeft->setDatabaseRequestHandler(_cullVisitor->getDatabaseRequestHandler());
            _cullVisitorLeft->setClampProjectionMatrixCallback(_cullVisitor->getClampProjectionMatrixCallback());
            _cullVisitorLeft->setTraversalMask(_cullMaskLeft);
            computeLeftEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeLeftEyeProjection(getProjectionMatrix()),computeLeftEyeView(getViewMatrix()),_cullVisitorLeft.get(),_stateGraphLeft.get(),_renderStageLeft.get(),_viewportLeft.get());


            // set up the right eye.
            _cullVisitorRight->setDatabaseRequestHandler(_cullVisitor->getDatabaseRequestHandler());
            _cullVisitorRight->setClampProjectionMatrixCallback(_cullVisitor->getClampProjectionMatrixCallback());
            _cullVisitorRight->setTraversalMask(_cullMaskRight);
            computeRightEyeViewport(getViewport());
            computeNearFar = cullStage(computeRightEyeProjection(getProjectionMatrix()),computeRightEyeView(getViewMatrix()),_cullVisitorRight.get(),_stateGraphRight.get(),_renderStageRight.get(),_viewportRight.get());
           
            if (computeNearFar)
            {
                CullVisitor::value_type zNear = osg::minimum(_cullVisitorLeft->getCalculatedNearPlane(),_cullVisitorRight->getCalculatedNearPlane());
                CullVisitor::value_type zFar =  osg::maximum(_cullVisitorLeft->getCalculatedFarPlane(),_cullVisitorRight->getCalculatedFarPlane());
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }

    }
    else
    {

        _cullVisitor->setTraversalMask(_cullMask);
        bool computeNearFar = cullStage(getProjectionMatrix(),getViewMatrix(),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),getViewport());

        if (computeNearFar)
        {
            CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
            CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
            _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
        }
    }
    


}

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{

    if (!_camera || !viewport) return false;

    osg::ref_ptr<RefMatrix> proj = new osg::RefMatrix(projection);
    osg::ref_ptr<RefMatrix> mv = new osg::RefMatrix(modelview);

    // collect any occluder in the view frustum.
    if (_camera->containsOccluderNodes())
    {
        //std::cout << "Scene graph contains occluder nodes, searching for them"<<std::endl;
        
        
        if (!_collectOccludersVisitor) _collectOccludersVisitor = new osg::CollectOccludersVisitor;
        
        _collectOccludersVisitor->inheritCullSettings(*this);
        
        _collectOccludersVisitor->reset();
        
        _collectOccludersVisitor->setFrameStamp(_frameStamp.get());

        // use the frame number for the traversal number.
        if (_frameStamp.valid())
        {
             _collectOccludersVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }

        _collectOccludersVisitor->pushViewport(viewport);
        _collectOccludersVisitor->pushProjectionMatrix(proj.get());
        _collectOccludersVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);

        // traverse the scene graph to search for occluder in there new positions.
        for(unsigned int i=0; i< _camera->getNumChildren(); ++i)
        {
            _camera->getChild(i)->accept(*_collectOccludersVisitor);
        }

        _collectOccludersVisitor->popModelViewMatrix();
        _collectOccludersVisitor->popProjectionMatrix();
        _collectOccludersVisitor->popViewport();
        
        // sort the occluder from largest occluder volume to smallest.
        _collectOccludersVisitor->removeOccludedOccluders();
        
        
        osg::notify(osg::DEBUG_INFO) << "finished searching for occluder - found "<<_collectOccludersVisitor->getCollectedOccluderSet().size()<<std::endl;
           
        cullVisitor->getOccluderList().clear();
        std::copy(_collectOccludersVisitor->getCollectedOccluderSet().begin(),_collectOccludersVisitor->getCollectedOccluderSet().end(), std::back_insert_iterator<CullStack::OccluderList>(cullVisitor->getOccluderList()));
    }
    


    cullVisitor->reset();

    cullVisitor->setFrameStamp(_frameStamp.get());

    // use the frame number for the traversal number.
    if (_frameStamp.valid())
    {
         cullVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
    }

    cullVisitor->inheritCullSettings(*this);

    cullVisitor->setStateGraph(rendergraph);
    cullVisitor->setRenderStage(renderStage);

    cullVisitor->setRenderInfo( _renderInfo );

    renderStage->reset();

    // comment out reset of rendergraph since clean is more efficient.
    //  rendergraph->reset();

    // use clean of the rendergraph rather than reset, as it is able to
    // reuse the structure on the rendergraph in the next frame. This
    // achieves a certain amount of frame cohereancy of memory allocation.
    rendergraph->clean();

    renderStage->setViewport(viewport);
    renderStage->setClearColor(_camera->getClearColor());
    renderStage->setClearDepth(_camera->getClearDepth());
    renderStage->setClearAccum(_camera->getClearAccum());
    renderStage->setClearStencil(_camera->getClearStencil());
    renderStage->setClearMask(_camera->getClearMask());
    
#if 1    
    renderStage->setCamera(_camera.get());
#endif

    switch(_lightingMode)
    {
    case(HEADLIGHT):
        if (_light.valid()) renderStage->addPositionedAttribute(NULL,_light.get());
        else osg::notify(osg::WARN)<<"Warning: no osg::Light attached to ogUtil::SceneView to provide head light.*/"<<std::endl;
        break;
    case(SKY_LIGHT):
        if (_light.valid()) renderStage->addPositionedAttribute(mv.get(),_light.get());
        else osg::notify(osg::WARN)<<"Warning: no osg::Light attached to ogUtil::SceneView to provide sky light.*/"<<std::endl;
        break;
    default:
        break;
    }            

    if (_globalStateSet.valid()) cullVisitor->pushStateSet(_globalStateSet.get());
    if (_localStateSet.valid()) cullVisitor->pushStateSet(_localStateSet.get());


    cullVisitor->pushViewport(viewport);
    cullVisitor->pushProjectionMatrix(proj.get());
    cullVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);
    

    // traverse the scene graph to generate the rendergraph.
    for(unsigned int childNo=0;
        childNo<_camera->getNumChildren();
        ++childNo)
    {
        _camera->getChild(childNo)->accept(*cullVisitor);
    }

    cullVisitor->popModelViewMatrix();
    cullVisitor->popProjectionMatrix();
    cullVisitor->popViewport();

    if (_localStateSet.valid()) cullVisitor->popStateSet();
    if (_globalStateSet.valid()) cullVisitor->popStateSet();
    

    renderStage->sort();

    // prune out any empty StateGraph children.
    // note, this would be not required if the rendergraph had been
    // reset at the start of each frame (see top of this method) but
    // a clean has been used instead to try to minimize the amount of
    // allocation and deleteing of the StateGraph nodes.
    rendergraph->prune();
    
    // set the number of dynamic objects in the scene.    
    _dynamicObjectCount += renderStage->computeNumberOfDynamicRenderLeaves();


    bool computeNearFar = (cullVisitor->getComputeNearFarMode()!=osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR) && getSceneData()!=0;
    return computeNearFar;
}

void SceneView::releaseAllGLObjects()
{
    if (!_camera) return;
   
    _camera->releaseGLObjects(_renderInfo.getState());
    
    // we need to reset State as it keeps handles to Program objects.
    if (_renderInfo.getState()) _renderInfo.getState()->reset();
}


void SceneView::flushAllDeletedGLObjects()
{
    _requiresFlush = false;
    
    osg::flushAllDeletedGLObjects(getState()->getContextID());
 }

void SceneView::flushDeletedGLObjects(double& availableTime)
{
    osg::State* state = _renderInfo.getState();

    _requiresFlush = false;

    double currentTime = state->getFrameStamp()?state->getFrameStamp()->getReferenceTime():0.0;

    osg::flushDeletedGLObjects(getState()->getContextID(), currentTime, availableTime);
}

void SceneView::draw()
{
    if (_camera->getNodeMask()==0) return;

    osg::State* state = _renderInfo.getState();
    state->initializeExtensionProcs();

    osg::Texture::TextureObjectManager* tom = osg::Texture::getTextureObjectManager(state->getContextID());
    tom->newFrame(state->getFrameStamp());

    osg::GLBufferObjectManager::GLBufferObjectManager* bom = osg::GLBufferObjectManager::getGLBufferObjectManager(state->getContextID());
    bom->newFrame(state->getFrameStamp());

    if (!_initCalled) init();

    // note, to support multi-pipe systems the deletion of OpenGL display list
    // and texture objects is deferred until the OpenGL context is the correct
    // context for when the object were originally created.  Here we know what
    // context we are in so can flush the appropriate caches.
    
    if (_requiresFlush)
    {
        double availableTime = 0.005;
        flushDeletedGLObjects(availableTime);<commit_msg>Removed erroneous inclusion of extra GLBufferObjectManager qualifier that broke Windows build<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 <NAME> 
 *
 * 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 <osgUtil/SceneView>
#include <osgUtil/UpdateVisitor>
#include <osgUtil/GLObjectsVisitor>

#include <osg/Timer>
#include <osg/GLExtensions>
#include <osg/GLObjects>
#include <osg/Notify>
#include <osg/Texture>
#include <osg/AlphaFunc>
#include <osg/TexEnv>
#include <osg/ColorMatrix>
#include <osg/LightModel>
#include <osg/CollectOccludersVisitor>

#include <osg/GLU>

using namespace osg;
using namespace osgUtil;

static const GLubyte patternVertEven[] = {
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};

static const GLubyte patternVertOdd[] = {
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};

static const GLubyte patternHorzEven[] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};

// 32 x 32 bit array every row is a horizontal line of pixels
//  and the (bitwise) columns a vertical line 
//  The following is a checkerboard pattern
static const GLubyte patternCheckerboard[] = {
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA,
    0x55, 0x55, 0x55, 0x55,
    0xAA, 0xAA, 0xAA, 0xAA};

SceneView::SceneView(DisplaySettings* ds)
{
    _displaySettings = ds;

    _fusionDistanceMode = PROPORTIONAL_TO_SCREEN_DISTANCE;
    _fusionDistanceValue = 1.0f;

    _lightingMode=NO_SCENEVIEW_LIGHT;
    
    _prioritizeTextures = false;
    
    setCamera(new Camera);
    _camera->setViewport(new Viewport);
    _camera->setClearColor(osg::Vec4(0.2f, 0.2f, 0.4f, 1.0f));
    
    _initCalled = false;

    _camera->setDrawBuffer(GL_BACK);

    _requiresFlush = true;
    
    _activeUniforms = DEFAULT_UNIFORMS;
    
    _previousFrameTime = 0;
    _previousSimulationTime = 0;
    
    _redrawInterlacedStereoStencilMask = true;
    _interlacedStereoStencilWidth = 0;
    _interlacedStereoStencilHeight = 0;

    _dynamicObjectCount = 0;
}

SceneView::SceneView(const SceneView& rhs, const osg::CopyOp& copyop):
    osg::Object(rhs,copyop),
    osg::CullSettings(rhs)
{
    _displaySettings = rhs._displaySettings;

    _fusionDistanceMode = rhs._fusionDistanceMode;
    _fusionDistanceValue = rhs._fusionDistanceValue;

    _lightingMode = rhs._lightingMode;
    
    _prioritizeTextures = rhs._prioritizeTextures;
    
    _camera = rhs._camera;
    _cameraWithOwnership = rhs._cameraWithOwnership;
    
    _initCalled = false;

    _requiresFlush = rhs._requiresFlush;
    
    _activeUniforms = rhs._activeUniforms;
    
    _previousFrameTime = 0;
    _previousSimulationTime = 0;
    
    _redrawInterlacedStereoStencilMask = rhs._redrawInterlacedStereoStencilMask;
    _interlacedStereoStencilWidth = rhs._interlacedStereoStencilWidth;
    _interlacedStereoStencilHeight = rhs._interlacedStereoStencilHeight;

    _dynamicObjectCount = 0;
}

SceneView::~SceneView()
{
}


void SceneView::setDefaults(unsigned int options)
{
    osg::CullSettings::setDefaults();

    _camera->getProjectionMatrix().makePerspective(50.0f,1.4f,1.0f,10000.0f);
    _camera->getViewMatrix().makeIdentity();

    if (!_globalStateSet) _globalStateSet = new osg::StateSet;
    else _globalStateSet->clear();

    if ((options & HEADLIGHT) || (options & SKY_LIGHT))
    {
        _lightingMode=(options&HEADLIGHT) ? HEADLIGHT : SKY_LIGHT;
        _light = new osg::Light;
        _light->setLightNum(0);
        _light->setAmbient(Vec4(0.00f,0.0f,0.00f,1.0f));
        _light->setDiffuse(Vec4(0.8f,0.8f,0.8f,1.0f));
        _light->setSpecular(Vec4(1.0f,1.0f,1.0f,1.0f));

        _globalStateSet->setAssociatedModes(_light.get(),osg::StateAttribute::ON);

        osg::LightModel* lightmodel = new osg::LightModel;
        lightmodel->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f));
        _globalStateSet->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);

        // enable lighting by default.
        _globalStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);

    }
    else
    {
        _lightingMode = NO_SCENEVIEW_LIGHT;
    }
 
    _renderInfo.setState(new State);
    
    _stateGraph = new StateGraph;
    _renderStage = new RenderStage;


    if (options & COMPILE_GLOBJECTS_AT_INIT)
    {
        GLObjectsVisitor::Mode  dlvMode = GLObjectsVisitor::COMPILE_DISPLAY_LISTS |
                                          GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES | 
                                          GLObjectsVisitor::CHECK_BLACK_LISTED_MODES;

    #ifdef __sgi
        dlvMode = GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES;
    #endif

        // sgi's IR graphics has a problem with lighting and display lists, as it seems to store 
        // lighting state with the display list, and the display list visitor doesn't currently apply
        // state before creating display lists. So will disable the init visitor default, this won't
        // affect functionality since the display lists will be created as and when needed.
        GLObjectsVisitor* dlv = new GLObjectsVisitor(dlvMode);
        dlv->setNodeMaskOverride(0xffffffff);
        _initVisitor = dlv;

    }
    
    _updateVisitor = new UpdateVisitor;

    _cullVisitor = CullVisitor::create();

    _cullVisitor->setStateGraph(_stateGraph.get());
    _cullVisitor->setRenderStage(_renderStage.get());

    _globalStateSet->setGlobalDefaults();

    // set up an texture environment by default to speed up blending operations.
     osg::TexEnv* texenv = new osg::TexEnv;
     texenv->setMode(osg::TexEnv::MODULATE);
     _globalStateSet->setTextureAttributeAndModes(0,texenv, osg::StateAttribute::ON);

    _camera->setClearColor(osg::Vec4(0.2f, 0.2f, 0.4f, 1.0f));
}

void SceneView::setCamera(osg::Camera* camera, bool assumeOwnershipOfCamera)
{
    if (camera)
    {
        _camera = camera;
    }
    else
    {
        osg::notify(osg::NOTICE)<<"Warning: attempt to assign a NULL camera to SceneView not permitted."<<std::endl;
    }
    
    if (assumeOwnershipOfCamera)
    {
        _cameraWithOwnership = _camera.get();
    }
    else
    {
        _cameraWithOwnership = 0;
    }
}

void SceneView::setSceneData(osg::Node* node)
{
    // take a temporary reference to node to prevent the possibility
    // of it getting deleted when when we do the camera clear of children. 
    osg::ref_ptr<osg::Node> temporaryRefernce = node;
    
    // remove pre existing children
    _camera->removeChildren(0, _camera->getNumChildren());
    
    // add the new one in.
    _camera->addChild(node);
}

void SceneView::init()
{
    _initCalled = true;

    // force the initialization of the OpenGL extension string
    // to try and work around a Windows NVidia driver bug circa Oct 2006.
    osg::isGLExtensionSupported(_renderInfo.getState()->getContextID(),"");

    if (_camera.valid() && _initVisitor.valid())
    {
        _initVisitor->reset();
        _initVisitor->setFrameStamp(_frameStamp.get());
        
        GLObjectsVisitor* dlv = dynamic_cast<GLObjectsVisitor*>(_initVisitor.get());
        if (dlv) dlv->setState(_renderInfo.getState());
        
        if (_frameStamp.valid())
        {
             _initVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }
        
        _camera->accept(*_initVisitor.get());
        
    } 
}

void SceneView::update()
{
    if (_camera.valid() && _updateVisitor.valid())
    { 
        _updateVisitor->reset();

        _updateVisitor->setFrameStamp(_frameStamp.get());

        // use the frame number for the traversal number.
        if (_frameStamp.valid())
        {
             _updateVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }
        
        _camera->accept(*_updateVisitor.get());
        
        // now force a recompute of the bounding volume while we are still in
        // the read/write app phase, this should prevent the need to recompute
        // the bounding volumes from within the cull traversal which may be
        // multi-threaded.
        _camera->getBound();
    }
}

void SceneView::updateUniforms()
{
    if (!_localStateSet)
    {
        _localStateSet = new osg::StateSet;
    }

    if (!_localStateSet) return;
    
    if ((_activeUniforms & FRAME_NUMBER_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_FrameNumber",osg::Uniform::INT);
        uniform->set(_frameStamp->getFrameNumber());        
    }
    
    if ((_activeUniforms & FRAME_TIME_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_FrameTime",osg::Uniform::FLOAT);
        uniform->set(static_cast<float>(_frameStamp->getReferenceTime()));
    }
    
    if ((_activeUniforms & DELTA_FRAME_TIME_UNIFORM) && _frameStamp.valid())
    {
        float delta_frame_time = (_previousFrameTime != 0.0) ? static_cast<float>(_frameStamp->getReferenceTime()-_previousFrameTime) : 0.0f;
        _previousFrameTime = _frameStamp->getReferenceTime();
        
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_DeltaFrameTime",osg::Uniform::FLOAT);
        uniform->set(delta_frame_time);
    }
    
    if ((_activeUniforms & SIMULATION_TIME_UNIFORM) && _frameStamp.valid())
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_SimulationTime",osg::Uniform::FLOAT);
        uniform->set(static_cast<float>(_frameStamp->getSimulationTime()));
    }
    
    if ((_activeUniforms & DELTA_SIMULATION_TIME_UNIFORM) && _frameStamp.valid())
    {
        float delta_simulation_time = (_previousSimulationTime != 0.0) ? static_cast<float>(_frameStamp->getSimulationTime()-_previousSimulationTime) : 0.0f;
        _previousSimulationTime = _frameStamp->getSimulationTime();
        
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_DeltaSimulationTime",osg::Uniform::FLOAT);
        uniform->set(delta_simulation_time);
    }
    
    if (_activeUniforms & VIEW_MATRIX_UNIFORM)
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_ViewMatrix",osg::Uniform::FLOAT_MAT4);
        uniform->set(getViewMatrix());
    }

    if (_activeUniforms & VIEW_MATRIX_INVERSE_UNIFORM)
    {
        osg::Uniform* uniform = _localStateSet->getOrCreateUniform("osg_ViewMatrixInverse",osg::Uniform::FLOAT_MAT4);
        uniform->set(osg::Matrix::inverse(getViewMatrix()));
    }

}

osg::Matrixd SceneView::computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const
{
    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double scale_x = 1.0;
    double scale_y = 1.0;

    if (_displaySettings->getSplitStereoAutoAdjustAspectRatio())
    {
        switch(_displaySettings->getStereoMode())
        {
            case(osg::DisplaySettings::HORIZONTAL_SPLIT):
                scale_x = 2.0;
                break;
            case(osg::DisplaySettings::VERTICAL_SPLIT):
                scale_y = 2.0;
                break;
            default:
                break;
        }
    }

    if (_displaySettings->getDisplayType()==osg::DisplaySettings::HEAD_MOUNTED_DISPLAY)
    {
        // head mounted display has the same projection matrix for left and right eyes.
        return osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
    else
    {
        // all other display types assume working like a projected power wall
        // need to shjear projection matrix to account for asymetric frustum due to eye offset.
        return osg::Matrixd(1.0,0.0,0.0,0.0,
                           0.0,1.0,0.0,0.0,
                           iod/(2.0*sd),0.0,1.0,0.0,
                           0.0,0.0,0.0,1.0) *
               osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
}

osg::Matrixd SceneView::computeLeftEyeViewImplementation(const osg::Matrixd& view) const
{
    double fusionDistance = _displaySettings->getScreenDistance();
    switch(_fusionDistanceMode)
    {
        case(USE_FUSION_DISTANCE_VALUE):
            fusionDistance = _fusionDistanceValue;
            break;
        case(PROPORTIONAL_TO_SCREEN_DISTANCE):
            fusionDistance *= _fusionDistanceValue;
            break;
    }

    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double es = 0.5f*iod*(fusionDistance/sd);

    return view *
           osg::Matrixd(1.0,0.0,0.0,0.0,
                       0.0,1.0,0.0,0.0,
                       0.0,0.0,1.0,0.0,
                       es,0.0,0.0,1.0);
}

osg::Matrixd SceneView::computeRightEyeProjectionImplementation(const osg::Matrixd& projection) const
{
    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double scale_x = 1.0;
    double scale_y = 1.0;

    if (_displaySettings->getSplitStereoAutoAdjustAspectRatio())
    {
        switch(_displaySettings->getStereoMode())
        {
            case(osg::DisplaySettings::HORIZONTAL_SPLIT):
                scale_x = 2.0;
                break;
            case(osg::DisplaySettings::VERTICAL_SPLIT):
                scale_y = 2.0;
                break;
            default:
                break;
        }
    }

    if (_displaySettings->getDisplayType()==osg::DisplaySettings::HEAD_MOUNTED_DISPLAY)
    {
        // head mounted display has the same projection matrix for left and right eyes.
        return osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
    else
    {
        // all other display types assume working like a projected power wall
        // need to shjear projection matrix to account for asymetric frustum due to eye offset.
        return osg::Matrixd(1.0,0.0,0.0,0.0,
                           0.0,1.0,0.0,0.0,
                           -iod/(2.0*sd),0.0,1.0,0.0,
                           0.0,0.0,0.0,1.0) *
               osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
}

osg::Matrixd SceneView::computeRightEyeViewImplementation(const osg::Matrixd& view) const
{
    float fusionDistance = _displaySettings->getScreenDistance();
    switch(_fusionDistanceMode)
    {
        case(USE_FUSION_DISTANCE_VALUE):
            fusionDistance = _fusionDistanceValue;
            break;
        case(PROPORTIONAL_TO_SCREEN_DISTANCE):
            fusionDistance *= _fusionDistanceValue;
            break;
    }

    double iod = _displaySettings->getEyeSeparation();
    double sd = _displaySettings->getScreenDistance();
    double es = 0.5*iod*(fusionDistance/sd);

    return view *
           osg::Matrixd(1.0,0.0,0.0,0.0,
                       0.0,1.0,0.0,0.0,
                       0.0,0.0,1.0,0.0,
                       -es,0.0,0.0,1.0);
}

void SceneView::computeLeftEyeViewport(const osg::Viewport *viewport)
{
    if(!viewport) return;

    if(!_viewportLeft.valid()) _viewportLeft = new osg::Viewport;

    switch(_displaySettings->getStereoMode())
    {

        case(osg::DisplaySettings::HORIZONTAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoHorizontalSeparation();

            if (_displaySettings->getSplitStereoHorizontalEyeMapping()==osg::DisplaySettings::LEFT_EYE_LEFT_VIEWPORT)
            {
                Viewport::value_type left_half_width = (viewport->width()-separation)/2;
                _viewportLeft->setViewport(viewport->x(),viewport->y(),left_half_width,viewport->height());
            }
            else
            {
                Viewport::value_type right_half_begin = (viewport->width()+separation)/2;
                Viewport::value_type right_half_width = viewport->width()-right_half_begin;
                _viewportLeft->setViewport(viewport->x()+right_half_begin,viewport->y(),right_half_width,viewport->height());
            }
        }
        break;

        case(osg::DisplaySettings::VERTICAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoVerticalSeparation();

            if (_displaySettings->getSplitStereoVerticalEyeMapping()==osg::DisplaySettings::LEFT_EYE_TOP_VIEWPORT)
            {
                Viewport::value_type top_half_begin = (viewport->height()+separation)/2;
                Viewport::value_type top_half_height = viewport->height()-top_half_begin;
                _viewportLeft->setViewport(viewport->x(),viewport->y()+top_half_begin,viewport->width(),top_half_height);
            }
            else
            {
                Viewport::value_type bottom_half_height = (viewport->height()-separation)/2;
                _viewportLeft->setViewport(viewport->x(),viewport->y(),viewport->width(),bottom_half_height);
            }
        }
        break;

        default:
            _viewportLeft->setViewport(viewport->x(),viewport->y(),viewport->width(),viewport->height());
            break;
    }
}

void SceneView::computeRightEyeViewport(const osg::Viewport *viewport)
{
    if(!viewport) return;

    if(!_viewportRight.valid()) _viewportRight = new osg::Viewport;

    switch(_displaySettings->getStereoMode())
    {
        case(osg::DisplaySettings::HORIZONTAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoHorizontalSeparation();

            if (_displaySettings->getSplitStereoHorizontalEyeMapping()!=osg::DisplaySettings::LEFT_EYE_LEFT_VIEWPORT)
            {
                Viewport::value_type left_half_width = (viewport->width()-separation)/2;
                _viewportRight->setViewport(viewport->x(),viewport->y(),left_half_width,viewport->height());
            }
            else
            {
                Viewport::value_type right_half_begin = (viewport->width()+separation)/2;
                Viewport::value_type right_half_width = viewport->width()-right_half_begin;
                _viewportRight->setViewport(viewport->x()+right_half_begin,viewport->y(),right_half_width,viewport->height());
            }
        }
        break;

        case(osg::DisplaySettings::VERTICAL_SPLIT):
        {
            Viewport::value_type separation = _displaySettings->getSplitStereoVerticalSeparation();

            if (_displaySettings->getSplitStereoVerticalEyeMapping()!=osg::DisplaySettings::LEFT_EYE_TOP_VIEWPORT)
            {
                Viewport::value_type top_half_begin = (viewport->height()+separation)/2;
                Viewport::value_type top_half_height = viewport->height()-top_half_begin;
                _viewportRight->setViewport(viewport->x(),viewport->y()+top_half_begin,viewport->width(),top_half_height);
            }
            else
            {
                Viewport::value_type bottom_half_height = (viewport->height()-separation)/2;
                _viewportRight->setViewport(viewport->x(),viewport->y(),viewport->width(),bottom_half_height);
            }
        }
        break;

        default:
            _viewportRight->setViewport(viewport->x(),viewport->y(),viewport->width(),viewport->height());
            break;
    }
}

void SceneView::setLightingMode(LightingMode mode)
{
    if (mode==_lightingMode) return;
    
    if (_lightingMode!=NO_SCENEVIEW_LIGHT)
    {
        // remove GL_LIGHTING mode
        _globalStateSet->removeMode(GL_LIGHTING);

        if (_light.valid())
        {
            _globalStateSet->removeAssociatedModes(_light.get());
        }

    }

    _lightingMode = mode;

    if (_lightingMode!=NO_SCENEVIEW_LIGHT)
    {
        // add GL_LIGHTING mode
        _globalStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
        if (_light.valid()) 
        {
            _globalStateSet->setAssociatedModes(_light.get(), osg::StateAttribute::ON);
        }
    }
}

void SceneView::inheritCullSettings(const osg::CullSettings& settings, unsigned int inheritanceMask)
{
    if (_camera.valid() && _camera->getView()) 
    {
        if (inheritanceMask & osg::CullSettings::LIGHTING_MODE)
        {
            LightingMode newLightingMode = _lightingMode;
        
            switch(_camera->getView()->getLightingMode())
            {
                case(osg::View::NO_LIGHT): newLightingMode = NO_SCENEVIEW_LIGHT; break;
                case(osg::View::HEADLIGHT): newLightingMode = HEADLIGHT; break;
                case(osg::View::SKY_LIGHT): newLightingMode = SKY_LIGHT; break;
            }
            
            if (newLightingMode != _lightingMode)
            {
                setLightingMode(newLightingMode);
            }
        }
                
        if (inheritanceMask & osg::CullSettings::LIGHT)
        {
            setLight(_camera->getView()->getLight());
        }
    }
    
    osg::CullSettings::inheritCullSettings(settings, inheritanceMask);
}


void SceneView::cull()
{
    _dynamicObjectCount = 0;

    if (_camera->getNodeMask()==0) return;

    _renderInfo.setView(_camera->getView());

    // update the active uniforms
    updateUniforms();

    if (!_renderInfo.getState())
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView::_state attached, creating a default state automatically."<< std::endl;

        // note the constructor for osg::State will set ContextID to 0 which will be fine to single context graphics
        // applications which is ok for most apps, but not multiple context/pipe applications.
        _renderInfo.setState(new osg::State);
    }
    
    osg::State* state = _renderInfo.getState();

    if (!_localStateSet)
    {
        _localStateSet = new osg::StateSet;
    }
    
    // we in theory should be able to be able to bypass reset, but we'll call it just incase.
    //_state->reset();
   
    state->setFrameStamp(_frameStamp.get());
    state->setDisplaySettings(_displaySettings.get());


    if (!_cullVisitor)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView:: attached, creating a default CullVisitor automatically."<< std::endl;
        _cullVisitor = CullVisitor::create();
    }
    if (!_stateGraph)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView:: attached, creating a global default StateGraph automatically."<< std::endl;
        _stateGraph = new StateGraph;
    }
    if (!_renderStage)
    {
        osg::notify(osg::INFO) << "Warning: no valid osgUtil::SceneView::_renderStage attached, creating a default RenderStage automatically."<< std::endl;
        _renderStage = new RenderStage;
    }

    if (_displaySettings.valid() && _displaySettings->getStereo()) 
    {

        if (_displaySettings->getStereoMode()==osg::DisplaySettings::LEFT_EYE)
        {
            // set up the left eye.
            _cullVisitor->setTraversalMask(_cullMaskLeft);
            computeLeftEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeLeftEyeProjection(getProjectionMatrix()),computeLeftEyeView(getViewMatrix()),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),_viewportLeft.get());

            if (computeNearFar)
            {
                CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
                CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }
        else if (_displaySettings->getStereoMode()==osg::DisplaySettings::RIGHT_EYE)
        {
            // set up the right eye.
            _cullVisitor->setTraversalMask(_cullMaskRight);
            computeRightEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeRightEyeProjection(getProjectionMatrix()),computeRightEyeView(getViewMatrix()),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),_viewportRight.get());

            if (computeNearFar)
            {
                CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
                CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }
        else
        {

            if (!_cullVisitorLeft.valid()) _cullVisitorLeft = dynamic_cast<CullVisitor*>(_cullVisitor->clone());
            if (!_stateGraphLeft.valid()) _stateGraphLeft = dynamic_cast<StateGraph*>(_stateGraph->cloneType());
            if (!_renderStageLeft.valid()) _renderStageLeft = dynamic_cast<RenderStage*>(_renderStage->clone(osg::CopyOp::DEEP_COPY_ALL));

            if (!_cullVisitorRight.valid()) _cullVisitorRight = dynamic_cast<CullVisitor*>(_cullVisitor->clone());
            if (!_stateGraphRight.valid()) _stateGraphRight = dynamic_cast<StateGraph*>(_stateGraph->cloneType());
            if (!_renderStageRight.valid()) _renderStageRight = dynamic_cast<RenderStage*>(_renderStage->clone(osg::CopyOp::DEEP_COPY_ALL));
            
            _cullVisitorLeft->setDatabaseRequestHandler(_cullVisitor->getDatabaseRequestHandler());
            _cullVisitorLeft->setClampProjectionMatrixCallback(_cullVisitor->getClampProjectionMatrixCallback());
            _cullVisitorLeft->setTraversalMask(_cullMaskLeft);
            computeLeftEyeViewport(getViewport());
            bool computeNearFar = cullStage(computeLeftEyeProjection(getProjectionMatrix()),computeLeftEyeView(getViewMatrix()),_cullVisitorLeft.get(),_stateGraphLeft.get(),_renderStageLeft.get(),_viewportLeft.get());


            // set up the right eye.
            _cullVisitorRight->setDatabaseRequestHandler(_cullVisitor->getDatabaseRequestHandler());
            _cullVisitorRight->setClampProjectionMatrixCallback(_cullVisitor->getClampProjectionMatrixCallback());
            _cullVisitorRight->setTraversalMask(_cullMaskRight);
            computeRightEyeViewport(getViewport());
            computeNearFar = cullStage(computeRightEyeProjection(getProjectionMatrix()),computeRightEyeView(getViewMatrix()),_cullVisitorRight.get(),_stateGraphRight.get(),_renderStageRight.get(),_viewportRight.get());
           
            if (computeNearFar)
            {
                CullVisitor::value_type zNear = osg::minimum(_cullVisitorLeft->getCalculatedNearPlane(),_cullVisitorRight->getCalculatedNearPlane());
                CullVisitor::value_type zFar =  osg::maximum(_cullVisitorLeft->getCalculatedFarPlane(),_cullVisitorRight->getCalculatedFarPlane());
                _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
            }

        }

    }
    else
    {

        _cullVisitor->setTraversalMask(_cullMask);
        bool computeNearFar = cullStage(getProjectionMatrix(),getViewMatrix(),_cullVisitor.get(),_stateGraph.get(),_renderStage.get(),getViewport());

        if (computeNearFar)
        {
            CullVisitor::value_type zNear = _cullVisitor->getCalculatedNearPlane();
            CullVisitor::value_type zFar = _cullVisitor->getCalculatedFarPlane();
            _cullVisitor->clampProjectionMatrix(getProjectionMatrix(),zNear,zFar);
        }
    }
    


}

bool SceneView::cullStage(const osg::Matrixd& projection,const osg::Matrixd& modelview,osgUtil::CullVisitor* cullVisitor, osgUtil::StateGraph* rendergraph, osgUtil::RenderStage* renderStage, osg::Viewport *viewport)
{

    if (!_camera || !viewport) return false;

    osg::ref_ptr<RefMatrix> proj = new osg::RefMatrix(projection);
    osg::ref_ptr<RefMatrix> mv = new osg::RefMatrix(modelview);

    // collect any occluder in the view frustum.
    if (_camera->containsOccluderNodes())
    {
        //std::cout << "Scene graph contains occluder nodes, searching for them"<<std::endl;
        
        
        if (!_collectOccludersVisitor) _collectOccludersVisitor = new osg::CollectOccludersVisitor;
        
        _collectOccludersVisitor->inheritCullSettings(*this);
        
        _collectOccludersVisitor->reset();
        
        _collectOccludersVisitor->setFrameStamp(_frameStamp.get());

        // use the frame number for the traversal number.
        if (_frameStamp.valid())
        {
             _collectOccludersVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
        }

        _collectOccludersVisitor->pushViewport(viewport);
        _collectOccludersVisitor->pushProjectionMatrix(proj.get());
        _collectOccludersVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);

        // traverse the scene graph to search for occluder in there new positions.
        for(unsigned int i=0; i< _camera->getNumChildren(); ++i)
        {
            _camera->getChild(i)->accept(*_collectOccludersVisitor);
        }

        _collectOccludersVisitor->popModelViewMatrix();
        _collectOccludersVisitor->popProjectionMatrix();
        _collectOccludersVisitor->popViewport();
        
        // sort the occluder from largest occluder volume to smallest.
        _collectOccludersVisitor->removeOccludedOccluders();
        
        
        osg::notify(osg::DEBUG_INFO) << "finished searching for occluder - found "<<_collectOccludersVisitor->getCollectedOccluderSet().size()<<std::endl;
           
        cullVisitor->getOccluderList().clear();
        std::copy(_collectOccludersVisitor->getCollectedOccluderSet().begin(),_collectOccludersVisitor->getCollectedOccluderSet().end(), std::back_insert_iterator<CullStack::OccluderList>(cullVisitor->getOccluderList()));
    }
    


    cullVisitor->reset();

    cullVisitor->setFrameStamp(_frameStamp.get());

    // use the frame number for the traversal number.
    if (_frameStamp.valid())
    {
         cullVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
    }

    cullVisitor->inheritCullSettings(*this);

    cullVisitor->setStateGraph(rendergraph);
    cullVisitor->setRenderStage(renderStage);

    cullVisitor->setRenderInfo( _renderInfo );

    renderStage->reset();

    // comment out reset of rendergraph since clean is more efficient.
    //  rendergraph->reset();

    // use clean of the rendergraph rather than reset, as it is able to
    // reuse the structure on the rendergraph in the next frame. This
    // achieves a certain amount of frame cohereancy of memory allocation.
    rendergraph->clean();

    renderStage->setViewport(viewport);
    renderStage->setClearColor(_camera->getClearColor());
    renderStage->setClearDepth(_camera->getClearDepth());
    renderStage->setClearAccum(_camera->getClearAccum());
    renderStage->setClearStencil(_camera->getClearStencil());
    renderStage->setClearMask(_camera->getClearMask());
    
#if 1    
    renderStage->setCamera(_camera.get());
#endif

    switch(_lightingMode)
    {
    case(HEADLIGHT):
        if (_light.valid()) renderStage->addPositionedAttribute(NULL,_light.get());
        else osg::notify(osg::WARN)<<"Warning: no osg::Light attached to ogUtil::SceneView to provide head light.*/"<<std::endl;
        break;
    case(SKY_LIGHT):
        if (_light.valid()) renderStage->addPositionedAttribute(mv.get(),_light.get());
        else osg::notify(osg::WARN)<<"Warning: no osg::Light attached to ogUtil::SceneView to provide sky light.*/"<<std::endl;
        break;
    default:
        break;
    }            

    if (_globalStateSet.valid()) cullVisitor->pushStateSet(_globalStateSet.get());
    if (_localStateSet.valid()) cullVisitor->pushStateSet(_localStateSet.get());


    cullVisitor->pushViewport(viewport);
    cullVisitor->pushProjectionMatrix(proj.get());
    cullVisitor->pushModelViewMatrix(mv.get(),osg::Transform::ABSOLUTE_RF);
    

    // traverse the scene graph to generate the rendergraph.
    for(unsigned int childNo=0;
        childNo<_camera->getNumChildren();
        ++childNo)
    {
        _camera->getChild(childNo)->accept(*cullVisitor);
    }

    cullVisitor->popModelViewMatrix();
    cullVisitor->popProjectionMatrix();
    cullVisitor->popViewport();

    if (_localStateSet.valid()) cullVisitor->popStateSet();
    if (_globalStateSet.valid()) cullVisitor->popStateSet();
    

    renderStage->sort();

    // prune out any empty StateGraph children.
    // note, this would be not required if the rendergraph had been
    // reset at the start of each frame (see top of this method) but
    // a clean has been used instead to try to minimize the amount of
    // allocation and deleteing of the StateGraph nodes.
    rendergraph->prune();
    
    // set the number of dynamic objects in the scene.    
    _dynamicObjectCount += renderStage->computeNumberOfDynamicRenderLeaves();


    bool computeNearFar = (cullVisitor->getComputeNearFarMode()!=osgUtil::CullVisitor::DO_NOT_COMPUTE_NEAR_FAR) && getSceneData()!=0;
    return computeNearFar;
}

void SceneView::releaseAllGLObjects()
{
    if (!_camera) return;
   
    _camera->releaseGLObjects(_renderInfo.getState());
    
    // we need to reset State as it keeps handles to Program objects.
    if (_renderInfo.getState()) _renderInfo.getState()->reset();
}


void SceneView::flushAllDeletedGLObjects()
{
    _requiresFlush = false;
    
    osg::flushAllDeletedGLObjects(getState()->getContextID());
 }

void SceneView::flushDeletedGLObjects(double& availableTime)
{
    osg::State* state = _renderInfo.getState();

    _requiresFlush = false;

    double currentTime = state->getFrameStamp()?state->getFrameStamp()->getReferenceTime():0.0;

    osg::flushDeletedGLObjects(getState()->getContextID(), currentTime, availableTime);
}

void SceneView::draw()
{
    if (_camera->getNodeMask()==0) return;

    osg::State* state = _renderInfo.getState();
    state->initializeExtensionProcs();

    osg::Texture::TextureObjectManager* tom = osg::Texture::getTextureObjectManager(state->getContextID());
    tom->newFrame(state->getFrameStamp());

    osg::GLBufferObjectManager* bom = osg::GLBufferObjectManager::getGLBufferObjectManager(state->getContextID());
    bom->newFrame(state->getFrameStamp());

    if (!_initCalled) init();

    // note, to support multi-pipe systems the deletion of OpenGL display list
    // and texture objects is deferred until the OpenGL context is the correct
    // context for when the object were originally created.  Here we know what
    // context we are in so can flush the appropriate caches.
    
    if (_requiresFlush)
    {
        double availableTime = 0.005;
        flushDeletedGLObjects(availableTime);
{'fraction_non_alphanumeric': 0.11906238045614637, 'fraction_numerical': 0.034560540305240806, 'mean_word_length': 3.051194356864212, 'pattern_counts': {'":': 0, '<': 143, '<?xml version=': 0, '>': 595, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4791711', 'n_tokens_mistral': 27057, 'n_tokens_neox': 24778, 'n_words': 4493}