Hi Roger, Moving a class definition into a function implementation makes me nervous about the possibile consequence for older compilers handling this. I don't know whether it would be supported or not, but... given it's not a technique used in the rest the OSG I don't have a prior art to confirm that it's OK to roll out.
Would enclosing the class in a local namespace be sufficient? Robert. On Thu, Jan 22, 2009 at 2:23 PM, Roger James <[email protected]> wrote: > Robert, > > This fix moves a couple of debug callback functions that have the same name > from the global context into the local function context where they are used. > This avoids some obscure problems when they used in DLLs generated by MS > VC8.1. > > Roger > > /* -*-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. > * > * ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski > * Thanks to to my company http://www.ai.com.pl for allowing me free this > work. > */ > > #include <osgShadow/DebugShadowMap> > #include <osgShadow/ConvexPolyhedron> > #include <osgUtil/RenderLeaf> > #include <osg/Geometry> > #include <osg/PrimitiveSet> > #include <osg/MatrixTransform> > #include <osg/Depth> > #include <iostream> > #include <iomanip> > > using namespace osgShadow; > > > #define VECTOR_LENGTH( v ) ( sizeof(v)/sizeof(v[0]) ) > > #define DEFAULT_DEBUG_HUD_SIZE_X 256 > #define DEFAULT_DEBUG_HUD_SIZE_Y 256 > #define DEFAULT_DEBUG_HUD_ORIGIN_X 8 > #define DEFAULT_DEBUG_HUD_ORIGIN_Y 8 > > DebugShadowMap::DebugShadowMap(): > BaseClass(), > _hudSize( 2, 2 ), > _hudOrigin( -1, -1 ), > _viewportSize( DEFAULT_DEBUG_HUD_SIZE_X, DEFAULT_DEBUG_HUD_SIZE_Y ), > _viewportOrigin( DEFAULT_DEBUG_HUD_ORIGIN_X, DEFAULT_DEBUG_HUD_ORIGIN_Y > ), > _orthoSize( 2, 2 ), > _orthoOrigin( -1, -1 ), > _doDebugDraw( false ) > { > > // Why this fancy 24 bit depth to 24 bit rainbow colors shader ? > // > // Depth values cannot be easily cast on color component because they > are: > // a) 24 or 32 bit and we loose lots of precision when cast on 8 bit > // b) depth value distribution is non linear due to projection division > // when cast on componenent color there is usually very minor shade > variety > // and its often difficult to notice that there is anything in the buffer > // > // Shader looks complex but it is used only for debug head-up rectangle > // so performance impact is not significant. > > _depthColorFragmentShader = new osg::Shader( osg::Shader::FRAGMENT, > #if 0 > "uniform sampler2D texture; > \n" > " > \n" > "void main(void) > \n" > "{ > \n" > " float f = texture2D( texture, vec3( gl_TexCoord[0].xy, 1.0).xy > ).r; \n" > " gl_FragColor = vec4( 0.0, 1.0 - f, 0.5 - f, 0.5 ); > \n" > "} > \n" > #else > "uniform sampler2D texture; > \n" > " > \n" > "void main(void) > \n" > "{ > \n" > " float f = texture2D( texture, vec3( gl_TexCoord[0].xy, 1.0).xy > ).r; \n" > " > \n" > " f = 256.0 * f; > \n" > " float fC = floor( f ) / 256.0; > \n" > " > \n" > " f = 256.0 * fract( f ); > \n" > " float fS = floor( f ) / 256.0; > \n" > " > \n" > " f = 256.0 * fract( f ); > \n" > " float fH = floor( f ) / 256.0; > \n" > " > \n" > " fS *= 0.5; > \n" > " fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); > \n" > " > \n" > " vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), > \n" > " abs( fC - 0.333333 ), > \n" > " abs( fC - 0.666667 ) ); > \n" > " > \n" > " rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); > \n" > " > \n" > " float fMax = max( max( rgb.r, rgb.g ), rgb.b ); > \n" > " fMax = 1.0 / fMax; > \n" > " > \n" > " vec3 color = fMax * rgb; > \n" > " > \n" > " gl_FragColor = vec4( fS + fH * color, 1 ) * gl_Color; > \n" > "} > \n" > #endif > ); // end _depthColorFragmentShader > } > > DebugShadowMap::DebugShadowMap > (const DebugShadowMap& copy, const osg::CopyOp& copyop) : > BaseClass(copy,copyop), > _hudSize( copy._hudSize ), > _hudOrigin( copy._hudOrigin ), > _viewportSize( copy._viewportSize ), > _viewportOrigin( copy._viewportOrigin ), > _orthoSize( copy._viewportOrigin ), > _orthoOrigin( copy._viewportOrigin ), > _doDebugDraw( copy._doDebugDraw ) > { > if( copy._depthColorFragmentShader.valid() ) > _depthColorFragmentShader = > dynamic_cast<osg::Shader*> > ( copy._depthColorFragmentShader->clone(copyop) ); > } > > DebugShadowMap::~DebugShadowMap() > { > } > > void DebugShadowMap::ViewData::cull( void ) > { > if( getDebugDraw() && !_cameraDebugHUD.valid() ) > createDebugHUD(); > > BaseClass::ViewData::cull( ); > > cullDebugGeometry( ); > } > > bool DebugShadowMap::ViewData::DebugBoundingBox > ( const osg::BoundingBox & bb, const char * name ) > { > bool result = false; > #if defined( _DEBUG ) || defined( DEBUG ) > if( !name ) name = ""; > > osg::BoundingBox & bb_prev = _boundingBoxMap[ std::string( name ) ]; > > result = bb.center() != bb_prev.center() || bb.radius() != > bb_prev.radius(); > if( result ) > std::cout << "Box<" << name << "> (" > << ( bb._max._v[0] + bb._min._v[0] ) * 0.5 << " " > << ( bb._max._v[1] + bb._min._v[1] ) * 0.5 << " " > << ( bb._max._v[2] + bb._min._v[2] ) * 0.5 << ") [" > << ( bb._max._v[0] - bb._min._v[0] ) << " " > << ( bb._max._v[1] - bb._min._v[1] ) << " " > << ( bb._max._v[2] - bb._min._v[2] ) << "] " > << std::endl; > > bb_prev = bb; > #endif > return result; > } > > bool DebugShadowMap::ViewData::DebugPolytope > ( const osg::Polytope & p, const char * name ) > { > bool result = false; > #if defined( _DEBUG ) || defined( DEBUG ) > if( !name ) name = ""; > > osg::Polytope & p_prev = _polytopeMap[ std::string( name ) ]; > > result = ( p.getPlaneList() != p_prev.getPlaneList() ); > > if( result ) { > std::cout << "Polytope<" << name > << "> size(" << p.getPlaneList().size() << ")" > << std::endl; > > if( p.getPlaneList().size() == p_prev.getPlaneList().size() ) { > for( unsigned i = 0; i < p.getPlaneList().size(); ++i ) > { > if( p.getPlaneList()[i] != p_prev.getPlaneList()[i] ) > { > std::cout << "Plane<" << i > << "> (" > << p.getPlaneList()[i].asVec4()[0] << ", " > << p.getPlaneList()[i].asVec4()[1] << ", " > << p.getPlaneList()[i].asVec4()[2] << ", " > << p.getPlaneList()[i].asVec4()[3] << ")" > << std::endl; > } > } > } > } > > p_prev = p; > #endif > return result; > } > > bool DebugShadowMap::ViewData::DebugMatrix > ( const osg::Matrix & m, const char * name ) > { > bool result = false; > #if defined( _DEBUG ) || defined( DEBUG ) > if( !name ) name = ""; > > osg::Matrix & m_prev = _matrixMap[ std::string( name ) ]; > > result = ( m != m_prev ); > > if( result ) > std::cout << "Matrix<" << name << "> " << std::endl > <<"[ " << m(0,0) << " " << m(0,1) << " " << m(0,2) << " " << > m(0,3) << " ] " << std::endl > <<"[ " << m(1,0) << " " << m(1,1) << " " << m(1,2) << " " << > m(1,3) << " ] " << std::endl > <<"[ " << m(2,0) << " " << m(2,1) << " " << m(2,2) << " " << > m(2,3) << " ] " << std::endl > <<"[ " << m(3,0) << " " << m(3,1) << " " << m(3,2) << " " << > m(3,3) << " ] " << std::endl; > > m_prev = m; > #endif > return result; > } > > void DebugShadowMap::ViewData::setDebugPolytope > ( const char * name, const ConvexPolyhedron & polytope, > osg::Vec4 colorOutline, osg::Vec4 colorInside ) > { > if( !getDebugDraw() ) return; > > if( &polytope == NULL ) { // delete > PolytopeGeometry & pg = _polytopeGeometryMap[ std::string( name ) ]; > for( unsigned int i = 0; i < VECTOR_LENGTH( pg._geometry ) ; i++ ) > { > if( pg._geometry[i].valid() ) { > if( _geode[i].valid() && > _geode[i]->containsDrawable( pg._geometry[i].get() ) ) > _geode[i]->removeDrawable( pg._geometry[i].get() ); > > pg._geometry[i] = NULL; > } > } > _polytopeGeometryMap.erase( std::string( name ) ); > } else { // update > PolytopeGeometry & pg = _polytopeGeometryMap[ std::string( name ) ]; > > pg._polytope = polytope; > if( colorOutline.a() > 0 ) > pg._colorOutline = colorOutline; > if( colorInside.a() > 0 ) > pg._colorInside = colorInside; > > for( unsigned int i = 0; i < VECTOR_LENGTH( pg._geometry ) ; i++ ) > { > if( !pg._geometry[i].valid() ) { > pg._geometry[i] = new osg::Geometry; > pg._geometry[i]->setDataVariance( osg::Object::DYNAMIC ); > pg._geometry[i]->setUseDisplayList( false ); > pg._geometry[i]->setSupportsDisplayList( false ); > } > > if( _geode[i].valid() && > !_geode[i]->containsDrawable( pg._geometry[i].get() ) ) { > osg::Geode::DrawableList & dl = > const_cast< osg::Geode::DrawableList &> > ( _geode[i]->getDrawableList() ); > dl.insert( dl.begin(), pg._geometry[i].get() ); > } > } > } > } > > void DebugShadowMap::ViewData::updateDebugGeometry > ( const osg::Camera * viewCam, const osg::Camera * shadowCam ) > { > if( !getDebugDraw() ) return; > if( _polytopeGeometryMap.empty() ) return; > > const int num = 2; // = VECTOR_LENGTH( PolytopeGeometry::_geometry ); > > osg::Matrix > transform[ num ] = > { viewCam->getViewMatrix() * > // use near far clamped projection ( precomputed in > cullDebugGeometry ) > ( viewCam == _viewCamera ? _viewProjection : > viewCam->getProjectionMatrix() ), > shadowCam->getViewMatrix() * shadowCam->getProjectionMatrix() > }, > inverse[ num ] = > { osg::Matrix::inverse( transform[0] ), > osg::Matrix::inverse( transform[1] ) }; > > #if 0 > ConvexPolyhedron frustum[ num ]; > for( int i = 0; i < num; i++ ) { > frustum[i].setToUnitFrustum( ); > #if 1 > frustum[i].transform( inverse[i], transform[i] ); > #else > frustum[i].transform > ( osg::Matrix::inverse( camera[i]->getProjectionMatrix() ), > camera[i]->getProjectionMatrix() ); > frustum[i].transform > ( osg::Matrix::inverse( camera[i]->getViewMatrix() ), > camera[i]->getViewMatrix() ); > #endif > }; > #else > osg::Polytope frustum[ num ]; > for( int i = 0; i < num; i++ ) { > frustum[i].setToUnitFrustum( ); > frustum[i].transformProvidingInverse( transform[i] ); > } > #endif > > transform[0] = viewCam->getViewMatrix(); > inverse[0] = viewCam->getInverseViewMatrix(); > > for( PolytopeGeometryMap::iterator itr = _polytopeGeometryMap.begin(); > itr != _polytopeGeometryMap.end(); > ++itr ) > { > PolytopeGeometry & pg = itr->second; > > for( int i = 0; i < num ; i++ ) > { > > ConvexPolyhedron cp( pg._polytope ); > cp.cut( frustum[i] ); > cp.transform( transform[i], inverse[i] ); > > pg._geometry[i] = cp.buildGeometry > ( pg._colorOutline, pg._colorInside, pg._geometry[i].get() ); > } > } > } > > void DebugShadowMap::ViewData::cullDebugGeometry( ) > { > if( !getDebugDraw() ) return; > if( !_camera.valid() ) return; > > // View camera may use clamping projection matrix after traversal. > // Since we need to know exact matrix for drawing the frusta, > // we have to compute it here in exactly the same way as cull visitor > // will after cull traversal completes view camera subgraph. > { > _viewProjection = *_cv->getProjectionMatrix(); > _viewCamera = _cv->getRenderStage()->getCamera(); > > if( _cv->getComputeNearFarMode() ) { > > // Redo steps from CullVisitor::popProjectionMatrix() > // which clamps projection matrix when Camera & Projection > // completes traversal of their children > > // We have to do this now manually > // because we did not complete camera traversal yet but > // we need to know how this clamped projection matrix will be > > _cv->computeNearPlane(); > > osgUtil::CullVisitor::value_type n = > _cv->getCalculatedNearPlane(); > osgUtil::CullVisitor::value_type f = > _cv->getCalculatedFarPlane(); > > if( n < f ) > _cv->clampProjectionMatrix( _viewProjection, n, f ); > } > } > > updateDebugGeometry( _viewCamera, _camera.get() ); > > #if 1 // Add geometries of polytopes to main cam Render Stage > _transform[0]->accept( *_cv ); > #else > for( PolytopeGeometryMap::iterator itr = _polytopeGeometryMap.begin(); > itr != _polytopeGeometryMap.end(); > ++itr ) > { > PolytopeGeometry & pg = itr->second; > _cv->pushStateSet( _geode[0]->getStateSet() ); > _cv->addDrawableAndDepth( pg._geometry[0].get(), NULL, FLT_MAX ); > _cv->popStateSet( ); > } > #endif > > // Add geometries of polytopes to hud cam Render Stage > _cameraDebugHUD->accept( *_cv ); > } > > void DebugShadowMap::ViewData::init( ThisClass *st, osgUtil::CullVisitor *cv > ) > { > BaseClass::ViewData::init( st, cv ); > > _doDebugDrawPtr = &st->_doDebugDraw; > > _hudSize = st->_hudSize; > _hudOrigin = st->_hudOrigin; > _viewportSize = st->_viewportSize; > _viewportOrigin = st->_viewportOrigin; > _orthoSize = st->_orthoSize; > _orthoOrigin = st->_orthoOrigin; > > _depthColorFragmentShader = st->_depthColorFragmentShader; > > // create placeholder geodeds for polytope geometries > // rest of their initialization will be performed during camera hud init > _geode[0] = new osg::Geode; > _geode[1] = new osg::Geode; > > _cameraDebugHUD = NULL;//Force debug HUD rebuild ( if needed ) > } > > void DebugShadowMap::ViewData::createDebugHUD( ) > { > // Callback used by debugging hud to display Shadow Map to color > buffer > // Had to do it this way because OSG does not allow to use > // the same GL Texture Id with different glTexParams. > // Callback simply turns compare mode off via GL while rendering hud > and > // restores it before rendering the scene with shadows. > class DrawableDrawWithDepthShadowComparisonOffCallback: > public osg::Drawable::DrawCallback > { > public: > DrawableDrawWithDepthShadowComparisonOffCallback( > osg::Texture2D *pTex ) > : _pTexture( pTex ) > { > } > > virtual void drawImplementation > ( osg::RenderInfo & ri,const osg::Drawable* drawable > ) const > { > ri.getState()->applyTextureAttribute( 0, > _pTexture.get() ); > > // Turn off depth comparison mode > glTexParameteri( GL_TEXTURE_2D, > GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE ); > > drawable->drawImplementation(ri); > > // Turn it back on > glTexParameteri( GL_TEXTURE_2D, > GL_TEXTURE_COMPARE_MODE_ARB, > GL_COMPARE_R_TO_TEXTURE_ARB ); > } > > osg::ref_ptr< osg::Texture2D > _pTexture; > }; > > _cameraDebugHUD = new osg::Camera; > > { // Make sure default HUD layout makes sense > if( _hudSize[0] <= 0 ) _hudSize[0] = DEFAULT_DEBUG_HUD_SIZE_X; > if( _hudSize[1] <= 0 ) _hudSize[1] = DEFAULT_DEBUG_HUD_SIZE_Y; > > if( _viewportSize[0] <= 0 ) _viewportSize[0] = _hudSize[0]; > if( _viewportSize[1] <= 0 ) _viewportSize[1] = _hudSize[1]; > > if( _orthoSize[0] <= 0 ) _orthoSize[0] = _viewportSize[0]; > if( _orthoSize[1] <= 0 ) _orthoSize[1] = _viewportSize[1]; > } > > { // Initialize hud camera > osg::Camera * camera = _cameraDebugHUD.get(); > camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); > camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); > camera->setViewMatrix(osg::Matrix::identity()); > camera->setViewport( _viewportOrigin[0], _viewportOrigin[1], > _viewportSize[0], _viewportSize[1] ); > > camera->setProjectionMatrixAsOrtho( > _orthoOrigin[0], _orthoOrigin[0] + _orthoSize[0], > _orthoOrigin[1], _orthoOrigin[1] + _orthoSize[1], > -10, 10 ); > > camera->setClearMask(GL_DEPTH_BUFFER_BIT); > camera->setRenderOrder(osg::Camera::POST_RENDER); > } > > { // Add geode and drawable with BaseClass display > // create geode to contain hud drawables > osg::Geode* geode = new osg::Geode; > _cameraDebugHUD->addChild(geode); > > // finally create and attach hud geometry > osg::Geometry* geometry = osg::createTexturedQuadGeometry > ( osg::Vec3(_hudOrigin[0],_hudOrigin[1],0), > osg::Vec3(_hudSize[0],0,0), > osg::Vec3(0,_hudSize[1],0) ); > > osg::StateSet* stateset = _cameraDebugHUD->getOrCreateStateSet(); > > > stateset->setTextureAttributeAndModes(0,_texture.get(),osg::StateAttribute::ON > ); > stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); > // stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF); > stateset->setAttributeAndModes > ( new osg::Depth( osg::Depth::ALWAYS, 0, 1, false ) ); > stateset->setMode(GL_BLEND,osg::StateAttribute::ON); > > osg::Program* program = new osg::Program; > program->addShader( _depthColorFragmentShader.get() ); > stateset->setAttribute( program ); > stateset->addUniform( new osg::Uniform( "texture" , 0 ) ); > > geometry->setDrawCallback > ( new DrawableDrawWithDepthShadowComparisonOffCallback( > _texture.get() ) ); > > geode->addDrawable( geometry ); > } > > { // Create transforms and geodes to manage polytope drawing > osg::StateSet * stateset = new osg::StateSet; > stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); > stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); > stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF); > stateset->setMode(GL_BLEND, osg::StateAttribute::ON); > stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); > stateset->setAttribute( new osg::Program() ); > stateset->setAttributeAndModes > ( new osg::Depth( osg::Depth::LEQUAL, 0, 1, false ) ); > > for( int i = 0; i < 2; i++ ) { > _geode[i]->setStateSet( stateset ); > _transform[i] = new osg::MatrixTransform; > _transform[i]->addChild( _geode[i].get() ); > _transform[i]->setMatrix( osg::Matrix::identity() ); > _transform[i]->setReferenceFrame( > osg::MatrixTransform::ABSOLUTE_RF ); > } > > _transform[1]->setMatrix > ( osg::Matrix::translate( 1, 1, 0 ) * > osg::Matrix::scale( 0.5, 0.5, 1 ) * > osg::Matrix::scale( _hudSize[0], _hudSize[1], 1 ) * > osg::Matrix::translate( _hudOrigin[0], _hudOrigin[1], 0 ) ); > > _cameraDebugHUD->addChild( _transform[1].get() ); > } > } > > osg::Vec3d DebugShadowMap::ViewData::computeShadowTexelToPixelError > ( const osg::Matrix & mvpwView, > const osg::Matrix & mvpwShadow, > const osg::Vec3d & vWorld, > const osg::Vec3d & vDelta ) > { > osg::Vec3d vS0 = mvpwShadow * vWorld; > osg::Vec3d vS1 = mvpwShadow * ( vWorld + vDelta ); > > osg::Vec3d vV0 = mvpwView * vWorld; > osg::Vec3d vV1 = mvpwView * ( vWorld + vDelta ); > > osg::Vec3d dV = vV1 - vV0; > osg::Vec3d dS = vS1 - vS0; > > return osg::Vec3( dS[0] / dV[0], dS[1] / dV[1], dS[2] / dV[2] ); > } > > void DebugShadowMap::ViewData::displayShadowTexelToPixelErrors > ( const osg::Camera* viewCamera, > const osg::Camera* shadowCamera, > const ConvexPolyhedron* hull ) > { > osg::Matrix mvpwMain = > viewCamera->getViewMatrix() * > viewCamera->getProjectionMatrix() * > viewCamera->getViewport()->computeWindowMatrix(); > > osg::Matrix mvpwShadow = > shadowCamera->getViewMatrix() * > shadowCamera->getProjectionMatrix() * > shadowCamera->getViewport()->computeWindowMatrix(); > > osg::BoundingBox bb = > hull->computeBoundingBox( viewCamera->getViewMatrix() ); > > osg::Matrix m = viewCamera->getInverseViewMatrix(); > > osg::Vec3d vn = osg::Vec3d( 0, 0, bb._max[2] ) * m; > osg::Vec3d vf = osg::Vec3d( 0, 0, bb._min[2] ) * m; > osg::Vec3d vm = osg::Vec3d( 0, 0, ( bb._max[2] + bb._min[2] ) * 0.5 ) * > m; > > osg::Vec3d vne = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vn > ); > osg::Vec3d vfe = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vf > ); > osg::Vec3d vme = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vm > ); > > std::cout << std::setprecision( 3 ) << " " > << "ne=(" << vne[0] << "," << vne[1] << "," << vne[2] << ") " > << "fe=(" << vfe[0] << "," << vfe[1] << "," << vfe[2] << ") " > << "me=(" << vme[0] << "," << vme[1] << "," << vme[2] << ") " > << > "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" > << > "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" > << > "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" > << std::flush; > } > > > /* -*-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_texcoord[] = > "uniform sampler2D osgShadow_shadowTexture; \n" > " \n" > "void main(void) \n" > "{ \n" > " vec4 texCoord = gl_TexCoord[1].xyzw; \n" > " float value = texCoord.z / texCoord.w; \n" > " gl_FragColor = vec4( value, value, value, 1.0 ); \n" > "} \n"; > > 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 - > equivilant 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_1D,osg::StateAttribute::OFF); > > > _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_2D,osg::StateAttribute::ON); > > > _stateset->setTextureMode(_baseTextureUnit,GL_TEXTURE_3D,osg::StateAttribute::OFF); > } > } > > _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 recieving 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,osg::Vec3(0.0f,1.0f,0.0f)); > } > 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(),osg::Vec3(0.0f,1.0f,0.0f)); > } > 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(),osg::Vec3(0.0f,1.0f,0.0f)); > } > > > } > > 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 > > //////////////////////////////////////////////////////////////////////////////// > osg::ref_ptr<osg::Camera> ShadowMap::makeDebugHUD() > { > //////////////////////////////////////////////////////////////////////////////// > // 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 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; > }; > > // 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 > > > _______________________________________________ > osg-submissions mailing list > [email protected] > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org > > _______________________________________________ osg-submissions mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
