diff --git a/include/osg/Stencil b/include/osg/Stencil index 95445e4df..046807d43 100644 --- a/include/osg/Stencil +++ b/include/osg/Stencil @@ -1,13 +1,13 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ @@ -25,12 +25,24 @@ namespace osg { /** Encapsulate OpenGL glStencilFunc/Op/Mask functions. -*/ +* +* All functionality except INCR_WRAP and DECR_WRAP is supported by OpenGL 1.1. +* INCR_WRAP an DECR_WRAP are available since OpenGL 1.4 or when +* GL_EXT_stencil_wrap extension is present. +* +* If INCR_WRAP or DECR_WRAP values are used while they are detected to be not supported, +* the INCR or DECR values are sent to OpenGL instead. Note: do not use Stencil::getFunction() +* to detect whether WRAP operations is used as the object's value is kept intact. +* Use osg::Stencil::getExtensions() method instead. +* +* OpenGL 2.0 introduced two side stenciling that is available through +* osg::StencilTwoSided class. +*/ class OSG_EXPORT Stencil : public StateAttribute { public : - - + + Stencil(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ @@ -46,7 +58,7 @@ class OSG_EXPORT Stencil : public StateAttribute META_StateAttribute(osg, Stencil, STENCIL); - + /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ virtual int compare(const StateAttribute& sa) const { @@ -90,18 +102,18 @@ class OSG_EXPORT Stencil : public StateAttribute _funcRef = ref; _funcMask = mask; } - + inline void setFunction(Function func) { _func = func; } inline Function getFunction() const { return _func; } - + inline void setFunctionRef(int ref) { _funcRef=ref; } inline int getFunctionRef() const { return _funcRef; } inline void setFunctionMask(unsigned int mask) { _funcMask=mask; } inline unsigned int getFunctionMask() const { return _funcMask; } - - - enum Operation + + + enum Operation { KEEP = GL_KEEP, ZERO = GL_ZERO, @@ -112,8 +124,8 @@ class OSG_EXPORT Stencil : public StateAttribute INCR_WRAP = GL_INCR_WRAP, DECR_WRAP = GL_DECR_WRAP }; - - /** set the operations to apply when the various stencil and depth + + /** set the operations to apply when the various stencil and depth * tests fail or pass. First parameter is to control the operation * when the stencil test fails. The second parameter is to control the * operation when the stencil test passes, but depth test fails. The @@ -126,16 +138,16 @@ class OSG_EXPORT Stencil : public StateAttribute _zfail = zfail; _zpass = zpass; } - + /** set the operation when the stencil test fails.*/ inline void setStencilFailOperation(Operation sfail) { _sfail = sfail; } /** get the operation when the stencil test fails.*/ inline Operation getStencilFailOperation() const { return _sfail; } - + /** set the operation when the stencil test passes but the depth test fails.*/ inline void setStencilPassAndDepthFailOperation(Operation zfail) { _zfail=zfail; } - + /** get the operation when the stencil test passes but the depth test fails.*/ inline Operation getStencilPassAndDepthFailOperation() const { return _zfail; } @@ -144,27 +156,69 @@ class OSG_EXPORT Stencil : public StateAttribute /** get the operation when both the stencil test and the depth test pass.*/ inline Operation getStencilPassAndDepthPassOperation() const { return _zpass; } - + inline void setWriteMask(unsigned int mask) { _writeMask = mask; } - + inline unsigned int getWriteMask() const { return _writeMask; } virtual void apply(State& state) const; + + + /** Extensions class which encapsulates the querying of extensions and + * associated function pointers, and provide convenience wrappers to + * check for the extensions or use the associated functions. + */ + class OSG_EXPORT Extensions : public osg::Referenced + { + public: + Extensions(unsigned int contextID); + + Extensions(const Extensions& rhs); + + void lowestCommonDenominator(const Extensions& rhs); + + void setupGLExtensions(unsigned int contextID); + + void setStencilWrapSupported(bool flag) { _isStencilWrapSupported = flag; } + bool isStencilWrapSupported() const { return _isStencilWrapSupported; } + + protected: + + ~Extensions() {} + + bool _isStencilWrapSupported; + }; + + /** Function to call to get the extension of a specified context. + * If the Extension object for that context has not yet been created + * and the 'createIfNotInitalized' flag been set to false then returns NULL. + * If 'createIfNotInitalized' is true then the Extensions object is + * automatically created. However, in this case the extension object + * will only be created with the graphics context associated with ContextID. + */ + static Extensions* getExtensions(unsigned int contextID, bool createIfNotInitalized); + + /** The setExtensions method allows users to override the extensions across graphics contexts. + * Typically used when you have different extensions supported across graphics pipes + * but need to ensure that they all use the same low common denominator extensions. + */ + static void setExtensions(unsigned int contextID, Extensions* extensions); + protected: - + virtual ~Stencil(); Function _func; int _funcRef; unsigned int _funcMask; - + Operation _sfail; Operation _zfail; Operation _zpass; - + unsigned int _writeMask; }; diff --git a/include/osg/StencilTwoSided b/include/osg/StencilTwoSided index ef163541d..8e5732d10 100644 --- a/include/osg/StencilTwoSided +++ b/include/osg/StencilTwoSided @@ -22,30 +22,40 @@ namespace osg { #define GL_STENCIL_TEST_TWO_SIDE 0x8910 #endif -/** Encapsulate OpenGL two sided glStencilFunc/Op/Mask functions. -*/ +/** Provides OpenGL two sided stencil functionality, also known as separate stencil. + * It enables to specify different stencil function for front and back facing polygons. + * Two sided stenciling is used usually to eliminate the need of two rendering passes + * when using standard stenciling functions. See also \sa osg::Stencil. + * + * Two sided stenciling is available since OpenGL 2.0. It is also supported by + * EXT_stencil_two_side extension especially on Nvidia cards. + * Another extension introduced by ATI is ATI_separate_stencil. However, ATI's extension + * is limited to have reference and mask value the same for both faces. + * ATI's extension is currently not supported by the current implementation. + * + * osg::StencilTwoSided does nothing if OpenGL 2.0 or EXT_stencil_two_side are not available. +*/ class OSG_EXPORT StencilTwoSided : public StateAttribute { public : - - + + StencilTwoSided(); /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ StencilTwoSided(const StencilTwoSided& stencil,const CopyOp& copyop=CopyOp::SHALLOW_COPY); META_StateAttribute(osg, StencilTwoSided, STENCIL); - + /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ virtual int compare(const StateAttribute& sa) const; virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const { usage.usesMode(GL_STENCIL_TEST); - usage.usesMode(GL_STENCIL_TEST_TWO_SIDE); return true; } - + enum Face { FRONT = 0, @@ -70,18 +80,18 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute _funcRef[face] = ref; _funcMask[face] = mask; } - + inline void setFunction(Face face, Function func) { _func[face] = func; } inline Function getFunction(Face face) const { return _func[face]; } - + inline void setFunctionRef(Face face, int ref) { _funcRef[face]=ref; } inline int getFunctionRef(Face face) const { return _funcRef[face]; } inline void setFunctionMask(Face face, unsigned int mask) { _funcMask[face]=mask; } inline unsigned int getFunctionMask(Face face) const { return _funcMask[face]; } - - - enum Operation + + + enum Operation { KEEP = GL_KEEP, ZERO = GL_ZERO, @@ -92,7 +102,7 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute INCR_WRAP = GL_INCR_WRAP, DECR_WRAP = GL_DECR_WRAP }; - + /** set the operations to apply when the various stencil and depth * tests fail or pass. First parameter is to control the operation * when the stencil test fails. The second parameter is to control the @@ -106,16 +116,16 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute _zfail[face] = zfail; _zpass[face] = zpass; } - + /** set the operation when the stencil test fails.*/ inline void setStencilFailOperation(Face face, Operation sfail) { _sfail[face] = sfail; } /** get the operation when the stencil test fails.*/ inline Operation getStencilFailOperation(Face face) const { return _sfail[face]; } - + /** set the operation when the stencil test passes but the depth test fails.*/ inline void setStencilPassAndDepthFailOperation(Face face, Operation zfail) { _zfail[face]=zfail; } - + /** get the operation when the stencil test passes but the depth test fails.*/ inline Operation getStencilPassAndDepthFailOperation(Face face) const { return _zfail[face]; } @@ -124,15 +134,15 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute /** get the operation when both the stencil test and the depth test pass.*/ inline Operation getStencilPassAndDepthPassOperation(Face face) const { return _zpass[face]; } - + inline void setWriteMask(Face face, unsigned int mask) { _writeMask[face] = mask; } - + inline unsigned int getWriteMask(Face face) const { return _writeMask[face]; } virtual void apply(State& state) const; - + public: /** Extensions class which encapsulates the querying of extensions and @@ -145,26 +155,45 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute Extensions(unsigned int contextID); Extensions(const Extensions& rhs); - + void lowestCommonDenominator(const Extensions& rhs); - + void setupGLExtensions(unsigned int contextID); void setStencilTwoSidedSupported(bool flag) { _isStencilTwoSidedSupported=flag; } bool isStencilTwoSidedSupported() const { return _isStencilTwoSidedSupported; } + void setOpenGL20Supported(bool flag) { _isOpenGL20Supported=flag; } + bool isOpenGL20Supported() const { return _isOpenGL20Supported; } + void setSeparateStencilSupported(bool flag) { _isSeparateStencilSupported=flag; } + bool isSeparateStencilSupported() const { return _isSeparateStencilSupported; } void glActiveStencilFace(GLenum face) const; + void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) const; + void glStencilMaskSeparate(GLenum face, GLuint mask) const; + void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) const; + void glStencilFuncSeparateATI(GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask) const; protected: ~Extensions() {} - + bool _isStencilTwoSidedSupported; - + bool _isOpenGL20Supported; + bool _isSeparateStencilSupported; + typedef void (GL_APIENTRY * ActiveStencilFaceProc) (GLenum); + typedef void (GL_APIENTRY * StencilOpSeparate) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); + typedef void (GL_APIENTRY * StencilMaskSeparate) (GLenum face, GLuint mask); + typedef void (GL_APIENTRY * StencilFuncSeparate) (GLenum face, GLenum func, GLint ref, GLuint mask); + typedef void (GL_APIENTRY * StencilFuncSeparateATI) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); + ActiveStencilFaceProc _glActiveStencilFace; + StencilOpSeparate _glStencilOpSeparate; + StencilMaskSeparate _glStencilMaskSeparate; + StencilFuncSeparate _glStencilFuncSeparate; + StencilFuncSeparate _glStencilFuncSeparateATI; }; - + /** Function to call to get the extension of a specified context. * If the Extension object for that context has not yet been created * and the 'createIfNotInitalized' flag been set to false then returns NULL. @@ -179,20 +208,20 @@ class OSG_EXPORT StencilTwoSided : public StateAttribute * but need to ensure that they all use the same low common denominator extensions. */ static void setExtensions(unsigned int contextID,Extensions* extensions); - + protected: - + virtual ~StencilTwoSided(); Function _func[2]; int _funcRef[2]; unsigned int _funcMask[2]; - + Operation _sfail[2]; Operation _zfail[2]; Operation _zpass[2]; - + unsigned int _writeMask[2]; }; diff --git a/src/osg/Stencil.cpp b/src/osg/Stencil.cpp index 154af5d36..c55cac674 100644 --- a/src/osg/Stencil.cpp +++ b/src/osg/Stencil.cpp @@ -1,16 +1,19 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-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 + * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include +#include +#include +#include using namespace osg; @@ -20,7 +23,7 @@ Stencil::Stencil() _func = ALWAYS; _funcRef = 0; _funcMask = ~0u; - + // set up same defaults as glStencilOp. _sfail = KEEP; _zfail = KEEP; @@ -30,15 +33,80 @@ Stencil::Stencil() _writeMask = ~0u; } - Stencil::~Stencil() { } -void Stencil::apply(State&) const +static Stencil::Operation validateOperation(State& state, + const Stencil::Extensions* extensions, Stencil::Operation op) { + // only wrap requires validation + if (op != Stencil::INCR_WRAP && op != Stencil::DECR_WRAP) + return op; + + // get extension object + if (!extensions) + { + const unsigned int contextID = state.getContextID(); + extensions = Stencil::getExtensions(contextID, true); + } + + // wrap support + if (extensions->isStencilWrapSupported()) + return op; + else + return op==Stencil::INCR_WRAP ? Stencil::INCR : Stencil::DECR; +} + +void Stencil::apply(State& state) const +{ + const Extensions* extensions = NULL; + Operation sf = validateOperation(state, extensions, _sfail); + Operation zf = validateOperation(state, extensions, _zfail); + Operation zp = validateOperation(state, extensions, _zpass); + glStencilFunc((GLenum)_func,_funcRef,_funcMask); - glStencilOp((GLenum)_sfail,(GLenum)_zfail,(GLenum)_zpass); + glStencilOp((GLenum)sf,(GLenum)zf,(GLenum)zp); glStencilMask(_writeMask); } + +typedef buffered_value< ref_ptr > BufferedExtensions; +static BufferedExtensions s_extensions; + +Stencil::Extensions* Stencil::getExtensions(unsigned int contextID, bool createIfNotInitalized) +{ + if (!s_extensions[contextID] && createIfNotInitalized) + s_extensions[contextID] = new Extensions(contextID); + return s_extensions[contextID].get(); +} + +void Stencil::setExtensions(unsigned int contextID, Extensions* extensions) +{ + s_extensions[contextID] = extensions; +} + +Stencil::Extensions::Extensions(unsigned int contextID) +{ + setupGLExtensions(contextID); +} + +Stencil::Extensions::Extensions(const Extensions& rhs) : + Referenced() +{ + _isStencilWrapSupported = rhs._isStencilWrapSupported; +} + + +void Stencil::Extensions::lowestCommonDenominator(const Extensions& rhs) +{ + if (!rhs._isStencilWrapSupported) + _isStencilWrapSupported = false; +} + +void Stencil::Extensions::setupGLExtensions(unsigned int contextID) +{ + _isStencilWrapSupported = isGLExtensionOrVersionSupported(contextID, "GL_EXT_stencil_wrap", 1.4f); + + OSG_INFO << "Stencil wrap: " << (_isStencilWrapSupported ? "supported" : "not supported") << std::endl; +} diff --git a/src/osg/StencilTwoSided.cpp b/src/osg/StencilTwoSided.cpp index 7022707ba..e658f4452 100644 --- a/src/osg/StencilTwoSided.cpp +++ b/src/osg/StencilTwoSided.cpp @@ -18,6 +18,7 @@ using namespace osg; + StencilTwoSided::StencilTwoSided() { // set up same defaults as glStencilFunc. @@ -85,21 +86,46 @@ int StencilTwoSided::compare(const StateAttribute& sa) const void StencilTwoSided::apply(State& state) const { + // get "per-context" extensions const unsigned int contextID = state.getContextID(); const Extensions* extensions = getExtensions(contextID,true); - if (!extensions->isStencilTwoSidedSupported()) - return; - - extensions->glActiveStencilFace(GL_BACK); - glStencilOp((GLenum)_sfail[BACK],(GLenum)_zfail[BACK],(GLenum)_zpass[BACK]); - glStencilMask(_writeMask[BACK]); - glStencilFunc((GLenum)_func[BACK],_funcRef[BACK],_funcMask[BACK]); + // use OpenGL 2.0 functions if available + if (extensions->isOpenGL20Supported()) + { + // front face + extensions->glStencilOpSeparate(GL_FRONT, (GLenum)_sfail[FRONT],(GLenum)_zfail[FRONT],(GLenum)_zpass[FRONT]); + extensions->glStencilMaskSeparate(GL_FRONT, _writeMask[FRONT]); + extensions->glStencilFuncSeparate(GL_FRONT, (GLenum)_func[FRONT],_funcRef[FRONT],_funcMask[FRONT]); - extensions->glActiveStencilFace(GL_FRONT); - glStencilOp((GLenum)_sfail[FRONT],(GLenum)_zfail[FRONT],(GLenum)_zpass[FRONT]); - glStencilMask(_writeMask[FRONT]); - glStencilFunc((GLenum)_func[FRONT],_funcRef[FRONT],_funcMask[FRONT]); + // back face + extensions->glStencilOpSeparate(GL_BACK, (GLenum)_sfail[BACK],(GLenum)_zfail[BACK],(GLenum)_zpass[BACK]); + extensions->glStencilMaskSeparate(GL_BACK, _writeMask[BACK]); + extensions->glStencilFuncSeparate(GL_BACK, (GLenum)_func[BACK],_funcRef[BACK],_funcMask[BACK]); + + return; + } + + // try to use two sided stencil extension + if (extensions->isStencilTwoSidedSupported()) + { + // enable two sided stenciling + glEnable(GL_STENCIL_TEST_TWO_SIDE); + + // back face + extensions->glActiveStencilFace(GL_BACK); + glStencilOp((GLenum)_sfail[BACK],(GLenum)_zfail[BACK],(GLenum)_zpass[BACK]); + glStencilMask(_writeMask[BACK]); + glStencilFunc((GLenum)_func[BACK],_funcRef[BACK],_funcMask[BACK]); + + // front face + extensions->glActiveStencilFace(GL_FRONT); + glStencilOp((GLenum)_sfail[FRONT],(GLenum)_zfail[FRONT],(GLenum)_zpass[FRONT]); + glStencilMask(_writeMask[FRONT]); + glStencilFunc((GLenum)_func[FRONT],_funcRef[FRONT],_funcMask[FRONT]); + + return; + } } @@ -126,34 +152,87 @@ StencilTwoSided::Extensions::Extensions(const Extensions& rhs): Referenced() { _isStencilTwoSidedSupported = rhs._isStencilTwoSidedSupported; + _isOpenGL20Supported = rhs._isOpenGL20Supported; + _isSeparateStencilSupported = rhs._isSeparateStencilSupported; _glActiveStencilFace = rhs._glActiveStencilFace; + _glStencilOpSeparate = rhs._glStencilOpSeparate; + _glStencilMaskSeparate = rhs._glStencilMaskSeparate; + _glStencilFuncSeparate = rhs._glStencilFuncSeparate; + _glStencilFuncSeparateATI = rhs._glStencilFuncSeparateATI; } void StencilTwoSided::Extensions::lowestCommonDenominator(const Extensions& rhs) { if (!rhs._isStencilTwoSidedSupported) _isStencilTwoSidedSupported = false; + if (!rhs._isOpenGL20Supported) _isOpenGL20Supported = false; + if (!rhs._isSeparateStencilSupported) _isSeparateStencilSupported = false; - if (!rhs._glActiveStencilFace) _glActiveStencilFace = 0; - + if (!rhs._glActiveStencilFace) _glActiveStencilFace = NULL; + if (!rhs._glStencilOpSeparate) _glStencilOpSeparate = NULL; + if (!rhs._glStencilMaskSeparate) _glStencilMaskSeparate = NULL; + if (!rhs._glStencilFuncSeparate) _glStencilFuncSeparate = NULL; + if (!rhs._glStencilFuncSeparateATI) _glStencilFuncSeparateATI = NULL; } void StencilTwoSided::Extensions::setupGLExtensions(unsigned int contextID) { - _isStencilTwoSidedSupported = isGLExtensionSupported(contextID,"GL_EXT_stencil_two_side"); + // extension support + _isStencilTwoSidedSupported = isGLExtensionSupported(contextID, "GL_EXT_stencil_two_side"); + _isOpenGL20Supported = getGLVersionNumber() >= 2.0; + _isSeparateStencilSupported = isGLExtensionSupported(contextID, "GL_ATI_separate_stencil"); - setGLExtensionFuncPtr(_glActiveStencilFace, "glActiveStencilFace","glActiveStencilFaceEXT"); + // function pointers + setGLExtensionFuncPtr(_glActiveStencilFace, "glActiveStencilFaceEXT"); + setGLExtensionFuncPtr(_glStencilOpSeparate, "glStencilOpSeparate", "glStencilOpSeparateATI"); + setGLExtensionFuncPtr(_glStencilMaskSeparate, "glStencilMaskSeparate"); + setGLExtensionFuncPtr(_glStencilFuncSeparate, "glStencilFuncSeparate"); + setGLExtensionFuncPtr(_glStencilFuncSeparateATI, "glStencilFuncSeparateATI"); + + // protect against buggy drivers (maybe not necessary) + if (!_glActiveStencilFace) _isStencilTwoSidedSupported = false; + if (!_glStencilOpSeparate) { _isOpenGL20Supported = false; _isSeparateStencilSupported = false; } + if (!_glStencilMaskSeparate) _isOpenGL20Supported = false; + if (!_glStencilFuncSeparate) _isOpenGL20Supported = false; + if (!_glStencilFuncSeparateATI) _isSeparateStencilSupported = false; } void StencilTwoSided::Extensions::glActiveStencilFace(GLenum face) const { - if (_glActiveStencilFace) + if (_isStencilTwoSidedSupported) { _glActiveStencilFace(face); } - else - { - OSG_WARN<<"Error: glActiveStencilFace not supported by OpenGL driver"<