Add love.graphics.setStencilMode. Remove old stencil API.

- Add setStencilMode(drawaction, comparemode, value = 0, readmask = 0xFF, writemask = 0xFF).
- Add getStencilMode.
- Remove love.graphics.stencil.
- Remove love.graphics.set/getStencilTest.

Note that the new setStencilMode API does not clear the stencil buffer, and does not turn off color writes (it can still be done manually), unlike love.graphics.stencil.

Fixes #1161.
Fixes #1251.
This commit is contained in:
Alex Szpakowski
2022-01-22 23:36:07 -04:00
parent 173fe3dab5
commit 6dbb7fa5de
10 changed files with 148 additions and 221 deletions
+49 -68
View File
@@ -869,8 +869,13 @@ void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth
flags |= GL_COLOR_BUFFER_BIT;
}
uint32 stencilwrites = gl.getStencilWriteMask();
if (stencil.hasValue)
{
if (stencilwrites != LOVE_UINT32_MAX)
gl.setStencilWriteMask(LOVE_UINT32_MAX);
glClearStencil(stencil.value);
flags |= GL_STENCIL_BUFFER_BIT;
}
@@ -889,6 +894,9 @@ void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth
if (flags != 0)
glClear(flags);
if (stencil.hasValue && stencilwrites != LOVE_UINT32_MAX)
gl.setStencilWriteMask(stencilwrites);
if (depth.hasValue && !hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
@@ -980,8 +988,13 @@ void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt sten
GLbitfield flags = 0;
uint32 stencilwrites = gl.getStencilWriteMask();
if (stencil.hasValue)
{
if (stencilwrites != LOVE_UINT32_MAX)
gl.setStencilWriteMask(LOVE_UINT32_MAX);
glClearStencil(stencil.value);
flags |= GL_STENCIL_BUFFER_BIT;
}
@@ -1000,6 +1013,9 @@ void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt sten
if (flags != 0)
glClear(flags);
if (stencil.hasValue && stencilwrites != LOVE_UINT32_MAX)
gl.setStencilWriteMask(stencilwrites);
if (depth.hasValue && !hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
@@ -1377,24 +1393,26 @@ void Graphics::setScissor()
gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, false);
}
void Graphics::drawToStencilBuffer(StencilAction action, int value)
void Graphics::setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask)
{
DisplayState &state = states.back();
const auto &rts = state.renderTargets;
love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
if (!isRenderTargetActive() && !windowHasStencil)
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
else if (isRenderTargetActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dstexture == nullptr || !isPixelFormatStencil(dstexture->getPixelFormat())))
throw love::Exception("Drawing to the stencil buffer with a render target active requires either stencil=true or a custom stencil-type texture to be used, in setRenderTarget.");
if (action != STENCIL_KEEP)
{
const auto &rts = state.renderTargets;
love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
if (!isRenderTargetActive() && !windowHasStencil)
throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
else if (isRenderTargetActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dstexture == nullptr || !isPixelFormatStencil(dstexture->getPixelFormat())))
throw love::Exception("Drawing to the stencil buffer with a render target active requires either stencil=true or a custom stencil-type texture to be used, in setRenderTarget.");
}
flushBatchedDraws();
state.stencil.action = action;
state.stencil.value = value;
// Disable color writes but don't save the state for it.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
bool enablestencil = action != STENCIL_KEEP || compare != COMPARE_ALWAYS;
if (enablestencil != gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST))
gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, enablestencil);
GLenum glaction = GL_REPLACE;
@@ -1421,67 +1439,30 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
break;
}
// The stencil test must be enabled in order to write to the stencil buffer.
if (!gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST))
gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, true);
glStencilFunc(GL_ALWAYS, value, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, glaction);
}
void Graphics::stopDrawToStencilBuffer()
{
DisplayState &state = states.back();
if (state.stencil.action == STENCIL_KEEP)
return;
flushBatchedDraws();
state.stencil.action = STENCIL_KEEP;
// Revert the color write mask.
setColorMask(state.colorMask);
// Use the user-set stencil test state when writes are disabled.
setStencilTest(state.stencil.compare, state.stencil.value);
}
void Graphics::setStencilTest(CompareMode compare, int value)
{
DisplayState &state = states.back();
if (state.stencil.compare != compare || state.stencil.value != value)
flushBatchedDraws();
state.stencil.compare = compare;
state.stencil.value = value;
if (state.stencil.action != STENCIL_KEEP)
return;
if (compare == COMPARE_ALWAYS)
{
if (gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST))
gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, false);
return;
}
/**
* OpenGL / GPUs do the comparison in the opposite way that makes sense
* for this API. For example, if the compare function is GL_GREATER then the
* stencil test will pass if the reference value is greater than the value
* in the stencil buffer. With our API it's more intuitive to assume that
* setStencilTest(COMPARE_GREATER, 4) will make it pass if the stencil
* buffer has a value greater than 4.
* GPUs do the comparison opposite to what makes sense for love's API. For
* example, if the compare function is GREATER then the stencil test will
* pass if the reference value is greater than the value in the stencil
* buffer. With our API it's more intuitive to assume that
* setStencilMode(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the
* stencil buffer has a value greater than 4.
**/
GLenum glcompare = OpenGL::getGLCompareMode(getReversedCompareMode(compare));
if (!gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST))
gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, true);
if (enablestencil)
{
glStencilFunc(glcompare, value, readmask);
glStencilOp(GL_KEEP, GL_KEEP, glaction);
}
glStencilFunc(glcompare, value, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
if (writemask != gl.getStencilWriteMask())
gl.setStencilWriteMask(writemask);
state.stencil.action = action;
state.stencil.compare = compare;
state.stencil.value = value;
state.stencil.readMask = readmask;
state.stencil.writeMask = writemask;
}
void Graphics::setDepthMode(CompareMode compare, bool write)
+1 -4
View File
@@ -91,10 +91,7 @@ public:
void setScissor(const Rect &rect) override;
void setScissor() override;
void drawToStencilBuffer(StencilAction action, int value) override;
void stopDrawToStencilBuffer() override;
void setStencilTest(CompareMode compare, int value) override;
void setStencilMode(StencilAction action, CompareMode compare, int value, uint32 readmask, uint32 writemask) override;
void setDepthMode(CompareMode compare, bool write) override;
+12
View File
@@ -283,6 +283,7 @@ void OpenGL::setupContext()
state.curTextureUnit = 0;
setDepthWrites(state.depthWritesEnabled);
setStencilWriteMask(state.stencilWriteMask);
createDefaultTexture();
@@ -1112,6 +1113,17 @@ bool OpenGL::hasDepthWrites() const
return state.depthWritesEnabled;
}
void OpenGL::setStencilWriteMask(uint32 mask)
{
glStencilMask(mask);
state.stencilWriteMask = mask;
}
uint32 OpenGL::getStencilWriteMask() const
{
return state.stencilWriteMask;
}
void OpenGL::useProgram(GLuint program)
{
glUseProgram(program);
+4
View File
@@ -298,6 +298,9 @@ public:
void setDepthWrites(bool enable);
bool hasDepthWrites() const;
void setStencilWriteMask(uint32 mask);
uint32 getStencilWriteMask() const;
/**
* Calls glUseProgram.
**/
@@ -524,6 +527,7 @@ private:
float pointSize;
bool depthWritesEnabled = true;
uint32 stencilWriteMask = LOVE_UINT32_MAX;
GLuint boundFramebuffers[2];
+7
View File
@@ -86,12 +86,19 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
if (!hadDepthWrites) // glDepthMask also affects glClear.
gl.setDepthWrites(true);
uint32 stencilwrite = gl.getStencilWriteMask();
if (stencilwrite != LOVE_UINT32_MAX)
gl.setStencilWriteMask(LOVE_UINT32_MAX);
gl.clearDepth(1.0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!hadDepthWrites)
gl.setDepthWrites(hadDepthWrites);
if (stencilwrite != LOVE_UINT32_MAX)
gl.setStencilWriteMask(stencilwrite);
}
else
{