Skip to content

Commit 7631012

Browse files
paulhouxandrewfb
authored andcommitted
Added gl::StencilOp, gl::stencilOpSeparate, gl::StencilFunc and gl::stencilFuncSeparate methods, as well as their push/pop/get variants, to control stencil buffer state. Also added matching gl::ScopedStencilOp and gl::ScopedStencilFunc classes.
1 parent 93b91a8 commit 7631012

File tree

4 files changed

+378
-0
lines changed

4 files changed

+378
-0
lines changed

include/cinder/gl/Context.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,44 @@ class CI_API Context {
7676
#endif
7777
};
7878

79+
struct CI_API StencilOp
80+
{
81+
StencilOp() = default;
82+
StencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass) : stencilFail(stencilFail), depthFail(depthFail), depthPass(depthPass) {}
83+
84+
bool operator==(const StencilOp& rhs) const
85+
{
86+
return stencilFail == rhs.stencilFail && depthFail == rhs.depthFail && depthPass == rhs.depthPass;
87+
}
88+
bool operator!=(const StencilOp& rhs) const
89+
{
90+
return !(*this == rhs);
91+
}
92+
93+
GLenum stencilFail;
94+
GLenum depthFail;
95+
GLenum depthPass;
96+
};
97+
98+
struct CI_API StencilFunc
99+
{
100+
StencilFunc() = default;
101+
StencilFunc(GLenum func, GLint ref, GLuint mask) : func(func), ref(ref), mask(mask) {}
102+
103+
bool operator==(const StencilFunc& rhs) const
104+
{
105+
return func == rhs.func && ref == rhs.ref && mask == rhs.mask;
106+
}
107+
bool operator!=(const StencilFunc& rhs) const
108+
{
109+
return !(*this == rhs);
110+
}
111+
112+
GLenum func;
113+
GLint ref;
114+
GLuint mask;
115+
};
116+
79117
//! Creates a new OpenGL context, sharing resources and pixel format with sharedContext. This (essentially) must be done from the primary thread on MSW. ANGLE doesn't support multithreaded use. Destroys the platform Context on destruction.
80118
static ContextRef create( const Context *sharedContext );
81119
//! Creates based on an existing platform-specific GL context. \a platformContext is CGLContextObj on Mac OS X, EAGLContext on iOS, HGLRC on MSW. \a platformContext is an HDC on MSW and ignored elsewhere. Does not assume ownership of the platform's context.
@@ -192,6 +230,36 @@ class CI_API Context {
192230
void popStencilMask( bool forceRestore = false );
193231
//! Returns the current stencil mask, which controls the front and back writing of individual bits in the stencil planes (front and back).
194232
glm::u8vec2 getStencilMask();
233+
234+
//! Set the current stencil operation, which controls the front and back stencil test actions.
235+
void stencilOp( GLenum stencilFail, GLenum depthFail, GLenum depthPass );
236+
//! Set the current stencil operation, which controls the front or back stencil test actions.
237+
void stencilOpSeparate( GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass );
238+
//! Push the current stencil operation, which controls the front and back stencil test actions.
239+
void pushStencilOp( GLenum stencilFail, GLenum depthFail, GLenum depthPass );
240+
//! Push the current stencil operation, which controls the front or back stencil test actions.
241+
void pushStencilOpSeparate( GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass );
242+
//! Pops the current stencil operation, which controls the front and back stencil test actions.
243+
void popStencilOp( bool forceRestore = false );
244+
//! Pops the current stencil operation, which controls the front or back stencil test actions.
245+
void popStencilOpSeparate( GLenum face, bool forceRestore = false );
246+
//! Returns the current stencil operation, which controls the front and back stencil test actions.
247+
std::pair<StencilOp,StencilOp> getStencilOp();
248+
249+
//! Set the current stencil function, which controls the front and back stencil test actions.
250+
void stencilFunc( GLenum func, GLint ref, GLuint mask );
251+
//! Set the current stencil function, which controls the front or back stencil test actions.
252+
void stencilFuncSeparate( GLenum face, GLenum func, GLint ref, GLuint mask );
253+
//! Push the current stencil function, which controls the front and back stencil test actions.
254+
void pushStencilFunc( GLenum func, GLint ref, GLuint mask );
255+
//! Push the current stencil function, which controls the front or back stencil test actions.
256+
void pushStencilFuncSeparate( GLenum face, GLenum func, GLint ref, GLuint mask );
257+
//! Pops the current stencil function, which controls the front and back stencil test actions.
258+
void popStencilFunc( bool forceRestore = false );
259+
//! Pops the current stencil function, which controls the front or back stencil test actions.
260+
void popStencilFuncSeparate( GLenum face, bool forceRestore = false );
261+
//! Returns the current stencil function, which controls the front and back stencil test actions.
262+
std::pair<StencilFunc,StencilFunc> getStencilFunc();
195263

196264
#if ! defined( CINDER_GL_ES )
197265
//! 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.
@@ -559,6 +627,10 @@ class CI_API Context {
559627

560628
std::vector<glm::bvec4> mColorMaskStack;
561629
std::vector<glm::u8vec2> mStencilMaskStack;
630+
std::vector<StencilOp> mStencilOpFrontStack;
631+
std::vector<StencilOp> mStencilOpBackStack;
632+
std::vector<StencilFunc> mStencilFuncFrontStack;
633+
std::vector<StencilFunc> mStencilFuncBackStack;
562634

563635
#if ! defined( CINDER_GL_ES )
564636
std::vector<GLenum> mLogicOpStack;

include/cinder/gl/scoped.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,34 @@ class ScopedStencilMask : private Noncopyable {
384384
Context *mCtx;
385385
};
386386

387+
//! Scopes the front and/or back stencil buffer operation
388+
class ScopedStencilOp : private Noncopyable {
389+
public:
390+
//!
391+
ScopedStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
392+
//!
393+
ScopedStencilOp(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
394+
~ScopedStencilOp();
395+
396+
private:
397+
Context* mCtx;
398+
GLenum mFace;
399+
};
400+
401+
//! Scopes the front and/or back stencil buffer function
402+
class ScopedStencilFunc : private Noncopyable {
403+
public:
404+
//!
405+
ScopedStencilFunc(GLenum func, GLint ref, GLuint mask);
406+
//!
407+
ScopedStencilFunc(GLenum face, GLenum func, GLint ref, GLuint mask);
408+
~ScopedStencilFunc();
409+
410+
private:
411+
Context* mCtx;
412+
GLenum mFace;
413+
};
414+
387415
#if defined( CINDER_GL_HAS_KHR_DEBUG )
388416

389417
//! Scopes debug group message

src/cinder/gl/Context.cpp

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,248 @@ glm::u8vec2 Context::getStencilMask()
571571
return mStencilMaskStack.back();
572572
}
573573

574+
void Context::stencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass)
575+
{
576+
stencilOpSeparate( GL_FRONT_AND_BACK, stencilFail, depthFail, depthPass );
577+
}
578+
579+
void Context::stencilOpSeparate(GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass)
580+
{
581+
switch (face)
582+
{
583+
case GL_FRONT:
584+
if (setStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
585+
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
586+
break;
587+
case GL_BACK:
588+
if (setStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
589+
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
590+
break;
591+
default:
592+
if (setStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
593+
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
594+
if (setStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
595+
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
596+
break;
597+
}
598+
}
599+
600+
void Context::pushStencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass)
601+
{
602+
pushStencilOpSeparate( GL_FRONT_AND_BACK, stencilFail, depthFail, depthPass );
603+
}
604+
605+
void Context::pushStencilOpSeparate(GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass)
606+
{
607+
switch (face)
608+
{
609+
case GL_FRONT:
610+
if (pushStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
611+
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
612+
break;
613+
case GL_BACK:
614+
if (pushStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
615+
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
616+
break;
617+
default:
618+
if (pushStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
619+
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
620+
if (pushStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
621+
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
622+
break;
623+
}
624+
}
625+
626+
void Context::popStencilOp(bool forceRestore)
627+
{
628+
popStencilOpSeparate(GL_FRONT, forceRestore);
629+
popStencilOpSeparate(GL_BACK, forceRestore);
630+
}
631+
632+
void Context::popStencilOpSeparate(GLenum face, bool forceRestore)
633+
{
634+
switch (face)
635+
{
636+
case GL_FRONT:
637+
if (mStencilOpFrontStack.empty())
638+
CI_LOG_E("Stencil operation stack underflow");
639+
else if (popStackState(mStencilOpFrontStack) || forceRestore) {
640+
const auto op = getStencilOp();
641+
glStencilOpSeparate(GL_FRONT, op.first.stencilFail, op.first.depthFail, op.first.depthPass);
642+
}
643+
break;
644+
case GL_BACK:
645+
if (mStencilOpBackStack.empty())
646+
CI_LOG_E("Stencil operation stack underflow");
647+
else if (popStackState(mStencilOpBackStack) || forceRestore) {
648+
const auto op = getStencilOp();
649+
glStencilOpSeparate(GL_BACK, op.second.stencilFail, op.second.depthFail, op.second.depthPass);
650+
}
651+
break;
652+
default:
653+
if (mStencilOpFrontStack.empty() || mStencilOpBackStack.empty())
654+
CI_LOG_E("Stencil operation stack underflow");
655+
else {
656+
if (popStackState(mStencilOpFrontStack) || forceRestore) {
657+
const auto op = getStencilOp();
658+
glStencilOpSeparate(GL_FRONT, op.first.stencilFail, op.first.depthFail, op.first.depthPass);
659+
}
660+
if (popStackState(mStencilOpBackStack) || forceRestore) {
661+
const auto op = getStencilOp();
662+
glStencilOpSeparate(GL_BACK, op.second.stencilFail, op.second.depthFail, op.second.depthPass);
663+
}
664+
}
665+
break;
666+
}
667+
}
668+
669+
std::pair<Context::StencilOp, Context::StencilOp> Context::getStencilOp()
670+
{
671+
if (mStencilOpFrontStack.empty()) {
672+
GLint queriedInt[3];
673+
glGetIntegerv(GL_STENCIL_FAIL, queriedInt);
674+
glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, queriedInt + 1);
675+
glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, queriedInt + 2);
676+
// push twice in anticipation of later pop
677+
mStencilOpFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
678+
mStencilOpFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
679+
}
680+
681+
if (mStencilOpBackStack.empty()) {
682+
GLint queriedInt[3];
683+
glGetIntegerv(GL_STENCIL_BACK_FAIL, queriedInt);
684+
glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, queriedInt + 1);
685+
glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, queriedInt + 2);
686+
// push twice in anticipation of later pop
687+
mStencilOpBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
688+
mStencilOpBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
689+
}
690+
691+
return std::make_pair(mStencilOpFrontStack.back(), mStencilOpBackStack.back());
692+
}
693+
694+
void Context::stencilFunc(GLenum func, GLint ref, GLuint mask)
695+
{
696+
stencilFuncSeparate(GL_FRONT, func, ref, mask);
697+
stencilFuncSeparate(GL_BACK, func, ref, mask);
698+
}
699+
700+
void Context::stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
701+
{
702+
switch (face)
703+
{
704+
case GL_FRONT:
705+
if (setStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
706+
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
707+
break;
708+
case GL_BACK:
709+
if (setStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
710+
glStencilFuncSeparate(GL_BACK, func, ref, mask);
711+
break;
712+
default:
713+
if (setStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
714+
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
715+
if (setStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
716+
glStencilFuncSeparate(GL_BACK, func, ref, mask);
717+
break;
718+
}
719+
}
720+
721+
void Context::pushStencilFunc(GLenum func, GLint ref, GLuint mask)
722+
{
723+
pushStencilFuncSeparate(GL_FRONT, func, ref, mask);
724+
pushStencilFuncSeparate(GL_BACK, func, ref, mask);
725+
}
726+
727+
void Context::pushStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
728+
{
729+
switch (face)
730+
{
731+
case GL_FRONT:
732+
if (pushStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
733+
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
734+
break;
735+
case GL_BACK:
736+
if (pushStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
737+
glStencilFuncSeparate(GL_BACK, func, ref, mask);
738+
break;
739+
default:
740+
if (pushStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
741+
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
742+
if (pushStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
743+
glStencilFuncSeparate(GL_BACK, func, ref, mask);
744+
break;
745+
}
746+
}
747+
748+
void Context::popStencilFunc(bool forceRestore)
749+
{
750+
popStencilFuncSeparate(GL_FRONT, forceRestore);
751+
popStencilFuncSeparate(GL_BACK, forceRestore);
752+
}
753+
754+
void Context::popStencilFuncSeparate(GLenum face, bool forceRestore)
755+
{
756+
switch (face)
757+
{
758+
case GL_FRONT:
759+
if (mStencilFuncFrontStack.empty())
760+
CI_LOG_E("Stencil function stack underflow");
761+
else if (popStackState(mStencilFuncFrontStack) || forceRestore) {
762+
const auto func = getStencilFunc();
763+
glStencilFuncSeparate(GL_FRONT, func.first.func, func.first.ref, func.first.mask);
764+
}
765+
break;
766+
case GL_BACK:
767+
if (mStencilFuncBackStack.empty())
768+
CI_LOG_E("Stencil function stack underflow");
769+
else if (popStackState(mStencilFuncBackStack) || forceRestore) {
770+
const auto func = getStencilFunc();
771+
glStencilFuncSeparate(GL_BACK, func.second.func, func.second.ref, func.second.mask);
772+
}
773+
break;
774+
default:
775+
if (mStencilFuncFrontStack.empty() || mStencilFuncBackStack.empty())
776+
CI_LOG_E("Stencil function stack underflow");
777+
else {
778+
if (popStackState(mStencilFuncFrontStack) || forceRestore) {
779+
const auto func = getStencilFunc();
780+
glStencilFuncSeparate(GL_FRONT, func.first.func, func.first.ref, func.first.mask);
781+
}
782+
if (popStackState(mStencilFuncBackStack) || forceRestore) {
783+
const auto func = getStencilFunc();
784+
glStencilFuncSeparate(GL_BACK, func.second.func, func.second.ref, func.second.mask);
785+
}
786+
}
787+
break;
788+
}
789+
}
790+
791+
std::pair<Context::StencilFunc, Context::StencilFunc> Context::getStencilFunc()
792+
{
793+
if (mStencilFuncFrontStack.empty()) {
794+
GLint queriedInt[3];
795+
glGetIntegerv(GL_STENCIL_FUNC, queriedInt);
796+
glGetIntegerv(GL_STENCIL_REF, queriedInt + 1);
797+
glGetIntegerv(GL_STENCIL_VALUE_MASK, queriedInt + 2);
798+
// push twice in anticipation of later pop
799+
mStencilFuncFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
800+
mStencilFuncFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
801+
}
802+
803+
if (mStencilFuncBackStack.empty()) {
804+
GLint queriedInt[3];
805+
glGetIntegerv(GL_STENCIL_BACK_FUNC, queriedInt);
806+
glGetIntegerv(GL_STENCIL_BACK_REF, queriedInt + 1);
807+
glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, queriedInt + 2);
808+
// push twice in anticipation of later pop
809+
mStencilFuncBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
810+
mStencilFuncBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
811+
}
812+
813+
return std::make_pair(mStencilFuncFrontStack.back(), mStencilFuncBackStack.back());
814+
}
815+
574816
//////////////////////////////////////////////////////////////////
575817
// LogicOp
576818
#if ! defined( CINDER_GL_ES )

0 commit comments

Comments
 (0)