From 03801849676b65dd3f4dc8841be6c50b88b7c7eb Mon Sep 17 00:00:00 2001 From: XProger Date: Sun, 26 Mar 2017 03:01:31 +0300 Subject: [PATCH] #23 shadow mask (volumes); #15 android fix double tap --- src/cache.h | 19 +++++-- src/controller.h | 21 +------ src/core.h | 86 ++++++++++++++++++++++++++--- src/level.h | 54 ++++++++++++++++-- src/mesh.h | 91 ++++++++++++++++++++++++++++--- src/platform/win/OpenLara.vcxproj | 1 + src/shader.h | 4 +- src/ui.h | 17 +++--- 8 files changed, 237 insertions(+), 56 deletions(-) diff --git a/src/cache.h b/src/cache.h index 8224ca1..f409917 100644 --- a/src/cache.h +++ b/src/cache.h @@ -17,12 +17,16 @@ const char SHADER[] = #include "shader.glsl" ; +const char WATER[] = + #include "water.glsl" +; + const char FILTER[] = #include "filter.glsl" ; -const char WATER[] = - #include "water.glsl" +const char VOLUME[] = + #include "volume.glsl" ; const char GUI[] = @@ -113,7 +117,7 @@ struct ShaderCache { } } - const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "WATER", "FILTER", "GUI" }; + const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "WATER", "FILTER", "VOLUME", "GUI" }; const char *src = NULL; const char *typ = NULL; switch (pass) { @@ -146,12 +150,19 @@ struct ShaderCache { break; } case Core::passFilter : { - static const char *typeNames[] = { "DOWNSAMPLE" }; + static const char *typeNames[] = { "DEFAULT", "DOWNSAMPLE" }; src = FILTER; typ = typeNames[type]; sprintf(def, "%s#define PASS_%s\n#define FILTER_%s\n", ext, passNames[pass], typ); break; } + case Core::passVolume : { + static const char *typeNames[] = { "DEFAULT" }; + src = VOLUME; + typ = typeNames[type]; + sprintf(def, "%s#define PASS_%s\n", ext, passNames[pass]); + break; + } case Core::passGUI : { static const char *typeNames[] = { "DEFAULT" }; src = GUI; diff --git a/src/controller.h b/src/controller.h index df61906..1446c6d 100644 --- a/src/controller.h +++ b/src/controller.h @@ -26,7 +26,7 @@ struct IGame { virtual void waterDrop(const vec3 &pos, float radius, float strength) {} virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {} virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {} - virtual void renderCompose(int roomIndex) {} + virtual void renderCompose(int roomIndex, bool genShadowMask = false) {} }; struct Controller { @@ -489,23 +489,6 @@ struct Controller { } void renderShadow(MeshBuilder *mesh, const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) { - /* - mat4 m; - m.identity(); - m.translate(pos); - m.rotateY(angle); - m.translate(vec3(offset.x, 0.0f, offset.z)); - m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f)); - - game->setShader(Core::pass, Shader::FLASH, false, false); - Core::active.shader->setParam(uBasis, Basis(m)); - Core::active.shader->setParam(uColor, vec4(0.5f, 0.5f, 0.5f, 1.0f)); - Core::active.shader->setParam(uAmbient, vec3(0.0f)); - - Core::setBlending(bmMultiply); - mesh->renderShadowSpot(); - Core::setBlending(bmNone); - */ mat4 m = Core::mViewProj; m.translate(pos); m.rotateY(angle); @@ -522,7 +505,7 @@ struct Controller { Core::active.shader->setParam(uAmbient, vec3(0.0f)); Core::setBlending(bmAlpha); - mesh->renderShadowSpot(); + mesh->renderShadowBlob(); Core::setBlending(bmNone); Core::active.shader->setParam(uViewProj, Core::mViewProj); diff --git a/src/core.h b/src/core.h index 7c68e42..4a79a29 100644 --- a/src/core.h +++ b/src/core.h @@ -79,8 +79,10 @@ #define GL_RGBA16F GL_RGBA #define GL_HALF_FLOAT GL_HALF_FLOAT_OES - #define glGetProgramBinary(...) 0 - #define glProgramBinary(...) 0 + #define GL_STENCIL_TEST_TWO_SIDE_EXT 0 + #define glGetProgramBinary(...) + #define glProgramBinary(...) + #define glActiveStencilFaceEXT(...) #endif #include "utils.h" @@ -158,6 +160,10 @@ PFNGLDELETEBUFFERSARBPROC glDeleteBuffers; PFNGLBINDBUFFERARBPROC glBindBuffer; PFNGLBUFFERDATAARBPROC glBufferData; + // Stencil + PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFaceEXT; + PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; + PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; #endif PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; @@ -180,6 +186,7 @@ struct Texture; namespace Core { struct { + bool shaderBinary; bool VAO; bool depthTexture; bool shadowSampler; @@ -187,7 +194,7 @@ namespace Core { bool texNPOT; bool texFloat, texFloatLinear; bool texHalf, texHalfLinear; - bool shaderBinary; + char stencil; #ifdef PROFILE bool profMarker; bool profTiming; @@ -254,7 +261,7 @@ namespace Core { Texture *blackTex, *whiteTex; - enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass; + enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passVolume, passGUI, passMAX } pass; GLuint FBO; struct RenderTargetCache { @@ -274,6 +281,7 @@ namespace Core { GLuint VAO; BlendMode blendMode; CullMode cullMode; + bool stencilTwoSide; } active; struct Stats { @@ -312,7 +320,6 @@ namespace Core { #include "texture.h" #include "shader.h" -#include "mesh.h" namespace Core { @@ -379,6 +386,10 @@ namespace Core { GetProcOGL(glDeleteBuffers); GetProcOGL(glBindBuffer); GetProcOGL(glBufferData); + + GetProcOGL(glActiveStencilFaceEXT); + GetProcOGL(glStencilFuncSeparate); + GetProcOGL(glStencilOpSeparate); #endif #ifdef MOBILE @@ -395,7 +406,7 @@ namespace Core { char *ext = (char*)glGetString(GL_EXTENSIONS); //LOG("%s\n", ext); - support.shaderBinary = extSupport(ext, "_program_binary"); + support.shaderBinary = false;//extSupport(ext, "_program_binary"); support.VAO = extSupport(ext, "_vertex_array_object"); support.depthTexture = extSupport(ext, "_depth_texture"); support.shadowSampler = extSupport(ext, "_shadow_samplers") || extSupport(ext, "GL_ARB_shadow"); @@ -405,6 +416,15 @@ namespace Core { support.texFloat = support.texFloatLinear || extSupport(ext, "_texture_float"); support.texHalfLinear = extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_half_float_linear"); support.texHalf = support.texHalfLinear || extSupport(ext, "_texture_half_float"); + + if (extSupport(ext, "_ATI_separate_stencil")) + support.stencil = 2; + else + if (extSupport(ext, "_stencil_two_side")) + support.stencil = 1; + else + support.stencil = 0; + #ifdef PROFILE support.profMarker = extSupport(ext, "_KHR_debug"); support.profTiming = extSupport(ext, "_timer_query"); @@ -425,6 +445,7 @@ namespace Core { LOG(" float textures : float = %s, half = %s\n", support.texFloat ? (support.texFloatLinear ? "linear" : "nearest") : "false", support.texHalf ? (support.texHalfLinear ? "linear" : "nearest") : "false"); + LOG(" stencil : %s\n", support.stencil == 2 ? "separate" : (support.stencil == 1 ? "two_side" : "false")); LOG("\n"); glGenFramebuffers(1, &FBO); @@ -472,14 +493,14 @@ namespace Core { item.height = height; glBindRenderbuffer(GL_RENDERBUFFER, item.ID); - glRenderbufferStorage(GL_RENDERBUFFER, depth ? GL_RGB : GL_DEPTH_COMPONENT16, width, height); + glRenderbufferStorage(GL_RENDERBUFFER, depth ? GL_RGB565 : GL_DEPTH_COMPONENT16, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); return cache.count++; } - void clear(bool clearColor, bool clearDepth) { - if (GLbitfield mask = (clearColor ? GL_COLOR_BUFFER_BIT : 0) | (clearDepth ? GL_DEPTH_BUFFER_BIT : 0)) + void clear(bool clearColor, bool clearDepth, bool clearStencil = false) { + if (GLbitfield mask = (clearColor ? GL_COLOR_BUFFER_BIT : 0) | (clearDepth ? GL_DEPTH_BUFFER_BIT : 0) | (clearStencil ? GL_STENCIL_BUFFER_BIT : 0)) glClear(mask); } @@ -487,6 +508,10 @@ namespace Core { glClearColor(color.x, color.y, color.z, color.w); } + void setClearStencil(int value) { + glClearStencil(value); + } + void setViewport(int x, int y, int width, int height) { glViewport(x, y, width, height); } @@ -555,6 +580,47 @@ namespace Core { glDisable(GL_DEPTH_TEST); } + void setStencilTest(bool test) { + if (test) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); + } + + void setStencilTwoSide(int ref, bool test) { // preset for z-fail shadow volumes + active.stencilTwoSide = test; + if (test) { + switch (Core::support.stencil) { + case 0 : + glStencilFunc(GL_ALWAYS, ref, ~0); + break; + case 1 : + setCulling(cfNone); + glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT); + glActiveStencilFaceEXT(GL_BACK); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + glStencilFunc(GL_ALWAYS, ref, ~0); + glActiveStencilFaceEXT(GL_FRONT); + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); + glStencilFunc(GL_ALWAYS, ref, ~0); + break; + case 2 : + setCulling(cfNone); + glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, ref, ~0); + glStencilFuncSeparate(GL_BACK, GL_ALWAYS, ref, ~0); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR, GL_KEEP); + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR, GL_KEEP); + break; + } + } else { + if (Core::support.stencil == 1) + glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_NOTEQUAL, ref, ~0); + setCulling(cfFront); + } + } + void invalidateTarget(bool color, bool depth) { #ifdef MOBILE if (support.discardFrame && (color || depth)) { @@ -619,4 +685,6 @@ namespace Core { } } +#include "mesh.h" + #endif diff --git a/src/level.h b/src/level.h index 3d52f6c..8febaca 100644 --- a/src/level.h +++ b/src/level.h @@ -86,7 +86,7 @@ struct Level : IGame { } } - virtual void renderCompose(int roomIndex) { + virtual void renderCompose(int roomIndex, bool genShadowMask = false) { PROFILE_MARKER("PASS_COMPOSE"); Core::pass = Core::passCompose; @@ -95,6 +95,51 @@ struct Level : IGame { if (shadow) shadow->bind(sShadow); renderScene(roomIndex); + + if (genShadowMask) { + // renderShadowVolumes(); + } + } + + void renderShadowVolumes() { + getLight(lara->pos, lara->getRoomIndex()); + + Core::setCulling(cfNone); + Core::setDepthWrite(false); + Core::setColorWrite(false, false, false, false); + + Core::setStencilTest(true); + Core::setStencilTwoSide(128, true); + + setShader(Core::passVolume, Shader::DEFAULT, false, false); + vec4 lp(Core::lightPos[0], Core::lightColor[0].w); + Core::active.shader->setParam(uLightPos, lp); + + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + if (e.flags.rendered && TR::castShadow(e.type)) { + Controller *controller = (Controller*)e.controller; + + mat4 matrix = controller->getMatrix(); + Box box = controller->animation.getBoundingBox(vec3(0.0f), 0); + matrix.translate(box.center()); + + Core::active.shader->setParam(uBasis, Basis(matrix) ); + Core::active.shader->setParam(uPosScale, box.size() * 0.5); + + mesh->renderShadowBox(); + } + } + + Core::setDepthWrite(true); + Core::setColorWrite(true, true, true, true); + + Core::setStencilTwoSide(128, false); + + setShader(Core::passFilter, Shader::DEFAULT, false, false); + mesh->renderQuad(); + + Core::setStencilTest(false); } //============================== @@ -739,14 +784,15 @@ struct Level : IGame { if (shadow) renderShadows(lara->getRoomIndex()); + Core::setClearStencil(128); Core::setTarget(NULL); - Core::clear(true, true); + Core::clear(true, true, true); Core::setViewport(0, 0, Core::width, Core::height); if (waterCache) waterCache->checkVisibility = true; - renderCompose(camera->getRoomIndex()); + renderCompose(camera->getRoomIndex(), true); if (waterCache) { waterCache->checkVisibility = false; @@ -825,7 +871,7 @@ struct Level : IGame { Core::setDepthTest(true); - + /* glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); diff --git a/src/mesh.h b/src/mesh.h index 5547b1e..2df516a 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -90,13 +90,25 @@ struct Mesh { glEnableVertexAttribArray(aColor); } - void render(const MeshRange &range) { - range.bind(VAO); + void DIP(const MeshRange &range) { glDrawElements(GL_TRIANGLES, range.iCount, GL_UNSIGNED_SHORT, (GLvoid*)(range.iStart * sizeof(Index))); - Core::stats.dips++; Core::stats.tris += range.iCount / 3; } + + void render(const MeshRange &range) { + range.bind(VAO); + + if (Core::active.stencilTwoSide && Core::support.stencil == 0) { + Core::setCulling(cfBack); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + DIP(range); + Core::setCulling(cfFront); + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); + } + + DIP(range); + } }; @@ -145,7 +157,7 @@ struct MeshBuilder { } *models; MeshRange *sequences; // procedured - MeshRange shadowBlob; + MeshRange shadowBlob, shadowBox; MeshRange bar; MeshRange quad, circle; MeshRange plane; @@ -231,13 +243,20 @@ struct MeshBuilder { vCount += level.spriteSequences[i].sCount * 4; } - // get size of simple shadow spot mesh (8 triangles, 8 vertices) + // shadow blob mesh (8 triangles, 8 vertices) shadowBlob.vStart = vCount; shadowBlob.iStart = iCount; shadowBlob.iCount = 8 * 3; iCount += shadowBlob.iCount; vCount += 8; + // shadow box (for stencil shadow volumes with degenerate triangles) + shadowBox.vStart = vCount; + shadowBox.iStart = iCount; + shadowBox.iCount = (3 * (2 + 4)) * 6; + iCount += shadowBox.iCount; + vCount += 4 * 6; + // bar (health, oxygen) bar.vStart = vCount; bar.iStart = iCount; @@ -350,7 +369,7 @@ struct MeshBuilder { } aCount += level.spriteSequencesCount; - // build shadow spot + // build shadow blob for (int i = 0; i < 8; i++) { Vertex &v = vertices[vCount + i]; v.normal = { 0, -1, 0, 1 }; @@ -371,6 +390,59 @@ struct MeshBuilder { iCount += shadowBlob.iCount; aCount++; + // build shadow box volume + { + static const Index tmpIndices[] = { + 0,1,2, 0,2,3, 0,7,1, 0,4,7, + 1,11,2, 1,8,11, 2,15,3, 2,12,15, + 3,19,0, 3,16,19, 21,6,5, 21,20,6, + 20,10,9, 20,23,10, 23,14,13, 23,22,14, + 22,18,17, 22,21,18, 20,21,22, 20,22,23, + 7,6,9, 7,9,8, 11,10,13, 11,13,12, + 15,14,17, 15,17,16, 19,5,4, 19,18,5, + 4,6,7, 4,5,6, 8,9,10, 8,10,11, + 12,14,15, 12,13,14, 16,18,19, 16,17,18 + }; + static const short4 tmpCoords[] = { + { -1, -1, -1, 0 }, { 1, -1, -1, 0 }, { 1, 1, -1, 0 }, { -1, 1, -1, 0 }, + { -1, -1, -1, 0 }, { -1, -1, 1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 }, + { 1, -1, -1, 0 }, { 1, -1, 1, 0 }, { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, + { 1, 1, -1, 0 }, { 1, 1, 1, 0 }, { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, + { -1, 1, -1, 0 }, { -1, 1, 1, 0 }, { -1, -1, 1, 0 }, { -1, -1, -1, 0 }, + { 1, -1, 1, 0 }, { -1, -1, 1, 0 }, { -1, 1, 1, 0 }, { 1, 1, 1, 0 }, + }; + + const short n = 32767; + static const short4 tmpNormals[] = { + { 0, 0, -n, 0 }, + { 0, -n, 0, 0 }, + { n, 0, 0, 0 }, + { 0, n, 0, 0 }, + { -n, 0, 0, 0 }, + { 0, 0, n, 0 }, + }; + + static const ubyte4 tmpColors[] = { + { 255, 0, 0, 0 }, + { 0, 255, 0, 0 }, + { 0, 0, 255, 0 }, + { 255, 0, 255, 0 }, + { 255, 255, 0, 0 }, + { 0, 255, 255, 0 }, + }; + + memcpy(&indices[iCount], &tmpIndices[0], shadowBox.iCount * sizeof(Index)); + memset(&vertices[vCount], 0, 4 * 6 * sizeof(Vertex)); + for (int i = 0; i < 4 * 6; i++) { + vertices[vCount + i].coord = tmpCoords[i]; + vertices[vCount + i].normal = tmpNormals[i / 4]; + vertices[vCount + i].color = tmpColors[i / 4]; + } + iCount += shadowBox.iCount; + vCount += 4 * 6; + aCount++; + } + // white bar addQuad(indices, iCount, vCount, bar.vStart, vertices, &whiteTile); vertices[vCount + 0].coord = { 0, 0, 0, 0 }; @@ -467,6 +539,7 @@ struct MeshBuilder { for (int i = 0; i < level.modelsCount; i++) mesh->initRange(models[i].geometry); mesh->initRange(shadowBlob); + mesh->initRange(shadowBox); mesh->initRange(bar); mesh->initRange(quad); mesh->initRange(circle); @@ -856,12 +929,12 @@ struct MeshBuilder { mesh->render(range); } - void renderShadowSpot() { + void renderShadowBlob() { mesh->render(shadowBlob); } - void renderLine(const vec2 &pos, const vec2 &size, uint32 color) { - + void renderShadowBox() { + mesh->render(shadowBox); } void renderQuad() { diff --git a/src/platform/win/OpenLara.vcxproj b/src/platform/win/OpenLara.vcxproj index aeac447..966c500 100644 --- a/src/platform/win/OpenLara.vcxproj +++ b/src/platform/win/OpenLara.vcxproj @@ -218,6 +218,7 @@ + diff --git a/src/shader.h b/src/shader.h index 57ee8e7..cb678f2 100644 --- a/src/shader.h +++ b/src/shader.h @@ -57,9 +57,9 @@ struct Shader { vec4 params[uMAX][4]; enum Type : GLint { - NONE = 0, + DEFAULT = 0, /* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4, - /* filter */ FILTER_DOWNSAMPLE = 0, + /* filter */ FILTER_DOWNSAMPLE = 1, /* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4, MAX = 5 }; diff --git a/src/ui.h b/src/ui.h index e11d82d..8dcd4b2 100644 --- a/src/ui.h +++ b/src/ui.h @@ -66,13 +66,12 @@ struct UI { } void update() { - if (touch[zMove] != ikNone || touch[zLook] != ikNone || touch[zButton] != ikNone) { + if (touchTimerTap > 0.0f) + touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime); + + if (touch[zMove] != ikNone || touch[zLook] != ikNone || touch[zButton] != ikNone) touchTimerVis = 30.0f; - - if (touchTimerTap > 0.0f) - touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime); - - } else + else if (touchTimerVis > 0.0f) touchTimerVis = max(0.0f, touchTimerVis - Core::deltaTime); @@ -116,11 +115,11 @@ struct UI { if (t == ikNone) { t = key; if (zone == zMove) { - if (touchTimerTap > 0.0f && touchTimerTap < 0.4f) { // 100 ms gap to reduce false positives (bad touch sensors) + if (touchTimerTap > 0.0f && touchTimerTap < 0.3f) { // 100 ms gap to reduce false positives (bad touch sensors) doubleTap = true; touchTimerTap = 0.0f; } else - touchTimerTap = 0.5f; + touchTimerTap = 0.3f; } } } @@ -151,7 +150,7 @@ struct UI { Core::mViewProj = mat4(0.0f, float(Core::width), float(Core::height), 0.0f, 0.0f, 1.0f); - game->setShader(Core::passGUI, Shader::NONE); + game->setShader(Core::passGUI, Shader::DEFAULT); float offset = Core::height * 0.25f;