From 77d8b96cfd30381bffd84338f241dc64b461885e Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 18 Dec 2017 19:48:28 -0800 Subject: [PATCH] lovr.graphics.stencil; --- src/api/graphics.c | 32 ++++++++++++++++++- src/graphics/canvas.c | 2 +- src/graphics/graphics.c | 71 +++++++++++++++++++++++++++++------------ src/graphics/graphics.h | 16 +++++++++- src/headset/fake.c | 2 +- src/headset/openvr.c | 2 +- 6 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/api/graphics.c b/src/api/graphics.c index fe260952..eb974a53 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -27,6 +27,7 @@ map_int_t MatrixTypes; map_int_t MeshAttributeTypes; map_int_t MeshDrawModes; map_int_t MeshUsages; +map_int_t StencilActions; map_int_t TextureFormats; map_int_t VerticalAligns; map_int_t Windings; @@ -72,6 +73,12 @@ static void luax_readvertices(lua_State* L, int index, vec_float_t* points) { } } +static void stencilCallback(void* userdata) { + lua_State* L = userdata; + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_call(L, 0, 0); +} + // Base int l_lovrGraphicsInit(lua_State* L) { @@ -161,6 +168,14 @@ int l_lovrGraphicsInit(lua_State* L) { map_set(&MeshUsages, "dynamic", MESH_DYNAMIC); map_set(&MeshUsages, "stream", MESH_STREAM); + map_init(&StencilActions); + map_set(&StencilActions, "replace", STENCIL_REPLACE); + map_set(&StencilActions, "increment", STENCIL_INCREMENT); + map_set(&StencilActions, "decrement", STENCIL_DECREMENT); + map_set(&StencilActions, "incrementwrap", STENCIL_INCREMENT_WRAP); + map_set(&StencilActions, "decrementwrap", STENCIL_DECREMENT_WRAP); + map_set(&StencilActions, "invert", STENCIL_INVERT); + map_init(&TextureFormats); map_set(&TextureFormats, "rgb", FORMAT_RGB); map_set(&TextureFormats, "rgba", FORMAT_RGBA); @@ -238,7 +253,8 @@ int l_lovrGraphicsReset(lua_State* L) { int l_lovrGraphicsClear(lua_State* L) { bool color = lua_gettop(L) < 1 || lua_toboolean(L, 1); bool depth = lua_gettop(L) < 2 || lua_toboolean(L, 2); - lovrGraphicsClear(color, depth); + bool stencil = lua_gettop(L) < 3 || lua_toboolean(L, 3); + lovrGraphicsClear(color, depth, stencil); return 0; } @@ -726,6 +742,19 @@ int l_lovrGraphicsPrint(lua_State* L) { return 0; } +int l_lovrGraphicsStencil(lua_State* L) { + luaL_checktype(L, 1, LUA_TFUNCTION); + StencilAction action = *(StencilAction*) luax_optenum(L, 2, "replace", &StencilActions, "stencil action"); + int replaceValue = luaL_optinteger(L, 3, 1); + bool keepValues = lua_toboolean(L, 4); + if (!keepValues) { + lovrGraphicsClear(false, false, true); + } + lua_settop(L, 1); + lovrGraphicsStencil(action, replaceValue, stencilCallback, L); + return 0; +} + // Types int l_lovrGraphicsNewAnimator(lua_State* L) { @@ -1054,6 +1083,7 @@ const luaL_Reg lovrGraphics[] = { { "sphere", l_lovrGraphicsSphere }, { "skybox", l_lovrGraphicsSkybox }, { "print", l_lovrGraphicsPrint }, + { "stencil", l_lovrGraphicsStencil }, { "newAnimator", l_lovrGraphicsNewAnimator }, { "newCanvas", l_lovrGraphicsNewCanvas }, { "newFont", l_lovrGraphicsNewFont }, diff --git a/src/graphics/canvas.c b/src/graphics/canvas.c index c0b5ddf9..1d7eca4f 100644 --- a/src/graphics/canvas.c +++ b/src/graphics/canvas.c @@ -78,7 +78,7 @@ Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, CanvasType } lovrAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Error creating Canvas"); - lovrGraphicsClear(true, true); + lovrGraphicsClear(true, true, true); glBindFramebuffer(GL_FRAMEBUFFER, 0); return canvas; diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index 8a3dd436..2d86b80c 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -80,9 +80,9 @@ void lovrGraphicsReset() { lovrGraphicsOrigin(); } -void lovrGraphicsClear(bool color, bool depth) { - if (!color && !depth) return; - glClear((color ? GL_COLOR_BUFFER_BIT : 0) | (depth ? GL_DEPTH_BUFFER_BIT : 0)); +void lovrGraphicsClear(bool color, bool depth, bool stencil) { + if (!color && !depth && !stencil) return; + glClear((color ? GL_COLOR_BUFFER_BIT : 0) | (depth ? GL_DEPTH_BUFFER_BIT : 0) | (stencil ? GL_STENCIL_BUFFER_BIT : 0)); } void lovrGraphicsPresent() { @@ -451,26 +451,33 @@ void lovrGraphicsGetStencilTest(CompareMode* mode, int* value) { } void lovrGraphicsSetStencilTest(CompareMode mode, int value) { - if (mode != state.stencilMode || value != state.stencilValue) { - state.stencilMode = mode; - state.stencilValue = value; - if (mode != COMPARE_NONE) { + state.stencilMode = mode; + state.stencilValue = value; + + if (state.stencilWriting) { + return; + } + + if (mode != COMPARE_NONE) { + if (!state.stencilEnabled) { glEnable(GL_STENCIL_TEST); - - GLenum glMode = mode; - switch (mode) { - case COMPARE_LESS: glMode = GL_GREATER; break; - case COMPARE_LEQUAL: glMode = GL_GEQUAL; break; - case COMPARE_GEQUAL: glMode = GL_LEQUAL; break; - case COMPARE_GREATER: glMode = GL_LESS; break; - default: break; - } - - glStencilFunc(glMode, value, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - } else { - glDisable(GL_STENCIL_TEST); + state.stencilEnabled = true; } + + GLenum glMode = mode; + switch (mode) { + case COMPARE_LESS: glMode = GL_GREATER; break; + case COMPARE_LEQUAL: glMode = GL_GEQUAL; break; + case COMPARE_GEQUAL: glMode = GL_LEQUAL; break; + case COMPARE_GREATER: glMode = GL_LESS; break; + default: break; + } + + glStencilFunc(glMode, value, 0xff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } else if (state.stencilEnabled) { + glDisable(GL_STENCIL_TEST); + state.stencilEnabled = false; } } @@ -1063,6 +1070,28 @@ void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAl lovrGraphicsPop(); } +void lovrGraphicsStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata) { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + if (!state.stencilEnabled) { + glEnable(GL_STENCIL_TEST); + state.stencilEnabled = true; + } + + glStencilFunc(GL_ALWAYS, replaceValue, 0xff); + glStencilOp(GL_KEEP, GL_KEEP, action); + + state.stencilWriting = true; + callback(userdata); + state.stencilWriting = false; + + glDepthMask(GL_TRUE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + lovrGraphicsSetStencilTest(state.stencilMode, state.stencilValue); +} + // Internal State void lovrGraphicsPushView() { if (++state.view >= MAX_VIEWS) { diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 9f4007e8..2c885af9 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -15,6 +15,8 @@ #define DEFAULT_SHADER_COUNT 4 #define MAX_TEXTURES 16 +typedef void (*StencilCallback)(void* userdata); + typedef enum { BLEND_ALPHA, BLEND_ADD, @@ -57,6 +59,15 @@ typedef enum { COMPARE_GREATER = GL_GREATER } CompareMode; +typedef enum { + STENCIL_REPLACE = GL_REPLACE, + STENCIL_INCREMENT = GL_INCR, + STENCIL_DECREMENT = GL_DECR, + STENCIL_INCREMENT_WRAP = GL_INCR_WRAP, + STENCIL_DECREMENT_WRAP = GL_DECR_WRAP, + STENCIL_INVERT = GL_INVERT +} StencilAction; + typedef enum { MATRIX_MODEL, MATRIX_VIEW @@ -115,6 +126,8 @@ typedef struct { View views[MAX_VIEWS]; int view; Texture* textures[MAX_TEXTURES]; + bool stencilEnabled; + bool stencilWriting; uint32_t program; uint32_t vertexArray; uint32_t vertexBuffer; @@ -126,7 +139,7 @@ typedef struct { void lovrGraphicsInit(); void lovrGraphicsDestroy(); void lovrGraphicsReset(); -void lovrGraphicsClear(bool color, bool depth); +void lovrGraphicsClear(bool color, bool depth, bool stencil); void lovrGraphicsPresent(); void lovrGraphicsPrepare(Material* material, float* pose); void lovrGraphicsCreateWindow(int w, int h, bool fullscreen, int msaa, const char* title, const char* icon); @@ -187,6 +200,7 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa void lovrGraphicsSphere(Material* material, mat4 transform, int segments); void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az); void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign); +void lovrGraphicsStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata); // Internal State void lovrGraphicsPushView(); diff --git a/src/headset/fake.c b/src/headset/fake.c index 9a6dc3ed..c1d27abc 100644 --- a/src/headset/fake.c +++ b/src/headset/fake.c @@ -352,7 +352,7 @@ static void fakeRenderTo(headsetRenderCallback callback, void* userdata) { lovrGraphicsMatrixTransform(MATRIX_VIEW, inv); lovrGraphicsSetProjection(state.projection); - lovrGraphicsClear(true, true); + lovrGraphicsClear(true, true, true); callback(EYE_LEFT, userdata); lovrGraphicsPop(); } diff --git a/src/headset/openvr.c b/src/headset/openvr.c index f464a3aa..f8e8cf9f 100644 --- a/src/headset/openvr.c +++ b/src/headset/openvr.c @@ -754,7 +754,7 @@ static void openvrRenderTo(headsetRenderCallback callback, void* userdata) { lovrGraphicsPush(); lovrGraphicsMatrixTransform(MATRIX_VIEW, transform); lovrGraphicsSetProjection(projection); - lovrGraphicsClear(true, true); + lovrGraphicsClear(true, true, false); callback(eye, userdata); lovrGraphicsPop(); lovrCanvasResolveMSAA(state.canvas);