Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions include/cinder/gl/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,44 @@ class CI_API Context {
#endif
};

struct CI_API StencilOp
{
StencilOp() = default;
StencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass) : stencilFail(stencilFail), depthFail(depthFail), depthPass(depthPass) {}

bool operator==(const StencilOp& rhs) const
{
return stencilFail == rhs.stencilFail && depthFail == rhs.depthFail && depthPass == rhs.depthPass;
}
bool operator!=(const StencilOp& rhs) const
{
return !(*this == rhs);
}

GLenum stencilFail;
GLenum depthFail;
GLenum depthPass;
};

struct CI_API StencilFunc
{
StencilFunc() = default;
StencilFunc(GLenum func, GLint ref, GLuint mask) : func(func), ref(ref), mask(mask) {}

bool operator==(const StencilFunc& rhs) const
{
return func == rhs.func && ref == rhs.ref && mask == rhs.mask;
}
bool operator!=(const StencilFunc& rhs) const
{
return !(*this == rhs);
}

GLenum func;
GLint ref;
GLuint mask;
};

//! 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.
static ContextRef create( const Context *sharedContext );
//! 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.
Expand Down Expand Up @@ -192,6 +230,36 @@ class CI_API Context {
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();

//! Set the current stencil operation, which controls the front and back stencil test actions.
void stencilOp( GLenum stencilFail, GLenum depthFail, GLenum depthPass );
//! Set the current stencil operation, which controls the front or back stencil test actions.
void stencilOpSeparate( GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass );
//! Push the current stencil operation, which controls the front and back stencil test actions.
void pushStencilOp( GLenum stencilFail, GLenum depthFail, GLenum depthPass );
//! Push the current stencil operation, which controls the front or back stencil test actions.
void pushStencilOpSeparate( GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass );
//! Pops the current stencil operation, which controls the front and back stencil test actions.
void popStencilOp( bool forceRestore = false );
//! Pops the current stencil operation, which controls the front or back stencil test actions.
void popStencilOpSeparate( GLenum face, bool forceRestore = false );
//! Returns the current stencil operation, which controls the front and back stencil test actions.
std::pair<StencilOp,StencilOp> getStencilOp();

//! Set the current stencil function, which controls the front and back stencil test actions.
void stencilFunc( GLenum func, GLint ref, GLuint mask );
//! Set the current stencil function, which controls the front or back stencil test actions.
void stencilFuncSeparate( GLenum face, GLenum func, GLint ref, GLuint mask );
//! Push the current stencil function, which controls the front and back stencil test actions.
void pushStencilFunc( GLenum func, GLint ref, GLuint mask );
//! Push the current stencil function, which controls the front or back stencil test actions.
void pushStencilFuncSeparate( GLenum face, GLenum func, GLint ref, GLuint mask );
//! Pops the current stencil function, which controls the front and back stencil test actions.
void popStencilFunc( bool forceRestore = false );
//! Pops the current stencil function, which controls the front or back stencil test actions.
void popStencilFuncSeparate( GLenum face, bool forceRestore = false );
//! Returns the current stencil function, which controls the front and back stencil test actions.
std::pair<StencilFunc,StencilFunc> getStencilFunc();

#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.
Expand Down Expand Up @@ -559,6 +627,10 @@ class CI_API Context {

std::vector<glm::bvec4> mColorMaskStack;
std::vector<glm::u8vec2> mStencilMaskStack;
std::vector<StencilOp> mStencilOpFrontStack;
std::vector<StencilOp> mStencilOpBackStack;
std::vector<StencilFunc> mStencilFuncFrontStack;
std::vector<StencilFunc> mStencilFuncBackStack;

#if ! defined( CINDER_GL_ES )
std::vector<GLenum> mLogicOpStack;
Expand Down
28 changes: 28 additions & 0 deletions include/cinder/gl/scoped.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,34 @@ class ScopedStencilMask : private Noncopyable {
Context *mCtx;
};

//! Scopes the front and/or back stencil buffer operation
class ScopedStencilOp : private Noncopyable {
public:
//!
ScopedStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
//!
ScopedStencilOp(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
~ScopedStencilOp();

private:
Context* mCtx;
GLenum mFace;
};

//! Scopes the front and/or back stencil buffer function
class ScopedStencilFunc : private Noncopyable {
public:
//!
ScopedStencilFunc(GLenum func, GLint ref, GLuint mask);
//!
ScopedStencilFunc(GLenum face, GLenum func, GLint ref, GLuint mask);
~ScopedStencilFunc();

private:
Context* mCtx;
GLenum mFace;
};

#if defined( CINDER_GL_HAS_KHR_DEBUG )

//! Scopes debug group message
Expand Down
250 changes: 250 additions & 0 deletions src/cinder/gl/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ Context::Context( const std::shared_ptr<PlatformData> &platformData )
// initial state for stencil mask is all bits enabled
mStencilMaskStack.emplace_back( 0xFF, 0xFF );

// initial state for stencil op is GL_KEEP, GL_KEEP, GL_KEEP
mStencilOpFrontStack.emplace_back(GL_KEEP, GL_KEEP, GL_KEEP);
mStencilOpBackStack.emplace_back(GL_KEEP, GL_KEEP, GL_KEEP);

// initial state for stencil func is GL_ALWAYS, 0, 0xFF
mStencilFuncFrontStack.emplace_back(GL_ALWAYS, 0, 0xFF);
mStencilFuncBackStack.emplace_back(GL_ALWAYS, 0, 0xFF);

// initial state for depth mask is enabled
mBoolStateStack[GL_DEPTH_WRITEMASK] = vector<GLboolean>();
mBoolStateStack[GL_DEPTH_WRITEMASK].push_back( GL_TRUE );
Expand Down Expand Up @@ -571,6 +579,248 @@ glm::u8vec2 Context::getStencilMask()
return mStencilMaskStack.back();
}

void Context::stencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass)
{
stencilOpSeparate( GL_FRONT_AND_BACK, stencilFail, depthFail, depthPass );
}

void Context::stencilOpSeparate(GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass)
{
switch (face)
{
case GL_FRONT:
if (setStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
break;
case GL_BACK:
if (setStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
break;
default:
if (setStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
if (setStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
break;
}
}

void Context::pushStencilOp(GLenum stencilFail, GLenum depthFail, GLenum depthPass)
{
pushStencilOpSeparate( GL_FRONT_AND_BACK, stencilFail, depthFail, depthPass );
}

void Context::pushStencilOpSeparate(GLenum face, GLenum stencilFail, GLenum depthFail, GLenum depthPass)
{
switch (face)
{
case GL_FRONT:
if (pushStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
break;
case GL_BACK:
if (pushStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
break;
default:
if (pushStackState(mStencilOpFrontStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_FRONT, stencilFail, depthFail, depthPass);
if (pushStackState(mStencilOpBackStack, StencilOp{ stencilFail, depthFail, depthPass }))
glStencilOpSeparate(GL_BACK, stencilFail, depthFail, depthPass);
break;
}
}

void Context::popStencilOp(bool forceRestore)
{
popStencilOpSeparate(GL_FRONT, forceRestore);
popStencilOpSeparate(GL_BACK, forceRestore);
}

void Context::popStencilOpSeparate(GLenum face, bool forceRestore)
{
switch (face)
{
case GL_FRONT:
if (mStencilOpFrontStack.empty())
CI_LOG_E("Stencil operation stack underflow");
else if (popStackState(mStencilOpFrontStack) || forceRestore) {
const auto op = getStencilOp();
glStencilOpSeparate(GL_FRONT, op.first.stencilFail, op.first.depthFail, op.first.depthPass);
}
break;
case GL_BACK:
if (mStencilOpBackStack.empty())
CI_LOG_E("Stencil operation stack underflow");
else if (popStackState(mStencilOpBackStack) || forceRestore) {
const auto op = getStencilOp();
glStencilOpSeparate(GL_BACK, op.second.stencilFail, op.second.depthFail, op.second.depthPass);
}
break;
default:
if (mStencilOpFrontStack.empty() || mStencilOpBackStack.empty())
CI_LOG_E("Stencil operation stack underflow");
else {
if (popStackState(mStencilOpFrontStack) || forceRestore) {
const auto op = getStencilOp();
glStencilOpSeparate(GL_FRONT, op.first.stencilFail, op.first.depthFail, op.first.depthPass);
}
if (popStackState(mStencilOpBackStack) || forceRestore) {
const auto op = getStencilOp();
glStencilOpSeparate(GL_BACK, op.second.stencilFail, op.second.depthFail, op.second.depthPass);
}
}
break;
}
}

std::pair<Context::StencilOp, Context::StencilOp> Context::getStencilOp()
{
if (mStencilOpFrontStack.empty()) {
GLint queriedInt[3];
glGetIntegerv(GL_STENCIL_FAIL, queriedInt);
glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, queriedInt + 1);
glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, queriedInt + 2);
// push twice in anticipation of later pop
mStencilOpFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
mStencilOpFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
}

if (mStencilOpBackStack.empty()) {
GLint queriedInt[3];
glGetIntegerv(GL_STENCIL_BACK_FAIL, queriedInt);
glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, queriedInt + 1);
glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, queriedInt + 2);
// push twice in anticipation of later pop
mStencilOpBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
mStencilOpBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
}

return std::make_pair(mStencilOpFrontStack.back(), mStencilOpBackStack.back());
}

void Context::stencilFunc(GLenum func, GLint ref, GLuint mask)
{
stencilFuncSeparate(GL_FRONT, func, ref, mask);
stencilFuncSeparate(GL_BACK, func, ref, mask);
}

void Context::stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
{
switch (face)
{
case GL_FRONT:
if (setStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
break;
case GL_BACK:
if (setStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_BACK, func, ref, mask);
break;
default:
if (setStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
if (setStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_BACK, func, ref, mask);
break;
}
}

void Context::pushStencilFunc(GLenum func, GLint ref, GLuint mask)
{
pushStencilFuncSeparate(GL_FRONT, func, ref, mask);
pushStencilFuncSeparate(GL_BACK, func, ref, mask);
}

void Context::pushStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
{
switch (face)
{
case GL_FRONT:
if (pushStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
break;
case GL_BACK:
if (pushStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_BACK, func, ref, mask);
break;
default:
if (pushStackState(mStencilFuncFrontStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_FRONT, func, ref, mask);
if (pushStackState(mStencilFuncBackStack, StencilFunc{ func, ref, mask }))
glStencilFuncSeparate(GL_BACK, func, ref, mask);
break;
}
}

void Context::popStencilFunc(bool forceRestore)
{
popStencilFuncSeparate(GL_FRONT, forceRestore);
popStencilFuncSeparate(GL_BACK, forceRestore);
}

void Context::popStencilFuncSeparate(GLenum face, bool forceRestore)
{
switch (face)
{
case GL_FRONT:
if (mStencilFuncFrontStack.empty())
CI_LOG_E("Stencil function stack underflow");
else if (popStackState(mStencilFuncFrontStack) || forceRestore) {
const auto func = getStencilFunc();
glStencilFuncSeparate(GL_FRONT, func.first.func, func.first.ref, func.first.mask);
}
break;
case GL_BACK:
if (mStencilFuncBackStack.empty())
CI_LOG_E("Stencil function stack underflow");
else if (popStackState(mStencilFuncBackStack) || forceRestore) {
const auto func = getStencilFunc();
glStencilFuncSeparate(GL_BACK, func.second.func, func.second.ref, func.second.mask);
}
break;
default:
if (mStencilFuncFrontStack.empty() || mStencilFuncBackStack.empty())
CI_LOG_E("Stencil function stack underflow");
else {
if (popStackState(mStencilFuncFrontStack) || forceRestore) {
const auto func = getStencilFunc();
glStencilFuncSeparate(GL_FRONT, func.first.func, func.first.ref, func.first.mask);
}
if (popStackState(mStencilFuncBackStack) || forceRestore) {
const auto func = getStencilFunc();
glStencilFuncSeparate(GL_BACK, func.second.func, func.second.ref, func.second.mask);
}
}
break;
}
}

std::pair<Context::StencilFunc, Context::StencilFunc> Context::getStencilFunc()
{
if (mStencilFuncFrontStack.empty()) {
GLint queriedInt[3];
glGetIntegerv(GL_STENCIL_FUNC, queriedInt);
glGetIntegerv(GL_STENCIL_REF, queriedInt + 1);
glGetIntegerv(GL_STENCIL_VALUE_MASK, queriedInt + 2);
// push twice in anticipation of later pop
mStencilFuncFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
mStencilFuncFrontStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
}

if (mStencilFuncBackStack.empty()) {
GLint queriedInt[3];
glGetIntegerv(GL_STENCIL_BACK_FUNC, queriedInt);
glGetIntegerv(GL_STENCIL_BACK_REF, queriedInt + 1);
glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, queriedInt + 2);
// push twice in anticipation of later pop
mStencilFuncBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
mStencilFuncBackStack.emplace_back(queriedInt[0], queriedInt[1], queriedInt[2]);
}

return std::make_pair(mStencilFuncFrontStack.back(), mStencilFuncBackStack.back());
}

//////////////////////////////////////////////////////////////////
// LogicOp
#if ! defined( CINDER_GL_ES )
Expand Down
Loading
Loading