diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index e1c9a819..85cfe60e 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -55,6 +55,8 @@ namespace PICA { BlendFunc = 0x101, BlendColour = 0x103, AlphaTestConfig = 0x104, + StencilTest = 0x105, + StencilOp = 0x106, DepthAndColorMask = 0x107, DepthBufferFormat = 0x116, ColourBufferFormat = 0x117, diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 3c729d76..18f52a1c 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -61,6 +61,7 @@ class RendererGL final : public Renderer { MAKE_LOG_FUNCTION(log, rendererLogger) void setupBlending(); + void setupStencilTest(bool stencilEnable); void bindDepthBuffer(); void setupTextureEnvState(); void bindTexturesToSlots(); @@ -79,4 +80,4 @@ class RendererGL final : public Renderer { // Take a screenshot of the screen and store it in a file void screenshot(const std::string& name) override; -}; \ No newline at end of file +}; diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 0126b3b8..91c70bba 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -205,6 +205,51 @@ void RendererGL::setupBlending() { } } +void RendererGL::setupStencilTest(bool stencilEnable) { + if (!stencilEnable) { + OpenGL::disableStencil(); + return; + } + + static constexpr std::array stencilFuncs = { + GL_NEVER, + GL_ALWAYS, + GL_EQUAL, + GL_NOTEQUAL, + GL_LESS, + GL_LEQUAL, + GL_GREATER, + GL_GEQUAL + }; + OpenGL::enableStencil(); + + const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest]; + const u32 stencilFunc = getBits<4, 3>(stencilConfig); + const u32 stencilBufferMask = getBits<8, 8>(stencilConfig); + const s32 reference = s8(getBits<16, 8>(stencilConfig)); // Signed reference value + const u32 stencilRefMask = getBits<24, 8>(stencilConfig); + + glStencilFunc(stencilFuncs[stencilFunc], reference, stencilRefMask); + glStencilMask(stencilBufferMask); + + static constexpr std::array stencilOps = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR, + GL_DECR, + GL_INVERT, + GL_INCR_WRAP, + GL_DECR_WRAP + }; + const u32 stencilOpConfig = regs[PICA::InternalRegs::StencilOp]; + const u32 stencilFailOp = getBits<0, 3>(stencilOpConfig); + const u32 depthFailOp = getBits<4, 3>(stencilOpConfig); + const u32 passOp = getBits<8, 3>(stencilOpConfig); + + glStencilOp(stencilOps[stencilFailOp], stencilOps[depthFailOp], stencilOps[passOp]); +} + void RendererGL::setupTextureEnvState() { // TODO: Only update uniforms when the TEV config changed. Use an UBO potentially. @@ -356,6 +401,9 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v GLsizei viewportHeight = GLsizei(f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0f); OpenGL::setViewport(viewportWidth, viewportHeight); + const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest]; + const bool stencilEnable = getBit<0>(stencilConfig); + // Note: The code below must execute after we've bound the colour buffer & its framebuffer // Because it attaches a depth texture to the aforementioned colour buffer if (depthEnable) { @@ -371,9 +419,15 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span v bindDepthBuffer(); } else { gl.disableDepth(); + + if (stencilEnable) { + bindDepthBuffer(); + } } } + setupStencilTest(stencilEnable); + vbo.bufferVertsSub(vertices); OpenGL::draw(primitiveTopology, GLsizei(vertices.size())); } @@ -476,6 +530,7 @@ void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u gl.disableBlend(); gl.disableDepth(); gl.disableScissor(); + OpenGL::disableStencil(); gl.setColourMask(true, true, true, true); gl.useProgram(displayProgram); gl.bindVAO(dummyVAO);