Replaced the old stencil API with love.graphics.drawStencil(stencilfunc), love.graphics.setStencilTest(enable, invert), and love.graphics.getStencilTest.

Draw to the stencil buffer (rather than the color buffer) using love.graphics.drawStencil with a function which draws geometry. Once the stencil buffer has been drawn to, use love.graphics.setStencilTest before drawing to let drawn geometry be affected by it.

love.graphics.clear (and Canvas:clear) will clear both the color and stencil buffers by default. love.graphics.clear("stencil") will clear only the stencil buffer.

--HG--
branch : stencil-functions
This commit is contained in:
Alex Szpakowski
2014-08-08 00:25:44 -03:00
parent 53e63d48c9
commit 2b70d84611
7 changed files with 161 additions and 61 deletions
+18
View File
@@ -109,6 +109,16 @@ bool Graphics::getConstant(StackType in, const char *&out)
return stackTypes.find(in, out); return stackTypes.find(in, out);
} }
bool Graphics::getConstant(const char *in, ClearType &out)
{
return clearTypes.find(in, out);
}
bool Graphics::getConstant(ClearType in, const char *&out)
{
return clearTypes.find(in, out);
}
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] = StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{ {
{ "line", Graphics::DRAW_LINE }, { "line", Graphics::DRAW_LINE },
@@ -183,5 +193,13 @@ StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM>::Entry Graphics::stackT
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries)); StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
StringMap<Graphics::ClearType, Graphics::CLEAR_MAX_ENUM>::Entry Graphics::clearTypeEntries[] =
{
{"all", Graphics::CLEAR_ALL},
{"stencil", Graphics::CLEAR_STENCIL},
};
StringMap<Graphics::ClearType, Graphics::CLEAR_MAX_ENUM> Graphics::clearTypes(Graphics::clearTypeEntries, sizeof(Graphics::clearTypeEntries));
} // graphics } // graphics
} // love } // love
+13
View File
@@ -103,6 +103,13 @@ public:
STACK_MAX_ENUM STACK_MAX_ENUM
}; };
enum ClearType
{
CLEAR_ALL,
CLEAR_STENCIL,
CLEAR_MAX_ENUM
};
struct RendererInfo struct RendererInfo
{ {
std::string name; std::string name;
@@ -158,6 +165,9 @@ public:
static bool getConstant(const char *in, StackType &out); static bool getConstant(const char *in, StackType &out);
static bool getConstant(StackType in, const char *&out); static bool getConstant(StackType in, const char *&out);
static bool getConstant(const char *in, ClearType &out);
static bool getConstant(ClearType in, const char *&out);
private: private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[]; static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
@@ -184,6 +194,9 @@ private:
static StringMap<StackType, STACK_MAX_ENUM>::Entry stackTypeEntries[]; static StringMap<StackType, STACK_MAX_ENUM>::Entry stackTypeEntries[];
static StringMap<StackType, STACK_MAX_ENUM> stackTypes; static StringMap<StackType, STACK_MAX_ENUM> stackTypes;
static StringMap<ClearType, CLEAR_MAX_ENUM>::Entry clearTypeEntries[];
static StringMap<ClearType, CLEAR_MAX_ENUM> clearTypes;
}; // Graphics }; // Graphics
} // graphics } // graphics
+7 -1
View File
@@ -823,7 +823,7 @@ void Canvas::clear(Color c)
// Don't use the state-shadowed gl.setClearColor because we want to save // Don't use the state-shadowed gl.setClearColor because we want to save
// the previous clear color. // the previous clear color.
glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]); glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (attachedCanvases.size() > 0) if (attachedCanvases.size() > 0)
strategy->setAttachments(attachedCanvases); strategy->setAttachments(attachedCanvases);
@@ -850,6 +850,12 @@ bool Canvas::checkCreateStencil()
bool success = strategy->createStencil(width, height, msaa_samples, depth_stencil); bool success = strategy->createStencil(width, height, msaa_samples, depth_stencil);
if (success)
{
// We don't want the stencil buffer filled with garbage.
glClear(GL_STENCIL_BUFFER_BIT);
}
if (current && current != this) if (current && current != this)
strategy->bindFBO(current->fbo); strategy->bindFBO(current->fbo);
else if (!current) else if (!current)
+75 -21
View File
@@ -47,7 +47,7 @@ Graphics::Graphics()
: width(0) : width(0)
, height(0) , height(0)
, created(false) , created(false)
, activeStencil(false) , writingToStencil(false)
, displayedMinReqWarning(false) , displayedMinReqWarning(false)
{ {
states.reserve(10); states.reserve(10);
@@ -95,6 +95,8 @@ void Graphics::restoreState(const DisplayState &s)
else else
setScissor(); setScissor();
setStencilTest(s.stencilTest, s.stencilInvert);
setFont(s.font.get()); setFont(s.font.get());
setShader(s.shader.get()); setShader(s.shader.get());
setCanvas(s.canvases); setCanvas(s.canvases);
@@ -132,6 +134,9 @@ void Graphics::restoreStateChecked(const DisplayState &s)
setScissor(); setScissor();
} }
if (s.stencilTest != cur.stencilTest || s.stencilInvert != cur.stencilInvert)
setStencilTest(s.stencilTest, s.stencilInvert);
setFont(s.font.get()); setFont(s.font.get());
setShader(s.shader.get()); setShader(s.shader.get());
@@ -350,14 +355,27 @@ void Graphics::setDebug(bool enable)
void Graphics::reset() void Graphics::reset()
{ {
DisplayState s; DisplayState s;
discardStencil(); drawToStencilBuffer(false);
origin();
restoreState(s); restoreState(s);
origin();
} }
void Graphics::clear() void Graphics::clear(ClearType type)
{ {
glClear(GL_COLOR_BUFFER_BIT); GLbitfield mask = 0;
switch (type)
{
case CLEAR_ALL:
default:
mask = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
break;
case CLEAR_STENCIL:
mask = GL_STENCIL_BUFFER_BIT;
break;
}
glClear(mask);
} }
void Graphics::present() void Graphics::present()
@@ -409,38 +427,68 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const
return states.back().scissor; return states.back().scissor;
} }
void Graphics::defineStencil() void Graphics::drawToStencilBuffer(bool enable)
{ {
if (writingToStencil == enable)
return;
writingToStencil = enable;
if (!enable)
{
const DisplayState &state = states.back();
// Revert the color write mask.
setColorMask(state.colorMask);
// Use the user-set stencil test state when writes are disabled.
setStencilTest(state.stencilTest, state.stencilInvert);
return;
}
// Make sure the active canvas has a stencil buffer. // Make sure the active canvas has a stencil buffer.
if (Canvas::current) if (Canvas::current)
Canvas::current->checkCreateStencil(); Canvas::current->checkCreateStencil();
// Disable color writes but don't save the mask values. // Disable color writes but don't save the state for it.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_STENCIL_BUFFER_BIT); // The stencil test must be enabled in order to write to the stencil buffer.
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1); glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
activeStencil = true;
} }
void Graphics::useStencil(bool invert) void Graphics::setStencilTest(bool enable, bool invert)
{ {
glStencilFunc(GL_EQUAL, (GLint)(!invert), 1); // invert ? 0 : 1 DisplayState &state = states.back();
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); state.stencilTest = enable;
setColorMask(states.back().colorMask); state.stencilInvert = invert;
}
void Graphics::discardStencil() if (writingToStencil)
{
if (!activeStencil)
return; return;
setColorMask(states.back().colorMask); if (!enable)
glDisable(GL_STENCIL_TEST); {
activeStencil = false; glDisable(GL_STENCIL_TEST);
return;
}
// Make sure the active canvas has a stencil buffer.
if (Canvas::current)
Canvas::current->checkCreateStencil();
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, invert ? 0 : 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
void Graphics::getStencilTest(bool &enable, bool &invert)
{
const DisplayState &state = states.back();
enable = state.stencilTest;
invert = state.stencilInvert;
} }
Image *Graphics::newImage(love::image::ImageData *data, const Image::Flags &flags) Image *Graphics::newImage(love::image::ImageData *data, const Image::Flags &flags)
@@ -1279,6 +1327,8 @@ Graphics::DisplayState::DisplayState()
, pointSize(1.0f) , pointSize(1.0f)
, scissor(false) , scissor(false)
, scissorBox() , scissorBox()
, stencilTest(false)
, stencilInvert(false)
, font(nullptr) , font(nullptr)
, shader(nullptr) , shader(nullptr)
, wireframe(false) , wireframe(false)
@@ -1298,6 +1348,8 @@ Graphics::DisplayState::DisplayState(const DisplayState &other)
, pointSize(other.pointSize) , pointSize(other.pointSize)
, scissor(other.scissor) , scissor(other.scissor)
, scissorBox(other.scissorBox) , scissorBox(other.scissorBox)
, stencilTest(other.stencilTest)
, stencilInvert(other.stencilInvert)
, font(other.font) , font(other.font)
, shader(other.shader) , shader(other.shader)
, canvases(other.canvases) , canvases(other.canvases)
@@ -1322,6 +1374,8 @@ Graphics::DisplayState &Graphics::DisplayState::operator = (const DisplayState &
pointSize = other.pointSize; pointSize = other.pointSize;
scissor = other.scissor; scissor = other.scissor;
scissorBox = other.scissorBox; scissorBox = other.scissorBox;
stencilTest = other.stencilTest;
stencilInvert = other.stencilInvert;
font = other.font; font = other.font;
shader = other.shader; shader = other.shader;
+14 -16
View File
@@ -80,7 +80,7 @@ public:
/** /**
* Clears the screen. * Clears the screen.
**/ **/
void clear(); void clear(ClearType type = CLEAR_ALL);
/** /**
* Flips buffers. (Rendered geometry is presented on screen). * Flips buffers. (Rendered geometry is presented on screen).
@@ -124,22 +124,16 @@ public:
bool getScissor(int &x, int &y, int &width, int &height) const; bool getScissor(int &x, int &y, int &width, int &height) const;
/** /**
* Enables the stencil buffer and set stencil function to fill it * Enables or disables drawing to the stencil buffer. When enabled, the
*/ * color buffer is disabled.
void defineStencil(); **/
void drawToStencilBuffer(bool enable);
/** /**
* Set stencil function to mask the following drawing calls using * Sets whether stencil testing is enabled.
* the current stencil buffer **/
* @param invert Invert the mask, i.e. draw everywhere expect where void setStencilTest(bool enable, bool invert);
* the mask is defined. void getStencilTest(bool &enable, bool &invert);
*/
void useStencil(bool invert = false);
/**
* Disables the stencil buffer
*/
void discardStencil();
/** /**
* Creates an Image object with padding and/or optimization. * Creates an Image object with padding and/or optimization.
@@ -443,6 +437,10 @@ private:
bool scissor; bool scissor;
OpenGL::Viewport scissorBox; OpenGL::Viewport scissorBox;
// Stencil.
bool stencilTest;
bool stencilInvert;
Object::StrongRef<Font> font; Object::StrongRef<Font> font;
Object::StrongRef<Shader> shader; Object::StrongRef<Shader> shader;
@@ -471,7 +469,7 @@ private:
int height; int height;
bool created; bool created;
bool activeStencil; bool writingToStencil;
std::vector<DisplayState> states; std::vector<DisplayState> states;
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types. std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
+31 -21
View File
@@ -44,9 +44,15 @@ int w_reset(lua_State *)
return 0; return 0;
} }
int w_clear(lua_State *) int w_clear(lua_State *L)
{ {
instance()->clear(); Graphics::ClearType type = Graphics::CLEAR_ALL;
const char *tname = lua_isnoneornil(L, 1) ? nullptr : luaL_checkstring(L, 1);
if (tname && !Graphics::getConstant(tname, type))
return luaL_error(L, "Invalid graphics clear type: %s", tname);
instance()->clear(type);
return 0; return 0;
} }
@@ -118,32 +124,35 @@ int w_getScissor(lua_State *L)
return 4; return 4;
} }
static int setStencil(lua_State *L, bool invert) int w_drawStencil(lua_State *L)
{ {
// no argument -> clear stencil
if (lua_isnoneornil(L, 1))
{
instance()->discardStencil();
return 0;
}
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
instance()->defineStencil(); instance()->drawToStencilBuffer(true);
lua_call(L, lua_gettop(L) - 1, 0); // call stencil(...)
instance()->useStencil(invert); // Call stencilfunc(...)
lua_call(L, lua_gettop(L) - 1, 0);
instance()->drawToStencilBuffer(false);
return 0; return 0;
} }
int w_setStencil(lua_State *L) int w_setStencilTest(lua_State *L)
{ {
return setStencil(L, false); bool enable = luax_toboolean(L, 1);
bool invert = luax_toboolean(L, 2);
instance()->setStencilTest(enable, invert);
return 0;
} }
int w_setInvertedStencil(lua_State *L) int w_getStencilTest(lua_State *L)
{ {
return setStencil(L, true); bool enabled, inverted;
instance()->getStencilTest(enabled, inverted);
luax_pushboolean(L, enabled);
luax_pushboolean(L, inverted);
return 2;
} }
static const char *imageFlagName(Image::FlagType flagtype) static const char *imageFlagName(Image::FlagType flagtype)
@@ -860,8 +869,8 @@ int w_newScreenshot(lua_State *L)
int w_setCanvas(lua_State *L) int w_setCanvas(lua_State *L)
{ {
// discard stencil testing // Disable stencil writes.
instance()->discardStencil(); instance()->drawToStencilBuffer(false);
// called with none -> reset to default buffer // called with none -> reset to default buffer
if (lua_isnoneornil(L, 1)) if (lua_isnoneornil(L, 1))
@@ -1411,8 +1420,9 @@ static const luaL_Reg functions[] =
{ "setScissor", w_setScissor }, { "setScissor", w_setScissor },
{ "getScissor", w_getScissor }, { "getScissor", w_getScissor },
{ "setStencil", w_setStencil }, { "drawStencil", w_drawStencil },
{ "setInvertedStencil", w_setInvertedStencil }, { "setStencilTest", w_setStencilTest },
{ "getStencilTest", w_getStencilTest },
{ "point", w_point }, { "point", w_point },
{ "line", w_line }, { "line", w_line },
+3 -2
View File
@@ -48,8 +48,9 @@ int w_getHeight(lua_State *L);
int w_getDimensions(lua_State *L); int w_getDimensions(lua_State *L);
int w_setScissor(lua_State *L); int w_setScissor(lua_State *L);
int w_getScissor(lua_State *L); int w_getScissor(lua_State *L);
int w_setStencil(lua_State *L); int w_drawStencil(lua_State *L);
int w_setInvertedStencil(lua_State *L); int w_setStencilTest(lua_State *L);
int w_getStencilTest(lua_State *L);
int w_newImage(lua_State *L); int w_newImage(lua_State *L);
int w_newQuad(lua_State *L); int w_newQuad(lua_State *L);
int w_newFont(lua_State *L); int w_newFont(lua_State *L);