diff --git a/include/cinder/gl/Context.h b/include/cinder/gl/Context.h index d668271735..34646e8a63 100755 --- a/include/cinder/gl/Context.h +++ b/include/cinder/gl/Context.h @@ -170,6 +170,28 @@ class CI_API Context { void popFrontFace( bool forceRestore = false ); //! Returns the winding order defining front-facing polygons, either \c GL_CW or \c GL_CCW (the default). GLenum getFrontFace(); + + //! Set the current color mask, which enables and disables writing of frame buffer color components. + void colorMask( bool red, bool green, bool blue, bool alpha ); + //! Push the current color mask, which enables and disables writing of frame buffer color components. + void pushColorMask( bool red, bool green, bool blue, bool alpha ); + //! Pops the current color mask, which enables and disables writing of frame buffer color components. + void popColorMask( bool forceRestore = false ); + //! Returns the current color mask, which enables and disables writing of frame buffer color components. Default is \c GL_TRUE for all 4 components (red, green, blue, alpha). + glm::bvec4 getColorMask(); + + //! Set the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + void stencilMask( GLuint mask ); + //! Set the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + void stencilMask( GLuint front, GLuint back ); + //! Push the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + void pushStencilMask( GLuint mask ); + //! Push the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + void pushStencilMask( GLuint front, GLuint back ); + //! Pops the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + void popStencilMask( bool forceRestore = false ); + //! Returns the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back). + glm::u8vec2 getStencilMask(); #if ! defined( CINDER_GL_ES ) //! Analogous to glLogicOp( \a mode ). Valid arguments are \c GL_CLEAR, \c GL_SET, \c GL_COPY, \c GL_COPY_INVERTED, \c GL_NOOP, \c GL_INVERT, \c GL_AND, \c GL_NAND, \c GL_OR, \c GL_NOR, \c GL_XOR, \c GL_EQUIV, \c GL_AND_REVERSE, \c GL_AND_INVERTED, \c GL_OR_REVERSE, or \c GL_OR_INVERTED. @@ -535,6 +557,9 @@ class CI_API Context { std::vector mCullFaceStack; std::vector mFrontFaceStack; + std::vector mColorMaskStack; + std::vector mStencilMaskStack; + #if ! defined( CINDER_GL_ES ) std::vector mLogicOpStack; std::vector mPolygonModeStack; diff --git a/include/cinder/gl/scoped.h b/include/cinder/gl/scoped.h index 9101989f0f..d4a28eaa19 100644 --- a/include/cinder/gl/scoped.h +++ b/include/cinder/gl/scoped.h @@ -360,6 +360,30 @@ struct CI_API ScopedFrontFace : private Noncopyable { Context *mCtx; }; +//! Scopes writing of frame buffer color components +class ScopedColorMask : private Noncopyable { + public: + //! Values for \a red, \a green, \a blue and \a alpha may be \c GL_TRUE or \c GL_FALSE + ScopedColorMask( GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha ); + ~ScopedColorMask(); + + private: + Context *mCtx; +}; + +//! Scopes the front and back writing of individual bits in the stencil planes (front and back) +class ScopedStencilMask : private Noncopyable { + public: + //! Values for \a mask may be between \c 0x00 and \c 0xFF + ScopedStencilMask( GLuint mask ); + //! Values for \a front and \a back may be between \c 0x00 and \c 0xFF + ScopedStencilMask( GLuint front, GLuint back ); + ~ScopedStencilMask(); + + private: + Context *mCtx; +}; + #if defined( CINDER_GL_HAS_KHR_DEBUG ) //! Scopes debug group message diff --git a/src/cinder/gl/Context.cpp b/src/cinder/gl/Context.cpp index 87ebec7f58..5248cf4e5c 100755 --- a/src/cinder/gl/Context.cpp +++ b/src/cinder/gl/Context.cpp @@ -87,6 +87,12 @@ Context::Context( const std::shared_ptr &platformData ) #endif mDefaultArrayVboIdx = 0; + // initial state for color mask is enabled + mColorMaskStack.emplace_back( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + + // initial state for stencil mask is all bits enabled + mStencilMaskStack.emplace_back( 0xFF, 0xFF ); + // initial state for depth mask is enabled mBoolStateStack[GL_DEPTH_WRITEMASK] = vector(); mBoolStateStack[GL_DEPTH_WRITEMASK].push_back( GL_TRUE ); @@ -470,6 +476,101 @@ GLenum Context::getFrontFace() return mFrontFaceStack.back(); } +////////////////////////////////////////////////////////////////// +// ColorMask +void Context::colorMask( bool red, bool green, bool blue, bool alpha ) +{ + if( setStackState( mColorMaskStack, glm::bvec4( red, green, blue, alpha ) ) ) { + glColorMask( red, green, blue, alpha ); + } +} + +void Context::pushColorMask( bool red, bool green, bool blue, bool alpha ) +{ + if( pushStackState( mColorMaskStack, glm::bvec4( red, green, blue, alpha ) ) ) { + glColorMask( red, green, blue, alpha ); + } +} + +void Context::popColorMask( bool forceRestore ) +{ + if( mColorMaskStack.empty() ) + CI_LOG_E( "Color mask stack underflow" ); + else if( popStackState( mColorMaskStack ) || forceRestore ) { + const auto mask = getColorMask(); + glColorMask( mask.r, mask.g, mask.b, mask.a ); + } +} + +glm::bvec4 Context::getColorMask() +{ + if( mColorMaskStack.empty() ) { + GLboolean queriedBool[4]; + glGetBooleanv( GL_COLOR_WRITEMASK, queriedBool ); + mColorMaskStack.emplace_back( queriedBool[0], queriedBool[1], queriedBool[2], queriedBool[3] ); // push twice + mColorMaskStack.emplace_back( queriedBool[0], queriedBool[1], queriedBool[2], queriedBool[3] ); + } + + return mColorMaskStack.back(); +} + +////////////////////////////////////////////////////////////////// +// StencilMask +void Context::stencilMask( GLuint mask ) +{ + if( setStackState( mStencilMaskStack, glm::u8vec2( mask, mask ) ) ) { + glStencilMask( mask ); + } +} + +void Context::stencilMask( GLuint front, GLuint back ) +{ + if( setStackState( mStencilMaskStack, glm::u8vec2( front, back ) ) ) { + glStencilMaskSeparate( GL_FRONT, front ); + glStencilMaskSeparate( GL_BACK, back ); + } +} + +void Context::pushStencilMask( GLuint mask ) +{ + if( pushStackState( mStencilMaskStack, glm::u8vec2( mask, mask ) ) ) { + glStencilMask( mask ); + } +} + +void Context::pushStencilMask( GLuint front, GLuint back ) +{ + if( pushStackState( mStencilMaskStack, glm::u8vec2( front, back ) ) ) { + glStencilMaskSeparate( GL_FRONT, front ); + glStencilMaskSeparate( GL_BACK, back ); + } +} + +void Context::popStencilMask( bool forceRestore ) +{ + if( mStencilMaskStack.empty() ) + CI_LOG_E( "Stencil mask stack underflow" ); + else if( popStackState( mStencilMaskStack ) || forceRestore ) { + const auto mask = getStencilMask(); + glStencilMaskSeparate( GL_FRONT, mask[0] ); + glStencilMaskSeparate( GL_BACK, mask[1] ); + } +} + +glm::u8vec2 Context::getStencilMask() +{ + if( mStencilMaskStack.empty() ) { + GLint queriedIntFront; + GLint queriedIntBack; + glGetIntegerv( GL_STENCIL_WRITEMASK, &queriedIntFront ); + glGetIntegerv( GL_STENCIL_BACK_WRITEMASK, &queriedIntBack ); + mStencilMaskStack.emplace_back( queriedIntFront, queriedIntBack ); // push twice + mStencilMaskStack.emplace_back( queriedIntFront, queriedIntBack ); + } + + return mStencilMaskStack.back(); +} + ////////////////////////////////////////////////////////////////// // LogicOp #if ! defined( CINDER_GL_ES ) diff --git a/src/cinder/gl/scoped.cpp b/src/cinder/gl/scoped.cpp index 2f2122ead9..f3d8d439ca 100644 --- a/src/cinder/gl/scoped.cpp +++ b/src/cinder/gl/scoped.cpp @@ -513,6 +513,38 @@ ScopedFrontFace::~ScopedFrontFace() mCtx->popFrontFace(); } +/////////////////////////////////////////////////////////////////////////////////////////// +// ScopedColorMask +ScopedColorMask::ScopedColorMask( GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha ) + : mCtx( gl::context() ) +{ + mCtx->pushColorMask( red, green, blue, alpha ); +} + +ScopedColorMask::~ScopedColorMask() +{ + mCtx->popColorMask(); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// ScopedStencilMask +ScopedStencilMask::ScopedStencilMask( GLuint mask ) + : mCtx( gl::context() ) +{ + mCtx->pushStencilMask( mask ); +} + +ScopedStencilMask::ScopedStencilMask( GLuint front, GLuint back ) + : mCtx( gl::context() ) +{ + mCtx->pushStencilMask( front, back ); +} + +ScopedStencilMask::~ScopedStencilMask() +{ + mCtx->popStencilMask(); +} + /////////////////////////////////////////////////////////////////////////////////////////// // ScopedDebugGroup #if defined( CINDER_GL_HAS_KHR_DEBUG )