1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-11 15:45:05 +02:00

#23 shadow mask (volumes); #15 android fix double tap

This commit is contained in:
XProger
2017-03-26 03:01:31 +03:00
parent b481719277
commit 0380184967
8 changed files with 237 additions and 56 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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() {

View File

@@ -218,6 +218,7 @@
<None Include="..\..\filter.glsl" />
<None Include="..\..\gui.glsl" />
<None Include="..\..\shader.glsl" />
<None Include="..\..\volume.glsl" />
<None Include="..\..\water.glsl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -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
};

View File

@@ -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;