#include "TransparencyGlitchFixNode.h" #include #include #include #include // ---------------------------------------------------------------------------- /*! * \par Actions: * - Creates one state-set that will be used multiple places. * - Creates two helper-bins to be ready for use (see traverse function). */ TransparencyGlitchFixNode::TransparencyGlitchFixNode() : osg::Group () , _stateSet ( 0 ) { // Create the state-set that we use to turn off depth-buffer write _stateSet = new osg::StateSet(); _stateSet->ref(); _stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); _stateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LESS, 0.0, 1.0, false), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); // Create the helper bins _listHelperBin.clear(); _listHelperBin.push_back( createHelperBin() ); _listHelperBin.push_back( createHelperBin() ); _nextHelperBin = _listHelperBin.begin(); // Extra state-set settings to highlight the first render-pass (to ease visual debugging) #if 1 // This enables first pass rendered to use additive blend osg::BlendFunc* blendFunc = new osg::BlendFunc( osg::BlendFunc::ONE , osg::BlendFunc::ONE ); _stateSet->setAttributeAndModes( blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); #endif } // ---------------------------------------------------------------------------- /*! * \par Actions: * - Calls unref on all objects this class has referenced. */ TransparencyGlitchFixNode::~TransparencyGlitchFixNode( void ) { // Release the helper-bins for( _nextHelperBin = _listHelperBin.begin(); _nextHelperBin != _listHelperBin.end(); ++_nextHelperBin ) { // Release the sub-bin ((*_nextHelperBin)->getRenderBinList())[9]->unref(); // Release the helper-bin (*_nextHelperBin)->unref(); } _listHelperBin.clear(); // Release the state-set _stateSet->unref(); } // ---------------------------------------------------------------------------- /*! * \return Pointer to new render-bin. * \par Actions: * - Creates a render-bin which contans a sub render-bin. * - Sub render-bin use the _stateSet. * * The sub-bin is located in the new render-bin's RenderBinList as element 9. */ osgUtil::RenderBin* TransparencyGlitchFixNode::createHelperBin() { osgUtil::RenderBin* newBin = new osgUtil::RenderBin(); osgUtil::RenderBin* subBin = new osgUtil::RenderBin(); newBin->ref(); subBin->ref(); subBin->setStateSet( _stateSet ); (newBin->getRenderBinList())[9] = subBin; // This is the setup: // // (newBin) // | // +---(RenderBinList[9]) = (subBin) // return newBin; } // ---------------------------------------------------------------------------- /*! * \param nv : The node visitor. * \par Actions: * - Runs the traverse. * - Looks for render-bin 10 in the result. * - Ensures that render-bin 10 is rendered twice: * - Once with depth-buffer write turned off. * - Once with depth-buffer write turned on. * */ void TransparencyGlitchFixNode::traverse( osg::NodeVisitor& nv ) { // Traverse children osg::Group::traverse( nv ); // Fix rendering glitches by rendering bin 10 twice if( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR ) { osgUtil::CullVisitor* cv = dynamic_cast( &nv ); if( cv ) { // Act if we have a RenderStage pointer if( osgUtil::RenderStage* renderStage = cv->getRenderStage() ) { // Get the RenderStage's render-bin list osgUtil::RenderBin::RenderBinList& renderStageBinList = renderStage->getRenderBinList(); // Only proceed if bin 10 is used if( renderStageBinList.find(10) != renderStageBinList.end() ) { // Find an available bin if( (*_nextHelperBin)->referenceCount() != 1 ) { // Traverse the list to look for a free one (refCount == 1). // We traverse the whole list one time. If none is found, then // we add a new entry to the list. // std::list::iterator targetBin = _nextHelperBin; while( 1 ) { // Move iterator one step ahead in list (wraps to start if at end) if( ++targetBin == _listHelperBin.end() ) { targetBin = _listHelperBin.begin(); } // Did we find a free one? if( (*targetBin)->referenceCount() == 1 ) { break; } // Did we go a full circle? if( targetBin == _nextHelperBin ) { // Insert a new bin and use that one. // _nextHelperBin still points to the one that is most // probable to be available first, so we insert the new // one in front of that one. // targetBin = _listHelperBin.insert( targetBin, createHelperBin() ); break; } } _nextHelperBin = targetBin; } // We have the following pointed to by _nextHelperBin: // // Label N = Our new render-bin (*_nextHelperBin). // Label S = The sub-bin of our new render-bin. // Label R = Content pointed to from RenderStage's RenderBinList[10]. // // The original setup looks like this: // // (N) (New render bin) // | // +---(RenderBinList[9]) = (S) (New render bin's sub-bin) // // We shall set it up to render bin number 10 (from RenderStage) twice: // // (N) // | // +---(RenderBinList[9]) = (S) // | | // | +---(RenderBinList[10]) = (R) // | // +---(RenderBinList[10]) = (R) // // osgUtil::RenderBin::RenderBinList& newBinList = (*_nextHelperBin)->getRenderBinList(); osgUtil::RenderBin::RenderBinList& subBinList = newBinList[9]->getRenderBinList(); subBinList[10] = renderStageBinList[10]; newBinList[10] = renderStageBinList[10]; // RenderStage's RenderBinList[10] shall now point to // our render bin (N) instead of what it used to point to (R). // Remember that bin (R) is pointed to two times from (N). // // (RenderStage) // | // +---(RenderBinList[10]) = (N) // renderStageBinList[10] = *_nextHelperBin; // Advance to most probable free bin if( ++_nextHelperBin == _listHelperBin.end() ) { _nextHelperBin = _listHelperBin.begin(); } } } } } }