diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 0b255ee..6ae96e0 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/cache.h b/src/cache.h index ecde068..466cb56 100644 --- a/src/cache.h +++ b/src/cache.h @@ -131,7 +131,7 @@ struct ShaderCache { typ = typeNames[type]; int animTexRangesCount = game->getMesh()->animTexRangesCount; int animTexOffsetsCount = game->getMesh()->animTexOffsetsCount; - sprintf(def, "%s#define PASS_%s\n#define TYPE_%s\n#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n#define FOG_DIST (1.0/%d.0)\n#define WATER_FOG_DIST (1.0/%d.0)\n#define SHADOW_TEX_SIZE %d.0\n", ext, passNames[pass], typ, MAX_LIGHTS, animTexRangesCount, animTexOffsetsCount, FOG_DIST, WATER_FOG_DIST, SHADOW_TEX_SIZE); + sprintf(def, "%s#define PASS_%s\n#define TYPE_%s\n#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n#define MAX_CONTACTS %d\n#define FOG_DIST (1.0/%d.0)\n#define WATER_FOG_DIST (1.0/%d.0)\n#define SHADOW_TEX_SIZE %d.0\n", ext, passNames[pass], typ, MAX_LIGHTS, animTexRangesCount, animTexOffsetsCount, MAX_CONTACTS, FOG_DIST, WATER_FOG_DIST, SHADOW_TEX_SIZE); if (fx & FX_UNDERWATER) strcat(def, "#define UNDERWATER\n" UNDERWATER_COLOR); if (fx & FX_ALPHA_TEST) strcat(def, "#define ALPHA_TEST\n"); if (fx & FX_CLIP_PLANE) strcat(def, "#define CLIP_PLANE\n"); @@ -139,6 +139,7 @@ struct ShaderCache { if (Core::settings.detail.lighting) strcat(def, "#define OPT_LIGHTING\n"); if (Core::settings.detail.shadows) strcat(def, "#define OPT_SHADOW\n"); if (Core::settings.detail.water) strcat(def, "#define OPT_WATER\n"); + if (Core::settings.detail.contact) strcat(def, "#define OPT_CONTACT\n"); break; } case Core::passWater : { @@ -181,7 +182,6 @@ struct ShaderCache { // TODO: bindable uniform block shader->setParam(uViewProj, Core::mViewProj); shader->setParam(uLightProj, Core::mLightProj); - shader->setParam(uViewInv, Core::mViewInv); shader->setParam(uViewPos, Core::viewPos); shader->setParam(uParam, Core::params); MeshBuilder *mesh = game->getMesh(); @@ -282,17 +282,14 @@ struct AmbientCache { // get result color from 1x1 textures for (int j = 0; j < 6; j++) { Core::setTarget(textures[j * 4 + 3]); - - TR::Color32 color; - glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color); - colors[j] = vec3(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f); - // colors[j] *= colors[j]; // to "linear" space + colors[j] = Core::copyPixel(0, 0).xyz; } Core::setDepthTest(true); } void precessQueue() { + game->setupBinding(); for (int i = 0; i < tasksCount; i++) { Task &task = tasks[i]; renderAmbient(task.room, task.sector, &task.cube->colors[0]); @@ -411,13 +408,11 @@ struct WaterCache { mask = new Texture(w, h, Texture::RGB16, false, m, false); delete[] m; - Core::setTarget(data[0], true); - Core::setTarget(NULL); - blank = false; - // Core::setTarget(data[0], true); - // Core::invalidateTarget(false, true); + // texture may be initialized with trash, so... + Core::setTarget(data[0], true); + Core::validateRenderState(); // immediate clear } void free() { @@ -430,7 +425,6 @@ struct WaterCache { } items[MAX_SURFACES]; int count, visible; - bool checkVisibility; int dropCount; struct Drop { @@ -441,7 +435,7 @@ struct WaterCache { Drop(const vec3 &pos, float radius, float strength) : pos(pos), radius(radius), strength(strength) {} } drops[MAX_DROPS]; - WaterCache(IGame *game) : game(game), level(game->getLevel()), refract(NULL), count(0), checkVisibility(false), dropCount(0) { + WaterCache(IGame *game) : game(game), level(game->getLevel()), refract(NULL), count(0), dropCount(0) { reflect = new Texture(512, 512, Texture::RGB16, false); } @@ -473,8 +467,6 @@ struct WaterCache { } void setVisible(int roomIndex, int nextRoom = TR::NO_ROOM) { - if (!checkVisibility) return; - if (nextRoom == TR::NO_ROOM) { // setVisible(underwaterRoom) for caustics update for (int i = 0; i < count; i++) if (items[i].caust == roomIndex) { @@ -569,7 +561,7 @@ struct WaterCache { game->setShader(Core::passWater, Shader::WATER_STEP); Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f)); - Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, 0)); + Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, Core::params.x)); while (item.timer >= SIMULATE_TIMESTEP) { // water step @@ -581,7 +573,6 @@ struct WaterCache { swap(item.data[0], item.data[1]); item.timer -= SIMULATE_TIMESTEP; } - // calc caustics game->setShader(Core::passWater, Shader::WATER_CAUSTICS); @@ -602,13 +593,6 @@ struct WaterCache { void renderMask() { // mask underwater geometry by zero alpha - for (int i = 0; i < count; i++) { - Item &item = items[i]; - if (item.visible && item.blank) - item.init(game); - } - Core::setTarget(NULL); - game->setShader(Core::passWater, Shader::WATER_MASK); Core::active.shader->setParam(uTexParam, vec4(1.0f)); @@ -631,15 +615,15 @@ struct WaterCache { } void getRefract() { - int w = int(Core::viewport.z); - int h = int(Core::viewport.w); + int w = int(Core::viewportDef.z); + int h = int(Core::viewportDef.w); // get refraction texture if (!refract || w != refract->width || h != refract->height) { delete refract; refract = new Texture(w, h, Texture::RGBA, false); } - Core::copyTarget(refract, 0, 0, int(Core::viewport.x), int(Core::viewport.y), w, h); // copy framebuffer into refraction texture + Core::copyTarget(refract, 0, 0, int(Core::viewportDef.x), int(Core::viewportDef.y), w, h); // copy framebuffer into refraction texture } void simulate() { @@ -658,7 +642,48 @@ struct WaterCache { } } Core::setDepthTest(true); - Core::setTarget(NULL); + } + + void renderReflect() { + if (!visible) return; + + for (int i = 0; i < count; i++) { + Item &item = items[i]; + if (item.visible && item.blank) + item.init(game); + } + + // render mirror reflection + Core::setTarget(reflect, true); + Camera *camera = (Camera*)game->getCamera(); + game->setupBinding(); + + for (int i = 0; i < count; i++) { + Item &item = items[i]; + if (!item.visible) continue; + + vec3 p = item.pos; + vec3 n = vec3(0, 1, 0); + + vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); + bool underwater = level->rooms[camera->getRoomIndex()].flags.water; + + //bool underwater = level->camera->pos.y > item.pos.y; + + camera->reflectPlane = &reflectPlane; + camera->setup(true); + + float sign = underwater ? -1.0f : 1.0f; + game->setClipParams(sign, item.pos.y * sign); + game->updateParams(); + game->renderView(underwater ? item.from : item.to, false); + } + Core::invalidateTarget(false, true); + game->setClipParams(1.0f, NO_CLIP_PLANE); + game->updateParams(); + + camera->reflectPlane = NULL; + camera->setup(true); } void render() { @@ -666,34 +691,6 @@ struct WaterCache { Item &item = items[i]; if (!item.visible) continue; - // render mirror reflection - Core::setTarget(reflect, true); - Core::viewport = Core::viewportDef; - vec3 p = item.pos; - vec3 n = vec3(0, 1, 0); - - vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); - - Camera *camera = (Camera*)game->getCamera(); - - bool underwater = level->rooms[camera->getRoomIndex()].flags.water; - - //bool underwater = level->camera->pos.y > item.pos.y; - - camera->reflectPlane = &reflectPlane; - float sign = underwater ? -1.0f : 1.0f; - game->setClipParams(sign, item.pos.y * sign); - game->updateParams(); - game->renderCompose(underwater ? item.from : item.to); - Core::invalidateTarget(false, true); - game->setClipParams(1.0f, NO_CLIP_PLANE); - game->updateParams(); - - camera->reflectPlane = NULL; - Core::setTarget(NULL); - - camera->setup(true); - // render water plane if (level->rooms[item.from].lightsCount) { TR::Room::Light &light = level->rooms[item.from].lights[0]; @@ -705,7 +702,7 @@ struct WaterCache { game->setShader(Core::passWater, Shader::WATER_COMPOSE); Core::active.shader->setParam(uLightPos, Core::lightPos[0], 1); Core::active.shader->setParam(uLightColor, Core::lightColor[0], 1); - Core::active.shader->setParam(uParam, vec4(float(Core::width) / refract->width, float(Core::height) / refract->height, 0.05f, 0.02f)); + Core::active.shader->setParam(uParam, vec4(Core::viewportDef.z / refract->width, Core::viewportDef.w / refract->height, 0.05f, 0.02f)); float sx = item.size.x * DETAIL / (item.data[0]->width / 2); float sz = item.size.z * DETAIL / (item.data[0]->height / 2); diff --git a/src/controller.h b/src/controller.h index 230d481..cc2facc 100644 --- a/src/controller.h +++ b/src/controller.h @@ -30,7 +30,8 @@ struct IGame { virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {} virtual void setupBinding() {} virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {} - virtual void renderCompose(int roomIndex, bool genShadowMask = false) {} + virtual void renderCompose(int roomIndex) {} + virtual void renderView(int roomIndex, bool water) {} virtual void fxQuake(float time) {} virtual bool invUse(TR::Entity::Type type) { return false; } diff --git a/src/core.h b/src/core.h index 6ee1269..aa10ddd 100644 --- a/src/core.h +++ b/src/core.h @@ -43,8 +43,6 @@ #define glProgramBinary glProgramBinaryOES #define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES - #define GL_STENCIL_TEST_TWO_SIDE_EXT 0 - #define glActiveStencilFaceEXT(...) #elif __linux__ #define LINUX 1 #include @@ -72,9 +70,6 @@ #define GL_TEXTURE_COMPARE_MODE GL_TEXTURE_COMPARE_MODE_EXT #define GL_TEXTURE_COMPARE_FUNC GL_TEXTURE_COMPARE_FUNC_EXT #define GL_COMPARE_REF_TO_TEXTURE GL_COMPARE_REF_TO_TEXTURE_EXT - - #define GL_STENCIL_TEST_TWO_SIDE_EXT 0 - #define glActiveStencilFaceEXT(...) #else #include #include @@ -117,10 +112,8 @@ #define GL_CLAMP_TO_BORDER GL_CLAMP_TO_BORDER_EXT #define GL_TEXTURE_BORDER_COLOR GL_TEXTURE_BORDER_COLOR_EXT - #define GL_STENCIL_TEST_TWO_SIDE_EXT 0 #define glGetProgramBinary(...) #define glProgramBinary(...) - #define glActiveStencilFaceEXT(...) #endif namespace Core { @@ -204,10 +197,6 @@ namespace Core { PFNGLBINDBUFFERARBPROC glBindBuffer; PFNGLBUFFERDATAARBPROC glBufferData; PFNGLBUFFERSUBDATAARBPROC glBufferSubData; - // Stencil - PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFaceEXT; - PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; - PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; #endif PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; @@ -224,10 +213,41 @@ namespace Core { #define MAX_LIGHTS 4 #define MAX_CACHED_LIGHTS 3 #define MAX_RENDER_BUFFERS 32 +#define MAX_CONTACTS 15 struct Shader; struct Texture; +enum RenderState : int32 { + RS_TARGET = 1 << 0, + RS_VIEWPORT = 1 << 1, + RS_DEPTH_TEST = 1 << 2, + RS_DEPTH_WRITE = 1 << 3, + RS_COLOR_WRITE_R = 1 << 4, + RS_COLOR_WRITE_G = 1 << 5, + RS_COLOR_WRITE_B = 1 << 6, + RS_COLOR_WRITE_A = 1 << 7, + RS_COLOR_WRITE = RS_COLOR_WRITE_R | RS_COLOR_WRITE_G | RS_COLOR_WRITE_B | RS_COLOR_WRITE_A, + RS_CULL_BACK = 1 << 8, + RS_CULL_FRONT = 1 << 9, + RS_CULL = RS_CULL_BACK | RS_CULL_FRONT, + RS_BLEND_ALPHA = 1 << 10, + RS_BLEND_ADD = 1 << 11, + RS_BLEND_MULTIPLY = 1 << 12, + RS_BLEND_SCREEN = 1 << 13, + RS_BLEND = RS_BLEND_ADD | RS_BLEND_ALPHA | RS_BLEND_MULTIPLY | RS_BLEND_SCREEN, +}; + +typedef unsigned short Index; + +struct Vertex { + short4 coord; // xyz - position, w - joint index (for entities only) + short4 normal; // xyz - vertex normalá w - unused + short4 texCoord; // xy - texture coordinates, zw - trapezoid warping + ubyte4 param; // xy - anim tex range and frame index, zw - unused + ubyte4 color; // xyz - color, w - intensity +}; + namespace Core { struct { bool shaderBinary; @@ -240,7 +260,6 @@ namespace Core { bool texBorder; bool texFloat, texFloatLinear; bool texHalf, texHalfLinear; - char stencil; #ifdef PROFILE bool profMarker; bool profTiming; @@ -298,13 +317,13 @@ extern int getTime(); namespace Core { float eye; vec4 viewport, viewportDef; - vec4 scissor; mat4 mView, mProj, mViewProj, mViewInv, mLightProj; Basis basis; vec3 viewPos; vec3 lightPos[MAX_LIGHTS]; vec4 lightColor[MAX_LIGHTS]; vec4 params; + vec4 contacts[MAX_CONTACTS]; Texture *blackTex, *whiteTex; @@ -322,18 +341,25 @@ namespace Core { } items[MAX_RENDER_BUFFERS]; } rtCache[2]; + int32 renderState; + struct { Shader *shader; Texture *textures[8]; Texture *target; int targetFace; + vec4 viewport; GLuint VAO; GLuint iBuffer; GLuint vBuffer; - BlendMode blendMode; - CullMode cullMode; - bool stencilTwoSide; + int32 renderState; } active; + + struct { + Texture *texture; + bool clear; + uint8 face; + } reqTarget; struct Stats { int dips, tris, frame, fps, fpsTime; @@ -367,6 +393,7 @@ namespace Core { bool lighting; bool shadows; bool water; + bool contact; } detail; struct { @@ -445,10 +472,6 @@ namespace Core { GetProcOGL(glBindBuffer); GetProcOGL(glBufferData); GetProcOGL(glBufferSubData); - - GetProcOGL(glActiveStencilFaceEXT); - GetProcOGL(glStencilFuncSeparate); - GetProcOGL(glStencilOpSeparate); #endif #if defined(ANDROID) || defined(__EMSCRIPTEN__) @@ -478,14 +501,6 @@ namespace Core { 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"); @@ -508,7 +523,6 @@ 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"); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFBO); @@ -567,143 +581,142 @@ namespace Core { return cache.count++; } - 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); + void validateRenderState() { + int32 mask = renderState ^ active.renderState; + if (!mask) return; + + if (mask & RS_TARGET) { + Texture *target = reqTarget.texture; + uint8 face = reqTarget.face; + + if (target != active.target || face != active.targetFace) { + + if (!target) { // may be a null + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + } else { + GLenum texTarget = GL_TEXTURE_2D; + if (target->cube) + texTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + + bool depth = target->format == Texture::DEPTH || target->format == Texture::SHADOW; + int rtIndex = cacheRenderTarget(depth, target->width, target->height); + + glBindFramebuffer(GL_FRAMEBUFFER, FBO); + glFramebufferTexture2D (GL_FRAMEBUFFER, depth ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, texTarget, target->ID, 0); + glFramebufferRenderbuffer (GL_FRAMEBUFFER, depth ? GL_COLOR_ATTACHMENT0 : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rtCache[depth].items[rtIndex].ID); + } + + active.target = target; + active.targetFace = face; + } + } + + if (mask & RS_VIEWPORT) { + if (viewport != active.viewport) { + active.viewport = viewport; + glViewport(int(viewport.x), int(viewport.y), int(viewport.z), int(viewport.w)); + } + renderState &= ~RS_VIEWPORT; + } + + if (mask & RS_DEPTH_TEST) { + if (renderState & RS_DEPTH_TEST) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + } + + if (mask & RS_DEPTH_WRITE) { + glDepthMask((renderState & RS_DEPTH_WRITE) != 0); + } + + if (mask & RS_COLOR_WRITE) { + glColorMask((renderState & RS_COLOR_WRITE_R) != 0, + (renderState & RS_COLOR_WRITE_G) != 0, + (renderState & RS_COLOR_WRITE_B) != 0, + (renderState & RS_COLOR_WRITE_A) != 0); + } + + if (mask & RS_CULL) { + if (!(active.renderState & RS_CULL)) + glEnable(GL_CULL_FACE); + switch (renderState & RS_CULL) { + case RS_CULL_BACK : glCullFace(GL_BACK); break; + case RS_CULL_FRONT : glCullFace(GL_FRONT); break; + default : glDisable(GL_CULL_FACE); + } + } + + if (mask & RS_BLEND) { + if (!(active.renderState & RS_BLEND)) + glEnable(GL_BLEND); + switch (renderState & RS_BLEND) { + case RS_BLEND_ALPHA : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + case RS_BLEND_ADD : glBlendFunc(GL_ONE, GL_ONE); break; + case RS_BLEND_MULTIPLY : glBlendFunc(GL_DST_COLOR, GL_ZERO); break; + case RS_BLEND_SCREEN : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); break; + default : glDisable(GL_BLEND); + } + } + + if (mask & RS_TARGET) { // for cler the RT & reset mask + if (reqTarget.clear) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + renderState &= ~RS_TARGET; + } + + active.renderState = renderState; } void setClearColor(const vec4 &color) { glClearColor(color.x, color.y, color.z, color.w); } - void setClearStencil(int value) { - glClearStencil(value); - } - - void setViewport(const vec4 &vp) { - glViewport(int(vp.x), int(vp.y), int(vp.z), int(vp.w)); - viewport = vp; - } - void setViewport(int x, int y, int width, int height) { - setViewport(vec4(float(x), float(y), float(width), float(height))); - } - - void setScissor(int x, int y, int width, int height) { - glScissor(x, y, width, height); - scissor = vec4(float(x), float(y), float(width), float(height)); + viewport = vec4(float(x), float(y), float(width), float(height)); + renderState |= RS_VIEWPORT; } void setCulling(CullMode mode) { - if (active.cullMode == mode) - return; - + renderState &= ~RS_CULL; switch (mode) { - case cfNone : - glDisable(GL_CULL_FACE); - case cfBack : - glCullFace(GL_BACK); - break; - case cfFront : - glCullFace(GL_FRONT); - break; + case cfNone : break; + case cfBack : renderState |= RS_CULL_BACK; break; + case cfFront : renderState |= RS_CULL_FRONT; break; } - - if (mode != cfNone && active.cullMode == cfNone) - glEnable(GL_CULL_FACE); - - active.cullMode = mode; } void setBlending(BlendMode mode) { - if (active.blendMode == mode) - return; - + renderState &= ~RS_BLEND; switch (mode) { - case bmNone : - glDisable(GL_BLEND); - break; - case bmAlpha : - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case bmAdd : - glBlendFunc(GL_ONE, GL_ONE); - break; - case bmMultiply : - glBlendFunc(GL_DST_COLOR, GL_ZERO); - break; - case bmScreen : - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); - break; + case bmNone : break; + case bmAlpha : renderState |= RS_BLEND_ALPHA; break; + case bmAdd : renderState |= RS_BLEND_ADD; break; + case bmMultiply : renderState |= RS_BLEND_MULTIPLY; break; + case bmScreen : renderState |= RS_BLEND_SCREEN; break; } - - if (mode != bmNone && active.blendMode == bmNone) - glEnable(GL_BLEND); - - active.blendMode = mode; } void setColorWrite(bool r, bool g, bool b, bool a) { - glColorMask(r, g, b, a); + renderState &= ~RS_COLOR_WRITE; + if (r) renderState |= RS_COLOR_WRITE_R; + if (g) renderState |= RS_COLOR_WRITE_G; + if (b) renderState |= RS_COLOR_WRITE_B; + if (a) renderState |= RS_COLOR_WRITE_A; } void setDepthWrite(bool write) { - glDepthMask(write); + if (write) + renderState |= RS_DEPTH_WRITE; + else + renderState &= ~RS_DEPTH_WRITE; } void setDepthTest(bool test) { if (test) - glEnable(GL_DEPTH_TEST); + renderState |= RS_DEPTH_TEST; else - glDisable(GL_DEPTH_TEST); - } - - void setStencilTest(bool test) { - if (test) - glEnable(GL_STENCIL_TEST); - else - glDisable(GL_STENCIL_TEST); - } - - void setScissorTest(bool test) { - if (test) - glEnable(GL_SCISSOR_TEST); - else - glDisable(GL_SCISSOR_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_KEEP); - glStencilFunc(GL_NOTEQUAL, ref, ~0); - setCulling(cfFront); - } + renderState &= ~RS_DEPTH_TEST; } void invalidateTarget(bool color, bool depth) { @@ -719,64 +732,59 @@ namespace Core { } void setTarget(Texture *target, bool clear = false, int face = 0) { - if (!target && defaultTarget) + if (!target) target = defaultTarget; - if (target != active.target || face != active.targetFace) { - if (!target) { - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glColorMask(true, true, true, true); + bool color = !target || (target->format != Texture::DEPTH && target->format != Texture::SHADOW); + setColorWrite(color, color, color, color); - setViewport(int(viewportDef.x), int(viewportDef.y), int(viewportDef.z), int(viewportDef.w)); - } else { - if (active.target == NULL || active.target == defaultTarget) - viewportDef = viewport; - GLenum texTarget = GL_TEXTURE_2D; - if (target->cube) - texTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + if (!target) // backbuffer + setViewport(int(viewportDef.x), int(viewportDef.y), int(viewportDef.z), int(viewportDef.w)); + else + setViewport(0, 0, target->width, target->height); - bool depth = target->format == Texture::DEPTH || target->format == Texture::SHADOW; - int rtIndex = cacheRenderTarget(depth, target->width, target->height); - - glBindFramebuffer(GL_FRAMEBUFFER, FBO); - glFramebufferTexture2D (GL_FRAMEBUFFER, depth ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, texTarget, target->ID, 0); - glFramebufferRenderbuffer (GL_FRAMEBUFFER, depth ? GL_COLOR_ATTACHMENT0 : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rtCache[depth].items[rtIndex].ID); - - if (depth) - glColorMask(false, false, false, false); - else - glColorMask(true, true, true, true); - setViewport(0, 0, target->width, target->height); - } - } - - if (clear) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - active.target = target; - active.targetFace = face; + reqTarget.texture = target; + reqTarget.clear = clear; + reqTarget.face = face; + renderState |= RS_TARGET; } - void copyTarget(Texture *texture, int xOffset, int yOffset, int x, int y, int width, int height) { - texture->bind(sDiffuse); + void copyTarget(Texture *dst, int xOffset, int yOffset, int x, int y, int width, int height) { + validateRenderState(); + dst->bind(sDiffuse); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); } + vec4 copyPixel(int x, int y) { // GPU sync! + validateRenderState(); + ubyte4 c; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &c); + return vec4(float(c.x), float(c.y), float(c.z), float(c.w)) * (1.0f / 255.0f); + } + void beginFrame() { - memset(&active, 0, sizeof(active)); + //memset(&active, 0, sizeof(active)); setViewport(0, 0, Core::width, Core::height); viewportDef = viewport; - setDepthTest(true); - active.blendMode = bmAlpha; - active.cullMode = cfNone; setCulling(cfFront); - setBlending(bmNone); + setBlending(bmAlpha); + setDepthTest(true); + setDepthWrite(true); + setColorWrite(true, true, true, true); + Core::stats.start(); } void endFrame() { Core::stats.stop(); } + + void DIP(int iStart, int iCount) { + validateRenderState(); + glDrawElements(GL_TRIANGLES, iCount, GL_UNSIGNED_SHORT, (Index*)NULL + iStart); + stats.dips++; + stats.tris += iCount / 3; + } } #include "mesh.h" diff --git a/src/debug.h b/src/debug.h index 4a53801..18eef15 100644 --- a/src/debug.h +++ b/src/debug.h @@ -60,6 +60,7 @@ namespace Debug { glUseProgram(0); Core::active.shader = NULL; Core::active.textures[0] = NULL; + Core::validateRenderState(); } void end() { @@ -178,15 +179,16 @@ namespace Debug { glLoadIdentity(); glOrtho(0, Core::width, Core::height, 0, 0, 1); - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); + Core::setDepthTest(false); + Core::setCulling(cfNone); + Core::validateRenderState(); + glColor4fv((GLfloat*)&color); glRasterPos2f(pos.x, pos.y); glListBase(font); glCallLists(strlen(str), GL_UNSIGNED_BYTE, str); - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); + Core::setDepthTest(true); + Core::setCulling(cfFront); glPopMatrix(); glMatrixMode(GL_MODELVIEW); diff --git a/src/format.h b/src/format.h index 625694a..3bfd48c 100644 --- a/src/format.h +++ b/src/format.h @@ -412,7 +412,8 @@ namespace TR { struct Vertex { int16 x, y, z; - operator vec3() const { return vec3((float)x, (float)y, (float)z); }; + operator vec3() const { return vec3((float)x, (float)y, (float)z); } + operator short3() const { return *((short3*)this); } }; struct Rectangle { @@ -480,7 +481,7 @@ namespace TR { uint16 meshesCount; int16 alternateRoom; struct { - uint16 water:1, unused:14, rendered:1; + uint16 water:1, unused:14, visible:1; } flags; struct Portal { @@ -524,6 +525,10 @@ namespace TR { uint16 meshID; uint16 meshIndex; // index into static meshes array } *meshes; + + vec3 getOffset() const { + return vec3(float(info.x), 0.0f, float(info.z)); + } }; union FloorData { diff --git a/src/frustum.h b/src/frustum.h index 2e5a523..670b1f4 100644 --- a/src/frustum.h +++ b/src/frustum.h @@ -6,24 +6,11 @@ #define MAX_CLIP_PLANES 16 struct Frustum { - - struct Poly { - vec3 vertices[MAX_CLIP_PLANES]; - int count; - }; - vec3 pos; vec4 planes[MAX_CLIP_PLANES * 2]; // + buffer for OBB visibility test int start, count; -#ifdef _DEBUG - int dbg; - Poly debugPoly; -#endif void calcPlanes(const mat4 &m) { - #ifdef _DEBUG - dbg = 0; - #endif start = 0; count = 5; planes[0] = vec4(m.e30 - m.e20, m.e31 - m.e21, m.e32 - m.e22, m.e33 - m.e23); // near @@ -35,71 +22,6 @@ struct Frustum { planes[i] *= 1.0f / planes[i].xyz.length(); } - void calcPlanes(const Poly &poly) { - count = 1 + poly.count; // add one for near plane (not changing) - ASSERT(count < MAX_CLIP_PLANES); - if (count < 4) return; - - vec3 e1 = poly.vertices[0] - pos; - for (int i = 1; i < count; i++) { - vec3 e2 = poly.vertices[i % poly.count] - pos; - planes[i].xyz = e1.cross(e2).normal(); - planes[i].w = -(pos.dot(planes[i].xyz)); - e1 = e2; - } - } - - void clipPlane(const Poly &src, Poly &dst, const vec4 &plane) { - dst.count = 0; - if (!src.count) return; - - float t1 = src.vertices[0].dot(plane.xyz) + plane.w; - - for (int i = 0; i < src.count; i++) { - const vec3 &v1 = src.vertices[i]; - const vec3 &v2 = src.vertices[(i + 1) % src.count]; - - float t2 = v2.dot(plane.xyz) + plane.w; - - // hack for big float numbers - int s1 = (int)t1; - int s2 = (int)t2; - - if (s1 >= 0) { - dst.vertices[dst.count++] = v1; - ASSERT(dst.count < MAX_CLIP_PLANES); - } - - if ((s1 ^ s2) < 0) { // check for opposite signs - float k1 = t2 / (t2 - t1); - float k2 = t1 / (t2 - t1); - dst.vertices[dst.count++] = v1 * (float)k1 - v2 * (float)k2; - ASSERT(dst.count < MAX_CLIP_PLANES); - } - - t1 = t2; - } - } - - bool clipByPortal(const vec3 *vertices, int vCount, const vec3 &normal) { - if (normal.dot(pos - vertices[0]) < 0.0f) // check portal winding order - return false; - - Poly poly[2]; - - poly[0].count = vCount; - memmove(poly[0].vertices, vertices, sizeof(vec3) * poly[0].count); -#ifdef _DEBUG - debugPoly.count = 0; -#endif - int j = 0; - for (int i = 1; i < count; i++, j ^= 1) - clipPlane(poly[j], poly[j ^ 1], planes[i]); - - calcPlanes(poly[j]); - return count >= 4; - } - // AABB visibility check bool isVisible(const vec3 &min, const vec3 &max) const { if (count < 4) return false; diff --git a/src/game.h b/src/game.h index 6d88517..0fd2ef9 100644 --- a/src/game.h +++ b/src/game.h @@ -23,6 +23,7 @@ namespace Game { Core::settings.detail.lighting = true; Core::settings.detail.shadows = true; Core::settings.detail.water = Core::support.texFloat || Core::support.texHalf; + Core::settings.detail.contact = false; Core::settings.controls.retarget = true; diff --git a/src/input.h b/src/input.h index 7b7de9f..172eeae 100644 --- a/src/input.h +++ b/src/input.h @@ -33,7 +33,7 @@ namespace Input { { ikRight, ikJoyRight }, { ikUp, ikJoyUp }, { ikDown, ikJoyDown }, - { ikAlt, ikJoyX }, + { ikD, ikJoyX }, { ikShift, ikJoyRB }, { ikCtrl, ikJoyA }, { ikSpace, ikJoyY }, @@ -111,7 +111,7 @@ namespace Input { } } head; - enum TouchButton { bNone, bWeapon, bWalk, bAction, bJump, bMAX }; + enum TouchButton { bNone, bWeapon, bWalk, bAction, bJump, bInventory, bMAX }; enum TouchZone { zMove, zLook, zButton, zMAX }; float touchTimerVis, touchTimerTap; @@ -245,11 +245,12 @@ namespace Input { float radius = offset; vec2 center = vec2(Core::width - offset * 0.7f, Core::height - offset * 0.7f); - btnPos[bWeapon] = center; - btnPos[bJump] = center + vec2(cosf(-PI * 0.5f), sinf(-PI * 0.5f)) * radius; - btnPos[bAction] = center + vec2(cosf(-PI * 3.0f / 4.0f), sinf(-PI * 3.0f / 4.0f)) * radius; - btnPos[bWalk] = center + vec2(cosf(-PI), sinf(-PI)) * radius; - btnRadius = Core::height * (25.0f / 1080.0f); + btnRadius = Core::height * (25.0f / 1080.0f); + btnPos[bWeapon] = center; + btnPos[bJump] = center + vec2(cosf(-PI * 0.5f), sinf(-PI * 0.5f)) * radius; + btnPos[bAction] = center + vec2(cosf(-PI * 3.0f / 4.0f), sinf(-PI * 3.0f / 4.0f)) * radius; + btnPos[bWalk] = center + vec2(cosf(-PI), sinf(-PI)) * radius; + btnPos[bInventory] = vec2(Core::width - btnRadius * 2.0f, btnRadius * 2.0f); // touch update if (checkTouchZone(zMove)) @@ -305,11 +306,12 @@ namespace Input { } switch (btn) { - case bWeapon : state[cWeapon] = true; break; - case bWalk : state[cWalk] = true; break; - case bAction : state[cAction] = true; break; - case bJump : state[cJump] = true; break; - default : ; + case bWeapon : state[cWeapon] = true; break; + case bWalk : state[cWalk] = true; break; + case bAction : state[cAction] = true; break; + case bJump : state[cJump] = true; break; + case bInventory : state[cInventory] = true; break; + default : ; } } diff --git a/src/inventory.h b/src/inventory.h index 1c0897f..7926f0f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -297,10 +297,10 @@ struct Inventory { if (type != TR::Entity::NONE) { int i = contains(type); if (i >= 0) - pageItemIndex[page] = getItemIndex(page, i); + pageItemIndex[page] = getLocalIndex(i); } - index = targetIndex = pageItemIndex[page]; + index = targetIndex = pageItemIndex[page]; } } return active; @@ -322,16 +322,27 @@ struct Inventory { } } - int getItemIndex(Page page, int index) { + int getGlobalIndex(Page page, int localIndex) { for (int i = 0; i < itemsCount; i++) if (items[i]->desc.page == page) { - if (!index) + if (!localIndex) return i; - index--; + localIndex--; } return 0; } + int getLocalIndex(int globalIndex) { + Page page = items[globalIndex]->desc.page; + + int idx = 0; + for (int i = 0; i < globalIndex; i++) + if (items[i]->desc.page == page) + idx++; + + return idx; + } + float getAngle(int index, int count) { return PI * 2.0f / float(count) * index; } @@ -347,7 +358,7 @@ struct Inventory { } bool showHealthBar() { - int idx = getItemIndex(page, index); + int idx = getGlobalIndex(page, index); TR::Entity::Type type = items[idx]->type; return active && phaseRing == 1.0f && index == targetIndex && phasePage == 1.0f && (type == TR::Entity::INV_MEDIKIT_SMALL || type == TR::Entity::INV_MEDIKIT_BIG); } @@ -372,10 +383,12 @@ struct Inventory { bool ready = active && phaseRing == 1.0f && phasePage == 1.0f; if (index == targetIndex && targetPage == page && ready && !chosen) { - if (Input::state[cLeft]) { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; } - if (Input::state[cRight]) { phaseSelect = 0.0f; targetIndex = (targetIndex + 1) % count; } - if (Input::state[cUp] && page < PAGE_ITEMS && getItemsCount(page + 1)) { phasePage = 0.0f; targetPage = Page(page + 1); } - if (Input::state[cDown] && page > PAGE_OPTION && getItemsCount(page - 1)) { phasePage = 0.0f; targetPage = Page(page - 1); } + float s = Input::touchTimerVis > 0.0f ? -1.0f : 1.0f; + + if (Input::state[cLeft] || Input::joy.L.x < -0.5f || Input::joy.R.x > 0.5f) { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; } + if (Input::state[cRight] || Input::joy.L.x > 0.5f || Input::joy.R.x < -0.5f) { phaseSelect = 0.0f; targetIndex = (targetIndex + 1) % count; } + if ((Input::state[cUp] || Input::joy.L.y < -0.5f || Input::joy.R.y > 0.5f) && page < PAGE_ITEMS && getItemsCount(page + 1)) { phasePage = 0.0f; targetPage = Page(page + 1); } + if ((Input::state[cDown] || Input::joy.L.y > 0.5f || Input::joy.R.y < -0.5f) && page > PAGE_OPTION && getItemsCount(page - 1)) { phasePage = 0.0f; targetPage = Page(page - 1); } if (index != targetIndex) { vec3 p; @@ -385,7 +398,7 @@ struct Inventory { vec3 p; - Item *item = items[getItemIndex(page, index)]; + Item *item = items[getGlobalIndex(page, index)]; if (index == targetIndex && ready) { if (Input::state[cAction] && (phaseChoose == 0.0f || (phaseChoose == 1.0f && item->anim->isEnded))) { @@ -410,7 +423,7 @@ struct Inventory { float w = 90.0f * DEG2RAD * Core::deltaTime; - int itemIndex = index == targetIndex ? getItemIndex(page, index) : -1; + int itemIndex = index == targetIndex ? getGlobalIndex(page, index) : -1; for (int i = 0; i < itemsCount; i++) { items[i]->update(); @@ -610,10 +623,10 @@ struct Inventory { vec3(0.4f), vec3(0.2f), vec3(0.4f), vec3(0.5f), vec3(0.4f), vec3(0.6f) }; - Core::lightPos[0] = vec3(1000, 2000, 1000); - Core::lightColor[0] = vec4(1, 1, 1, 8192); - for (int i = 1; i < MAX_LIGHTS; i++) - Core::lightColor[1] = vec4(0, 0, 0, 1); + for (int i = 0; i < MAX_LIGHTS; i++) { + Core::lightPos[i] = vec3(0, 0, 0); + Core::lightColor[i] = vec4(0, 0, 0, 1); + } Core::active.shader->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); Core::active.shader->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); @@ -642,7 +655,7 @@ struct Inventory { } if (index == targetIndex) - renderItemText(items[getItemIndex(page, index)], UI::width); + renderItemText(items[getGlobalIndex(page, index)], UI::width); } }; diff --git a/src/lara.h b/src/lara.h index 2f2a92b..9d98126 100644 --- a/src/lara.h +++ b/src/lara.h @@ -425,10 +425,8 @@ struct Lara : Character { if (level->extra.braid > -1) braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f)); - #ifdef _DEBUG - //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, true); // gym (pool) - + //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) //reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) //reset(61, vec3(27221, -1024, 29205), PI * 0.5f); // level 2 (blade) @@ -1301,6 +1299,7 @@ struct Lara : Character { damageTime = LARA_DAMAGE_TIME; health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH)); playSound(TR::SND_HEALTH, pos, Sound::PAN); + //TODO: remove medikit item break; case TR::Entity::INV_PUZZLE_1 : case TR::Entity::INV_PUZZLE_2 : @@ -1557,12 +1556,16 @@ struct Lara : Character { if (state == STATE_HANDSTAND || state == STATE_HANG_UP) return STAND_HANG; - if (stand == STAND_ONWATER && state != STATE_DIVE && state != STATE_STOP) - return stand; + if (stand == STAND_ONWATER && state != STATE_STOP) { + if (!getRoom().flags.water && state != STATE_WATER_OUT) + return STAND_AIR; + if (state != STATE_DIVE) + return stand; + } if (getRoom().flags.water) { wpnHide(); - if (stand != STAND_UNDERWATER && (state != STATE_FALL && state != STATE_FALL_BACK && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE)) + if (stand != STAND_UNDERWATER && stand != STAND_ONWATER && (state != STATE_FALL && state != STATE_REACH && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE)) animation.setAnim(ANIM_FALL_FORTH); return STAND_UNDERWATER; } diff --git a/src/level.h b/src/level.h index 4253331..8ed8c19 100644 --- a/src/level.h +++ b/src/level.h @@ -101,28 +101,18 @@ struct Level : IGame { virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) { PROFILE_MARKER("ENVIRONMENT"); Core::eye = 0.0f; + setupBinding(); // first pass render level into cube faces for (int i = 0; i < 6; i++) { setupCubeCamera(pos, i); Core::pass = Core::passAmbient; Texture *target = targets[0]->cube ? targets[0] : targets[i * stride]; Core::setTarget(target, true, i); - renderScene(roomIndex); + renderView(roomIndex, false); Core::invalidateTarget(false, true); } } - virtual void renderCompose(int roomIndex, bool genShadowMask = false) { - PROFILE_MARKER("PASS_COMPOSE"); - - if (shadow) shadow->bind(sShadow); - - Core::setDepthTest(true); - Core::setDepthWrite(true); - Core::pass = Core::passCompose; - renderScene(roomIndex); - } - virtual void fxQuake(float time) { camera->shake = time; } @@ -411,8 +401,14 @@ struct Level : IGame { fwrite(data, 1024 * 1024 * 4, 1, f); fclose(f); */ + /* + memset(data, 255, 4 * 1024 * 1024); + for (int i = 0; i < 1024; i++) + for (int j = 0; j < 1024; j++) + data[i * 1024 + j].b = data[i * 1024 + j].g = ((i % 8 == 0) || (j % 8 == 0)) ? 0 : 255; + */ - atlas = new Texture(1024, 1024, Texture::RGBA, false, data); + atlas = new Texture(1024, 1024, Texture::RGBA, false, data, true, false); // TODO: generate mips PROFILE_LABEL(TEXTURE, atlas->ID, "atlas"); uint32 whitePix = 0xFFFFFFFF; @@ -494,98 +490,97 @@ struct Level : IGame { } Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha)); + if (Core::settings.detail.contact) + Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS); } - void renderRoom(int roomIndex, int from = TR::NO_ROOM) { - ASSERT(roomIndex >= 0 && roomIndex < level.roomsCount); - PROFILE_MARKER("ROOM"); - - TR::Room &room = level.rooms[roomIndex]; - vec3 offset = vec3(float(room.info.x), 0.0f, float(room.info.z)); - - // room geometry & sprites - if (!room.flags.rendered) { // skip if already rendered - if (waterCache && room.flags.water) - waterCache->setVisible(roomIndex); - - room.flags.rendered = true; - - if (Core::pass != Core::passShadow) { - - Basis qTemp = Core::basis; - Core::basis.translate(offset); - - MeshBuilder::RoomRange &range = mesh->rooms[roomIndex]; - - for (int transp = 0; transp < 2; transp++) { - if (!range.geometry[transp].iCount) - continue; - - Core::setBlending(transp ? bmAlpha : bmNone); - - setRoomParams(roomIndex, Shader::ROOM, 1.0f, intensityf(room.ambient), 0.0f, 1.0f, transp > 0); - Shader *sh = Core::active.shader; - sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); - sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); - sh->setParam(uBasis, Core::basis); - - // render room geometry - mesh->renderRoomGeometry(roomIndex, transp > 0); - } - - // render room sprites - if (range.sprites.iCount) { - Core::setBlending(bmAlpha); - setRoomParams(roomIndex, Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, true); - Shader *sh = Core::active.shader; - sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); - sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); - sh->setParam(uBasis, Core::basis); - mesh->renderRoomSprites(roomIndex); - } - - Core::basis = qTemp; - } - Core::setBlending(bmNone); - } else - return; - - #ifdef LEVEL_EDITOR - return; - #endif - - // render rooms through portals recursively - Frustum *camFrustum = camera->frustum; // push camera frustum - Frustum frustum; - camera->frustum = &frustum; - - for (int i = 0; i < room.portalsCount; i++) { - TR::Room::Portal &p = room.portals[i]; - - if (p.roomIndex == from) continue; - - vec3 v[] = { - offset + p.vertices[0], - offset + p.vertices[1], - offset + p.vertices[2], - offset + p.vertices[3], - }; - - frustum = *camFrustum; - if (frustum.clipByPortal(v, 4, p.normal)) { - if (waterCache &&(level.rooms[roomIndex].flags.water ^ level.rooms[p.roomIndex].flags.water) && v[0].y == v[1].y && v[0].y == v[2].y) - waterCache->setVisible(roomIndex, p.roomIndex); - renderRoom(p.roomIndex, roomIndex); - } - } - camera->frustum = camFrustum; // pop camera frustum - } - void setMainLight(Controller *controller) { Core::lightPos[0] = controller->mainLightPos; Core::lightColor[0] = vec4(controller->mainLightColor.xyz, 1.0f / controller->mainLightColor.w); } + void renderRooms(int *roomsList, int roomsCount) { + PROFILE_MARKER("ROOMS"); + + for (int i = 0; i < level.roomsCount; i++) + level.rooms[i].flags.visible = false; + + for (int i = 0; i < roomsCount; i++) + level.rooms[roomsList[i]].flags.visible = true; + + if (Core::pass == Core::passShadow) + return; + + if (Core::settings.detail.contact) { + Sphere spheres[MAX_CONTACTS]; + int spheresCount; + lara->getSpheres(spheres, spheresCount); + + for (int i = 0; i < MAX_CONTACTS; i++) + if (i < spheresCount) + Core::contacts[i] = vec4(spheres[i].center, PI * spheres[i].radius * spheres[i].radius * 0.25f); + else + Core::contacts[i] = vec4(0.0f); + } + + setMainLight(lara); + + bool hasGeom[2], hasSprite; + hasGeom[0] = hasGeom[1] = hasSprite = false; + + Basis basis; + basis.identity(); + + Core::setBlending(bmNone); + for (int transp = 0; transp < 2; transp++) { + for (int i = 0; i < roomsCount; i++) { + int roomIndex = roomsList[i]; + MeshBuilder::RoomRange &range = mesh->rooms[roomIndex]; + + if (!range.geometry[transp].iCount) + continue; + + setRoomParams(roomIndex, Shader::ROOM, 1.0f, intensityf(level.rooms[roomIndex].ambient), 0.0f, 1.0f, transp > 0); + Shader *sh = Core::active.shader; + + if (!hasGeom[transp]) { + hasGeom[transp] = true; + sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); + sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); + } + + basis.pos = level.rooms[roomIndex].getOffset(); + sh->setParam(uBasis, basis); + mesh->renderRoomGeometry(roomIndex, transp > 0); + } + Core::setBlending(bmAlpha); + } + + basis.rot = Core::mViewInv.getRot(); + for (int i = 0; i < roomsCount; i++) { + level.rooms[roomsList[i]].flags.visible = true; + + int roomIndex = roomsList[i]; + MeshBuilder::RoomRange &range = mesh->rooms[roomIndex]; + + if (!range.sprites.iCount) + continue; + + setRoomParams(roomIndex, Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, true); + Shader *sh = Core::active.shader; + if (!hasSprite) { + sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); + sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); + } + + basis.pos = level.rooms[roomIndex].getOffset(); + sh->setParam(uBasis, basis); + mesh->renderRoomSprites(roomIndex); + } + + Core::setBlending(bmNone); + } + void renderEntity(const TR::Entity &entity) { //if (entity.room != lara->getRoomIndex()) return; if (entity.type == TR::Entity::NONE || !entity.modelIndex) return; @@ -597,10 +592,9 @@ struct Level : IGame { Controller *controller = (Controller*)entity.controller; - TR::Room &room = level.rooms[entity.room]; if (entity.type != TR::Entity::LARA) // TODO: remove this hack (collect conjugate room entities) - if (!room.flags.rendered || entity.flags.invisible || entity.flags.rendered) + if (!room.flags.visible || entity.flags.invisible || entity.flags.rendered) return; int16 lum = entity.intensity == -1 ? room.ambient : entity.intensity; @@ -610,8 +604,6 @@ struct Level : IGame { type = Shader::MIRROR; int roomIndex = entity.room; -// if (entity.type == TR::Entity::LARA && ((Lara*)entity.controller)->state == Lara::STATE_WATER_OUT) -// roomIndex = ((Lara*)entity.controller)->roomPrev; setRoomParams(roomIndex, type, 1.0f, intensityf(lum), controller->specular, 1.0f, isModel ? !mesh->models[entity.modelIndex - 1].opaque : true); @@ -688,31 +680,6 @@ struct Level : IGame { camera->setup(Core::pass == Core::passCompose); setupBinding(); - - // clear visibility flag for rooms - for (int i = 0; i < level.roomsCount; i++) - level.rooms[i].flags.rendered = false; - - if (Core::pass != Core::passAmbient) - for (int i = 0; i < level.entitiesCount; i++) - level.entities[i].flags.rendered = false; - } - - void renderRooms(int roomIndex) { - PROFILE_MARKER("ROOMS"); - - setMainLight(lara); - - #ifdef LEVEL_EDITOR - for (int i = 0; i < level.roomsCount; i++) - renderRoom(i); - #else - if (!camera->cutscene) - renderRoom(roomIndex); - else // TODO: use brain - for (int i = 0; i < level.roomsCount; i++) - renderRoom(i); - #endif } void renderEntities() { @@ -727,12 +694,161 @@ struct Level : IGame { } } - void renderScene(int roomIndex) { - PROFILE_MARKER("SCENE"); - setup(); - renderRooms(roomIndex); + bool checkPortal(const TR::Room &room, const TR::Room::Portal &portal, const vec4 &viewPort, vec4 &clipPort) { + vec3 n = portal.normal; + vec3 v = Core::viewPos - (room.getOffset() + portal.vertices[0]); + + if (n.dot(v) <= 0.0f) + return false; + + int zClip = 0; + vec4 p[4]; + + clipPort = vec4(INF, INF, -INF, -INF); + + for (int i = 0; i < 4; i++) { + p[i] = Core::mViewProj * vec4(vec3(portal.vertices[i]) + room.getOffset(), 1.0f); + + if (p[i].w > 0.0f) { + p[i].xyz *= (1.0f / p[i].w); + + clipPort.x = min(clipPort.x, p[i].x); + clipPort.y = min(clipPort.y, p[i].y); + clipPort.z = max(clipPort.z, p[i].x); + clipPort.w = max(clipPort.w, p[i].y); + } else + zClip++; + } + + if (zClip == 4) + return false; + + if (zClip > 0) { + for (int i = 0; i < 4; i++) { + vec4 &a = p[i]; + vec4 &b = p[(i + 1) % 4]; + + if ((a.w > 0.0f) ^ (b.w > 0.0f)) { + + if (a.x < 0.0f && b.x < 0.0f) + clipPort.x = -1.0f; + else + if (a.x > 0.0f && b.x > 0.0f) + clipPort.z = 1.0f; + else { + clipPort.x = -1.0f; + clipPort.z = 1.0f; + } + + if (a.y < 0.0f && b.y < 0.0f) + clipPort.y = -1.0f; + else + if (a.y > 0.0f && b.y > 0.0f) + clipPort.w = 1.0f; + else { + clipPort.y = -1.0f; + clipPort.w = 1.0f; + } + + } + } + } + + if (clipPort.x > clipPort.z || clipPort.y > clipPort.w) + return false; + + if (clipPort.x > viewPort.z || clipPort.y > viewPort.w || clipPort.z < viewPort.x || clipPort.w < viewPort.y) + return false; + + clipPort.x = max(clipPort.x, viewPort.x); + clipPort.y = max(clipPort.y, viewPort.y); + clipPort.z = min(clipPort.z, viewPort.z); + clipPort.w = min(clipPort.w, viewPort.w); + + return true; + } + + void getVisibleRooms(int *roomsList, int &roomsCount, int from, int to, const vec4 &viewPort, bool water, int count = 0) { + if (camera->cutscene) { + roomsCount = level.roomsCount; + for (int i = 0; i < roomsCount; i++) + roomsList[i] = i; + return; + } + + if (count > 16) { + ASSERT(false); + return; + } + + TR::Room &room = level.rooms[to]; + + if (!room.flags.visible) { + if (Core::pass == Core::passCompose && water && waterCache && from != TR::NO_ROOM && (level.rooms[from].flags.water ^ level.rooms[to].flags.water)) + waterCache->setVisible(from, to); + + room.flags.visible = true; + roomsList[roomsCount++] = to; + } + + vec4 clipPort; + for (int i = 0; i < room.portalsCount; i++) { + TR::Room::Portal &p = room.portals[i]; + + if (from == room.portals[i].roomIndex || !checkPortal(room, p, viewPort, clipPort)) + continue; + + getVisibleRooms(roomsList, roomsCount, to, p.roomIndex, clipPort, water, count + 1); + } + } + + virtual void renderView(int roomIndex, bool water) { + PROFILE_MARKER("VIEW"); + Core::Pass pass = Core::pass; + + if (water && waterCache) { + waterCache->reset(); + } + + for (int i = 0; i < level.roomsCount; i++) + level.rooms[i].flags.visible = false; + + int roomsList[128]; + int roomsCount = 0; + + getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water); + + if (water && waterCache) { + for (int i = 0; i < roomsCount; i++) + waterCache->setVisible(roomsList[i]); + waterCache->renderReflect(); + waterCache->simulate(); + } + + // clear visibility flag for rooms + if (Core::pass != Core::passAmbient) + for (int i = 0; i < level.entitiesCount; i++) + level.entities[i].flags.rendered = false; + + if (water) { + Core::setTarget(NULL, true); // render to back buffer + setupBinding(); + } + + camera->setup(Core::pass == Core::passCompose); + + Core::pass = pass; + + renderRooms(roomsList, roomsCount); + if (Core::pass != Core::passAmbient) renderEntities(); + + if (water && waterCache && waterCache->visible) { + waterCache->renderMask(); + waterCache->getRefract(); + waterCache->render(); + } } void setupCubeCamera(const vec3 &pos, int face) { @@ -747,12 +863,13 @@ struct Level : IGame { case 5 : dir = vec3( 0, 0, -1); break; } - Core::mViewInv = mat4(pos, pos + dir, up); - Core::mView = Core::mViewInv.inverse(); - Core::mProj = mat4(90, 1.0f, camera->znear, camera->zfar); + Core::mViewInv = mat4(pos, pos + dir, up); + Core::mView = Core::mViewInv.inverse(); + Core::mProj = mat4(90, 1.0f, camera->znear, camera->zfar); + Core::mViewProj = Core::mProj * Core::mView; } - bool setupLightCamera() { + void setupLightCamera() { vec3 pos = lara->getBoundingBox().center(); Core::mViewInv = mat4(lara->mainLightPos, pos, vec3(0, -1, 0)); @@ -764,8 +881,6 @@ struct Level : IGame { bias.e03 = bias.e13 = bias.e23 = bias.e00 = bias.e11 = bias.e22 = 0.5f; Core::mLightProj = bias * (Core::mProj * Core::mView); - - return true; } void renderShadows(int roomIndex) { @@ -775,16 +890,18 @@ struct Level : IGame { shadow->unbind(sShadow); bool colorShadow = shadow->format == Texture::Format::RGBA ? true : false; if (colorShadow) - Core::setClearColor(vec4(1.0f, 1.0f, 1.0f, 1.0f)); - Core::setTarget(shadow); - if (!setupLightCamera()) return; - Core::clear(true, true); + Core::setClearColor(vec4(1.0f)); + Core::setTarget(shadow, true); + setupLightCamera(); Core::setCulling(cfBack); - renderScene(roomIndex); + + setup(); + renderView(roomIndex, false); + Core::invalidateTarget(!colorShadow, colorShadow); Core::setCulling(cfFront); if (colorShadow) - Core::setClearColor(vec4(0.0f, 0.0f, 0.0f, 0.0f)); + Core::setClearColor(vec4(0.0f)); } #ifdef _DEBUG @@ -869,14 +986,14 @@ struct Level : IGame { glLoadIdentity(); glOrtho(0, Core::width, 0, Core::height, 0, 1); - if (shadow) - shadow->bind(sDiffuse); + if (waterCache->reflect) + waterCache->reflect->bind(sDiffuse); else atlas->bind(sDiffuse); glEnable(GL_TEXTURE_2D); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_BLEND); + Core::setCulling(cfNone); + Core::setDepthTest(false); + Core::setBlending(bmNone); glColor3f(10, 10, 10); int w = Core::active.textures[sDiffuse]->width / 2; @@ -890,9 +1007,6 @@ struct Level : IGame { glColor3f(1, 1, 1); glDisable(GL_TEXTURE_2D); - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -914,7 +1028,7 @@ struct Level : IGame { // Box bbox = lara->getBoundingBox(); // Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1)); - Core::setBlending(bmAlpha); + // Core::setBlending(bmAlpha); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex(), lara); // Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); @@ -928,8 +1042,8 @@ struct Level : IGame { // Debug::Level::path(level, (Enemy*)level.entities[86].controller); // Debug::Level::debugOverlaps(level, lara->box); - Core::setBlending(bmNone); - + // Core::setBlending(bmNone); + Debug::Level::info(level, lara->getEntity(), lara->animation); @@ -945,28 +1059,14 @@ struct Level : IGame { if (ambientCache) ambientCache->precessQueue(); - if (waterCache) - waterCache->reset(); if (shadow) renderShadows(lara->getRoomIndex()); - Core::setClearStencil(128); - Core::setTarget(NULL, true); - - if (waterCache) - waterCache->checkVisibility = true; + if (shadow) shadow->bind(sShadow); + Core::pass = Core::passCompose; - renderCompose(camera->getRoomIndex(), true); - - if (waterCache) { - waterCache->checkVisibility = false; - if (waterCache->visible) { - waterCache->renderMask(); - waterCache->getRefract(); - waterCache->simulate(); - waterCache->render(); - } - } + setup(); + renderView(camera->getRoomIndex(), true); } void renderInventory() { @@ -1013,11 +1113,8 @@ struct Level : IGame { bool copyBg = title && lastTitle != title; if (copyBg) { - vec4 vp = Core::viewportDef; Core::defaultTarget = inventory.background[0]; - Core::setTarget(Core::defaultTarget, true); renderGame(); - Core::viewportDef = vp; Core::defaultTarget = NULL; inventory.prepareBackground(); diff --git a/src/mesh.h b/src/mesh.h index e607baf..14fadc2 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -10,15 +10,6 @@ #define TEX_OXYGEN_BAR_X 1002 #define TEX_OXYGEN_BAR_Y 1000 -typedef unsigned short Index; - -struct Vertex { - short4 coord; // xyz - position, w - unused - short4 texCoord; // xy - texture coordinates, z - anim tex range index, w - anim tex frame index - short4 normal; // xyz - vertex normalá w - unused - ubyte4 color; // xyz - color, w - intensity -}; - struct MeshRange { int iStart; int iCount; @@ -29,14 +20,16 @@ struct MeshRange { void setup() const { glEnableVertexAttribArray(aCoord); - glEnableVertexAttribArray(aTexCoord); glEnableVertexAttribArray(aNormal); + glEnableVertexAttribArray(aTexCoord); + glEnableVertexAttribArray(aParam); glEnableVertexAttribArray(aColor); Vertex *v = (Vertex*)NULL + vStart; glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord); - glVertexAttribPointer(aTexCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->texCoord); glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(Vertex), &v->normal); + glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(Vertex), &v->texCoord); + glVertexAttribPointer(aParam, 4, GL_UNSIGNED_BYTE, false, sizeof(Vertex), &v->param); glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->color); } @@ -114,12 +107,6 @@ struct Mesh { glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]); } - void DIP(const MeshRange &range) { - glDrawElements(GL_TRIANGLES, range.iCount, GL_UNSIGNED_SHORT, (Index*)NULL + range.iStart); - Core::stats.dips++; - Core::stats.tris += range.iCount / 3; - } - void render(const MeshRange &range) { range.bind(VAO); @@ -128,15 +115,7 @@ struct Mesh { range.setup(); }; - 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); + Core::DIP(range.iStart, range.iCount); } }; @@ -189,7 +168,7 @@ struct MeshBuilder { } *models; MeshRange *sequences; // procedured - MeshRange shadowBlob, shadowBox; + MeshRange shadowBlob; MeshRange quad, circle; MeshRange plane; @@ -234,7 +213,7 @@ struct MeshBuilder { if (Core::settings.detail.water) roomRemoveWaterSurfaces(r, iCount, vCount); - + for (int j = 0; j < r.meshesCount; j++) { TR::Room::Mesh &m = r.meshes[j]; TR::StaticMesh *s = &level.staticMeshes[m.meshIndex]; @@ -286,13 +265,6 @@ struct MeshBuilder { iCount += shadowBlob.iCount; vCount += 8 * 2 + 1; - // 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; - // quad (post effect filter) quad.vStart = vCount; quad.iStart = iCount; @@ -402,8 +374,9 @@ struct MeshBuilder { // build shadow blob for (int i = 0; i < 9; i++) { Vertex &v0 = vertices[vCount + i * 2 + 0]; - v0.normal = { 0, -1, 0, 1 }; - v0.texCoord = { 32688, 32688, 0, 0 }; + v0.normal = { 0, -1, 0, 0 }; + v0.texCoord = { 32688, 32688, 32767, 32767 }; + v0.param = { 0, 0, 0, 0 }; v0.color = { 0, 0, 0, 0 }; if (i == 8) { @@ -443,59 +416,6 @@ 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++; - } - // quad addQuad(indices, iCount, vCount, quad.vStart, vertices, &whiteTile); vertices[vCount + 3].coord = { -1, -1, 0, 0 }; @@ -507,7 +427,8 @@ struct MeshBuilder { Vertex &v = vertices[vCount + i]; v.normal = { 0, 0, 0, 0 }; v.color = { 255, 255, 255, 255 }; - v.texCoord = { 32688, 32688, 0, 0 }; + v.texCoord = { 32688, 32688, 32767, 32767 }; + v.param = { 0, 0, 0, 0 }; } vCount += 4; aCount++; @@ -522,7 +443,8 @@ struct MeshBuilder { v.coord = { short(pos.x), short(pos.y), 0, 0 }; v.normal = { 0, 0, 0, 0 }; v.color = { 255, 255, 255, 255 }; - v.texCoord = { 32688, 32688, 0, 0 }; + v.texCoord = { 32688, 32688, 32767, 32767 }; + v.param = { 0, 0, 0, 0 }; indices[iCount++] = i; indices[iCount++] = (i + 1) % CIRCLE_SEGS; @@ -576,7 +498,6 @@ struct MeshBuilder { for (int i = 0; i < level.modelsCount; i++) mesh->initRange(models[i].geometry); mesh->initRange(shadowBlob); - mesh->initRange(shadowBox); mesh->initRange(quad); mesh->initRange(circle); mesh->initRange(plane); @@ -700,8 +621,11 @@ struct MeshBuilder { if (opaque != (t.attribute == 0)) continue; - - addQuad(indices, iCount, vCount, vStart, vertices, &t); + addQuad(indices, iCount, vCount, vStart, vertices, &t, + d.vertices[f.vertices[0]].vertex, + d.vertices[f.vertices[1]].vertex, + d.vertices[f.vertices[2]].vertex, + d.vertices[f.vertices[3]].vertex); TR::Vertex n; CHECK_ROOM_NORMAL(n); @@ -761,7 +685,11 @@ struct MeshBuilder { TR::Color24 c = textured ? COLOR_WHITE : level.getColor(f.texture); - addQuad(indices, iCount, vCount, vStart, vertices, &t); + addQuad(indices, iCount, vCount, vStart, vertices, &t, + mesh.vertices[f.vertices[0]].coord, + mesh.vertices[f.vertices[1]].coord, + mesh.vertices[f.vertices[2]].coord, + mesh.vertices[f.vertices[3]].coord); for (int k = 0; k < 4; k++) { TR::Mesh::Vertex &v = mesh.vertices[f.vertices[k]]; @@ -808,7 +736,7 @@ struct MeshBuilder { int tx = (tile % 4) * 256; int ty = (tile / 4) * 256; return vec2( (float)(((tx + tex.texCoord[0].x) << 5) + 16), - (float)(((ty + tex.texCoord[0].y) << 5) + 16) ); + (float)(((ty + tex.texCoord[0].y) << 5) + 16) ) * (1.0f / 32767.0f); } void initAnimTextures(TR::Level &level) { @@ -846,7 +774,7 @@ struct MeshBuilder { } } - TR::ObjectTexture* getAnimTexture(TR::ObjectTexture *tex, int &range, int &frame) { + TR::ObjectTexture* getAnimTexture(TR::ObjectTexture *tex, uint8 &range, uint8 &frame) { range = frame = 0; if (!level->animTexturesDataSize) return tex; @@ -869,28 +797,41 @@ struct MeshBuilder { } void addTexCoord(Vertex *vertices, int vCount, TR::ObjectTexture *tex, bool triangle) { - int range, frame; + uint8 range, frame; tex = getAnimTexture(tex, range, frame); int tile = tex->tile.index; int tx = (tile % 4) * 256; int ty = (tile / 4) * 256; + int count = triangle ? 3 : 4; for (int i = 0; i < count; i++) { Vertex &v = vertices[vCount + i]; v.texCoord.x = ((tx + tex->texCoord[i].x) << 5) + 16; v.texCoord.y = ((ty + tex->texCoord[i].y) << 5) + 16; - v.texCoord.z = range; - v.texCoord.w = frame; + v.texCoord.z = 32767; + v.texCoord.w = 32767; + v.param = { range, frame, 0, 0 }; } if (level->version == TR::Level::VER_TR1_PSX && !triangle) swap(vertices[vCount + 2].texCoord, vertices[vCount + 3].texCoord); + /* + short2 uv[4] = { + {0, 0}, {32767, 0}, {32767, 32767}, {0, 32767} + }; + + for (int i = 0; i < count; i++) { + Vertex &v = vertices[vCount + i]; + v.texCoord.x = uv[i].x; + v.texCoord.y = uv[i].y; + } + */ } void addTriangle(Index *indices, int &iCount, int vCount, int vStart, Vertex *vertices, TR::ObjectTexture *tex) { - int vIndex = vCount - vStart; + int vIndex = vCount - vStart; indices[iCount + 0] = vIndex + 0; indices[iCount + 1] = vIndex + 1; @@ -902,7 +843,7 @@ struct MeshBuilder { } void addQuad(Index *indices, int &iCount, int vCount, int vStart, Vertex *vertices, TR::ObjectTexture *tex) { - int vIndex = vCount - vStart; + int vIndex = vCount - vStart; indices[iCount + 0] = vIndex + 0; indices[iCount + 1] = vIndex + 1; @@ -917,6 +858,43 @@ struct MeshBuilder { if (tex) addTexCoord(vertices, vCount, tex, false); } + void addQuad(Index *indices, int &iCount, int &vCount, int vStart, Vertex *vertices, TR::ObjectTexture *tex, + const short3 &c0, const short3 &c1, const short3 &c2, const short3 &c3) { + addQuad(indices, iCount, vCount, vStart, vertices, tex); + + vec3 a = c0 - c1; + vec3 b = c3 - c2; + vec3 c = c0 - c3; + vec3 d = c1 - c2; + + float aL = a.length(); + float bL = b.length(); + float cL = c.length(); + float dL = d.length(); + + float ab = a.dot(b) / (aL * bL); + float cd = c.dot(d) / (cL * dL); + + int16 tx = abs(vertices[vCount + 0].texCoord.x - vertices[vCount + 3].texCoord.x); + int16 ty = abs(vertices[vCount + 0].texCoord.y - vertices[vCount + 3].texCoord.y); + + if (ab > cd) { + int k = (tx > ty) ? 3 : 2; + + if (aL > bL) + vertices[vCount + 2].texCoord[k] = vertices[vCount + 3].texCoord[k] = int16(bL / aL * 32767.0f); + else + vertices[vCount + 0].texCoord[k] = vertices[vCount + 1].texCoord[k] = int16(aL / bL * 32767.0f); + } else { + int k = (tx > ty) ? 2 : 3; + + if (cL > dL) { + vertices[vCount + 1].texCoord[k] = vertices[vCount + 2].texCoord[k] = int16(dL / cL * 32767.0f); + } else + vertices[vCount + 0].texCoord[k] = vertices[vCount + 3].texCoord[k] = int16(cL / dL * 32767.0f); + } + } + void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity, bool expand = false) { addQuad(indices, iCount, vCount, vStart, NULL, NULL); @@ -939,12 +917,12 @@ struct MeshBuilder { quad[2].coord = { x1, y1, z, 0 }; quad[3].coord = { x0, y1, z, 0 }; - quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 }; quad[0].color = quad[1].color = quad[2].color = quad[3].color = { 255, 255, 255, intensity }; + quad[0].param = quad[1].param = quad[2].param = quad[3].param = { 0, 0, 0, 0 }; - int tx = (sprite.tile % 4) * 256; - int ty = (sprite.tile / 4) * 256; + int tx = (sprite.tile % 4) * 256; + int ty = (sprite.tile / 4) * 256; int16 u0 = (((tx + sprite.texCoord[0].x) << 5)); int16 v0 = (((ty + sprite.texCoord[0].y) << 5)); @@ -992,7 +970,8 @@ struct MeshBuilder { s = int(s) * 32767 / 1024; t = int(t) * 32767 / 1024; - v.texCoord = { s, t, 0, 0 }; + v.texCoord = { s, t, 32767, 32767 }; + v.param = { 0, 0, 0, 0 }; } vCount += 4; @@ -1018,7 +997,7 @@ struct MeshBuilder { Vertex &v = vertices[vCount + i]; v.normal = { 0, 0, 0, 0 }; v.color = *((ubyte4*)&color1); - v.texCoord = { 32688, 32688, 0, 0 }; + v.texCoord = { 32688, 32688, 32767, 32767 }; } addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; @@ -1038,7 +1017,7 @@ struct MeshBuilder { Vertex &v = vertices[vCount + i]; v.normal = { 0, 0, 0, 0 }; v.color = *((ubyte4*)&color2); - v.texCoord = { 32688, 32688, 0, 0 }; + v.texCoord = { 32688, 32688, 32767, 32767 }; } addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; @@ -1082,10 +1061,6 @@ struct MeshBuilder { mesh->render(shadowBlob); } - void renderShadowBox() { - mesh->render(shadowBox); - } - void renderQuad() { mesh->render(quad); } diff --git a/src/shader.h b/src/shader.h index 1882603..842ea7b 100644 --- a/src/shader.h +++ b/src/shader.h @@ -5,8 +5,9 @@ #define SHADER_ATTRIBS(E) \ E( aCoord ) \ - E( aTexCoord ) \ E( aNormal ) \ + E( aTexCoord ) \ + E( aParam ) \ E( aColor ) #define SHADER_SAMPLERS(E) \ @@ -21,7 +22,6 @@ E( uParam ) \ E( uTexParam ) \ E( uViewProj ) \ - E( uViewInv ) \ E( uBasis ) \ E( uLightProj ) \ E( uMaterial ) \ @@ -32,7 +32,8 @@ E( uAnimTexRanges ) \ E( uAnimTexOffsets ) \ E( uRoomSize ) \ - E( uPosScale ) + E( uPosScale ) \ + E( uContacts ) enum AttribType { SHADER_ATTRIBS(DECL_ENUM) aMAX }; enum SamplerType { SHADER_SAMPLERS(DECL_ENUM) sMAX }; diff --git a/src/shaders/gui.glsl b/src/shaders/gui.glsl index 4764b7c..5988d58 100644 --- a/src/shaders/gui.glsl +++ b/src/shaders/gui.glsl @@ -16,10 +16,8 @@ varying vec4 vColor; attribute vec4 aTexCoord; attribute vec4 aColor; - #define TEXCOORD_SCALE (1.0 / 32767.0) - void main() { - vTexCoord = aTexCoord.xy * TEXCOORD_SCALE; + vTexCoord = aTexCoord.xy; vColor = aColor * uMaterial; gl_Position = uViewProj * vec4(aCoord.xy * uPosScale.zw + uPosScale.xy, 0.0, 1.0); } diff --git a/src/shaders/shader.glsl b/src/shaders/shader.glsl index 4345cf3..db9b99f 100644 --- a/src/shaders/shader.glsl +++ b/src/shaders/shader.glsl @@ -4,73 +4,64 @@ R"====( precision highp float; #endif -varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords +#ifdef OPT_CONTACT + varying vec3 vCoord; +#endif + +varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction + +#if defined(OPT_WATER) && defined(UNDERWATER) + varying vec2 vCausticsCoord; // - xy caustics texture coord +#endif + +//uniform vec4 data[MAX_RANGES + MAX_OFFSETS + 4 + 4 + 1 + 1 + MAX_LIGHTS + MAX_LIGHTS + 1 + 6 + 1 + 32 * 2]; + +uniform vec2 uAnimTexRanges[MAX_RANGES]; +uniform vec2 uAnimTexOffsets[MAX_OFFSETS]; +uniform mat4 uLightProj; +uniform mat4 uViewProj; +uniform vec3 uViewPos; +uniform vec4 uParam; // x - time, y - water height, z - clip plane sign, w - clip plane height +uniform vec3 uLightPos[MAX_LIGHTS]; +uniform vec4 uLightColor[MAX_LIGHTS]; // xyz - color, w - radius * intensity +uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ +uniform vec3 uAmbient[6]; +uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha +uniform vec4 uBasis[32 * 2]; #ifndef PASS_SHADOW varying vec4 vViewVec; // xyz - dir * dist, w - coord.y - uniform vec3 uViewPos; - varying vec4 vDiffuse; #ifndef TYPE_FLASH #ifdef PASS_COMPOSE - varying vec4 vNormal; // xyz - normal dir, w - fog factor + varying vec3 vNormal; // xyz - normal dir varying vec4 vLightProj; - varying vec3 vLightVec; + varying vec4 vLightVec; // xyz - dir, w - fog factor #ifdef OPT_SHADOW varying vec3 vAmbient; #endif - - uniform vec3 uLightPos[4]; - uniform vec4 uLightColor[4]; // xyz - color, w - radius * intensity #endif - varying vec4 vLight; // 4 lights intensity - - #if defined(OPT_WATER) && defined(UNDERWATER) - uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ - #endif + varying vec4 vLight; // lights intensity (MAX_LIGHTS == 4) #if defined(OPT_AMBIENT) && defined(TYPE_ENTITY) - uniform vec3 uAmbient[6]; - vec3 calcAmbient(vec3 n) { vec3 sqr = n * n; vec3 pos = step(0.0, n); - return sqr.x * mix(uAmbient[1], uAmbient[0], pos.x) + - sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) + - sqr.z * mix(uAmbient[5], uAmbient[4], pos.z); + return sqr.x * mix(uAmbient[1], uAmbient[0], pos.x) + + sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) + + sqr.z * mix(uAmbient[5], uAmbient[4], pos.z); } #endif #endif - - uniform vec4 uParam; // x - time, y - water height, z - clip plane sign, w - clip plane height - uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #endif #ifdef VERTEX - uniform mat4 uViewProj; - - uniform mat4 uViewInv; - - #ifndef PASS_AMBIENT - uniform mat4 uLightProj; - #endif - - #ifdef PASS_COMPOSE - uniform vec2 uAnimTexRanges[MAX_RANGES]; - uniform vec2 uAnimTexOffsets[MAX_OFFSETS]; - #endif - - #ifdef TYPE_ENTITY - uniform vec4 uBasis[32 * 2]; - #else - uniform vec4 uBasis[2]; - #endif - attribute vec4 aCoord; attribute vec4 aTexCoord; + attribute vec4 aParam; #ifndef PASS_AMBIENT attribute vec4 aNormal; @@ -80,8 +71,6 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords attribute vec4 aColor; #endif - #define TEXCOORD_SCALE (1.0 / 32767.0) - vec3 mulQuat(vec4 q, vec3 v) { return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + v * q.w); } @@ -100,10 +89,12 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords vec4 rBasisPos = uBasis[1]; #endif - vec4 coord = vec4(mulBasis(rBasisRot, rBasisPos, aCoord.xyz), rBasisPos.w); - + vec4 coord; + coord.w = rBasisPos.w; // visible flag #ifdef TYPE_SPRITE - coord.xyz += uViewInv[0].xyz * aTexCoord.z - uViewInv[1].xyz * aTexCoord.w; + coord.xyz = mulBasis(rBasisRot, rBasisPos + aCoord.xyz, vec3(aTexCoord.z, -aTexCoord.w, 0.0) * 32767.0); + #else + coord.xyz = mulBasis(rBasisRot, rBasisPos, aCoord.xyz); #endif #ifndef PASS_SHADOW @@ -129,9 +120,12 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords fog = length(vViewVec.xyz); #endif - vNormal.w = clamp(1.0 / exp(fog), 0.0, 1.0); + vLightVec.w = clamp(1.0 / exp(fog), 0.0, 1.0); #endif + #ifdef OPT_CONTACT + vCoord = coord.xyz; + #endif return coord; } @@ -161,22 +155,24 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords vec3 lv2 = (uLightPos[2].xyz - coord) * uLightColor[2].w; vec3 lv3 = (uLightPos[3].xyz - coord) * uLightColor[3].w; - vLightVec = lv0; + vLightVec.xyz = lv0; vec4 lum, att; #ifdef TYPE_ENTITY - lum.x = dot(vNormal.xyz, normalize(lv0)); att.x = dot(lv0, lv0); + lum.x = dot(vNormal.xyz, normalize(lv0)); + att.x = dot(lv0, lv0); #else - lum.x = aColor.w; att.x = 0.0; + lum.x = aColor.w; + att.x = 0.0; #ifdef TYPE_SPRITE lum.x *= uMaterial.y; #endif #endif - lum.y = dot(vNormal.xyz, normalize(lv1)); att.y = dot(lv1, lv1); - lum.z = dot(vNormal.xyz, normalize(lv2)); att.z = dot(lv2, lv2); - lum.w = dot(vNormal.xyz, normalize(lv3)); att.w = dot(lv3, lv3); + lum.y = dot(vNormal.xyz, normalize(lv1)); att.y = dot(lv1, lv1); + lum.z = dot(vNormal.xyz, normalize(lv2)); att.z = dot(lv2, lv2); + lum.w = dot(vNormal.xyz, normalize(lv3)); att.w = dot(lv3, lv3); vec4 light = max(vec4(0.0), lum) * max(vec4(0.0), vec4(1.0) - att); #ifdef UNDERWATER @@ -219,20 +215,18 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords } void _uv(vec3 coord) { + vTexCoord = aTexCoord; #if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE) // animated texture coordinates - vec2 range = uAnimTexRanges[int(aTexCoord.z)]; // x - start index, y - count - float frame = fract((aTexCoord.w + uParam.x * 4.0 - range.x) / range.y) * range.y; - vec2 offset = uAnimTexOffsets[int(range.x + frame)]; // texCoord offset from first frame - vTexCoord.xy = (aTexCoord.xy + offset) * TEXCOORD_SCALE; - #else - vTexCoord.xy = aTexCoord.xy * TEXCOORD_SCALE; + vec2 range = uAnimTexRanges[int(aParam.x)]; // x - start index, y - count + float frame = fract((aParam.y + uParam.x * 4.0 - range.x) / range.y) * range.y; + vec2 offset = uAnimTexOffsets[int(range.x + frame)]; // texCoord offset from first frame + vTexCoord.xy += offset; + vTexCoord.xy *= vTexCoord.zw; #endif #if defined(OPT_WATER) && defined(UNDERWATER) - vTexCoord.zw = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0)); - #else - vTexCoord.zw = vec2(1.0); + vCausticsCoord.xy = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0)); #endif } @@ -346,13 +340,13 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords } else rShadow /= 4.0; - float fade = clamp(dot(vLightVec, vLightVec), 0.0, 1.0); + float fade = clamp(dot(vLightVec.xyz, vLightVec.xyz), 0.0, 1.0); return rShadow + (1.0 - rShadow) * fade; } float getShadow() { #ifdef TYPE_ROOM - float vis = min(dot(vNormal.xyz, vLightVec), vLightProj.w); + float vis = min(dot(vNormal.xyz, vLightVec.xyz), vLightProj.w); #else float vis = vLightProj.w; #endif @@ -369,13 +363,29 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #if defined(OPT_WATER) && defined(UNDERWATER) float calcCaustics(vec3 n) { + vec2 cc = vCausticsCoord.xy; vec2 border = vec2(256.0) / (uRoomSize.zw - uRoomSize.xy); - vec2 fade = smoothstep(vec2(0.0), border, vTexCoord.zw) * (1.0 - smoothstep(vec2(1.0) - border, vec2(1.0), vTexCoord.zw)); - return texture2D(sReflect, vTexCoord.zw).g * max(0.0, -n.y) * fade.x * fade.y; + vec2 fade = smoothstep(vec2(0.0), border, cc) * (1.0 - smoothstep(vec2(1.0) - border, vec2(1.0), cc)); + return texture2D(sReflect, cc).g * max(0.0, -n.y) * fade.x * fade.y; } #endif #endif +#ifdef OPT_CONTACT + uniform vec4 uContacts[MAX_CONTACTS]; + + float getContactAO(vec3 p, vec3 n) { + float res = 1.0; + for (int i = 0; i < MAX_CONTACTS; i++) { + vec3 v = uContacts[i].xyz - p; + float a = uContacts[i].w; + float o = a * clamp(dot(n, v), 0.0, 1.0) / dot(v, v); + res *= clamp(1.0 - o, 0.0, 1.0); + } + return res; + } +#endif + void main() { #ifdef PASS_COMPOSE #ifdef CLIP_PLANE @@ -391,7 +401,11 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords color = textureCube(sEnvironment, normalize(rv)); #endif #else - color = texture2D(sDiffuse, vTexCoord.xy); + #if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE) + color = texture2D(sDiffuse, vTexCoord.xy / vTexCoord.zw); + #else + color = texture2D(sDiffuse, vTexCoord.xy); + #endif #endif #ifdef ALPHA_TEST @@ -429,10 +443,16 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #endif #ifdef TYPE_ROOM + light += mix(vAmbient.x, vLight.x, getShadow()); #if defined(OPT_WATER) && defined(UNDERWATER) light += calcCaustics(n); #endif + + #ifdef OPT_CONTACT + light *= getContactAO(vCoord, n) * 0.5 + 0.5; + #endif + #endif #ifdef TYPE_SPRITE @@ -444,7 +464,7 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #endif #ifdef TYPE_ENTITY - color.xyz += calcSpecular(n, vViewVec.xyz, vLightVec, uLightColor[0], uMaterial.z * rShadow + 0.03); + color.xyz += calcSpecular(n, vViewVec.xyz, vLightVec.xyz, uLightColor[0], uMaterial.z * rShadow + 0.03); #endif #endif @@ -460,9 +480,9 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #if defined(PASS_COMPOSE) && !defined(TYPE_FLASH) #ifdef UNDERWATER - color.xyz = mix(UNDERWATER_COLOR * 0.2, color.xyz, vNormal.w); + color.xyz = mix(UNDERWATER_COLOR * 0.2, color.xyz, vLightVec.w); #else - color.xyz = mix(vec3(0.0), color.xyz, vNormal.w); + color.xyz = mix(vec3(0.0), color.xyz, vLightVec.w); #endif #endif diff --git a/src/shaders/water.glsl b/src/shaders/water.glsl index c3db46e..328f8ab 100644 --- a/src/shaders/water.glsl +++ b/src/shaders/water.glsl @@ -1,7 +1,7 @@ R"====( #ifdef GL_ES - precision lowp int; - precision highp float; + precision lowp int; + precision highp float; #endif varying vec3 vCoord; @@ -23,181 +23,212 @@ uniform vec4 uParam; uniform sampler2D sNormal; #ifdef VERTEX - #define ETA_AIR 1.000 - #define ETA_WATER 1.333 + #define ETA_AIR 1.000 + #define ETA_WATER 1.333 - attribute vec4 aCoord; + attribute vec4 aCoord; - void main() { - vTexCoord = (aCoord.xy * 0.5 + 0.5) * uTexParam.zw; + void main() { + vTexCoord = (aCoord.xy * 0.5 + 0.5) * uTexParam.zw; - #if defined(WATER_MASK) || defined(WATER_COMPOSE) + #if defined(WATER_MASK) || defined(WATER_COMPOSE) - float height = 0.0; + float height = 0.0; - #ifdef WATER_COMPOSE - #ifdef WATER_USE_GRID - vTexCoord = (aCoord.xy * (1.0 / 48.0) * 0.5 + 0.5) * uTexParam.zw; - height = texture2D(sNormal, vTexCoord).x; - #endif - #endif + #ifdef WATER_COMPOSE + #ifdef WATER_USE_GRID + vTexCoord = (aCoord.xy * (1.0 / 48.0) * 0.5 + 0.5) * uTexParam.zw; + height = texture2D(sNormal, vTexCoord).x; + #endif + #endif - vCoord = vec3(aCoord.x, height, aCoord.y) * uPosScale[1] + uPosScale[0]; + vCoord = vec3(aCoord.x, height, aCoord.y) * uPosScale[1] + uPosScale[0]; - vec4 cp = uViewProj * vec4(vCoord, 1.0); + vec4 cp = uViewProj * vec4(vCoord, 1.0); - vProjCoord = cp; - gl_Position = cp; - #else - vProjCoord = vec4(0.0); - vCoord = vec3(aCoord.xy, 0.0); - #ifdef WATER_CAUSTICS - vec3 rCoord = vec3(aCoord.x, aCoord.y, 0.0) * uPosScale[1].xzy; + vProjCoord = cp; + gl_Position = cp; + #else + vProjCoord = vec4(0.0); + vCoord = vec3(aCoord.xy, 0.0); + #ifdef WATER_CAUSTICS + vec3 rCoord = vec3(aCoord.x, aCoord.y, 0.0) * uPosScale[1].xzy; - vec4 info = texture2D(sNormal, (rCoord.xy * 0.5 + 0.5) * uTexParam.zw); - vec3 normal = vec3(info.z, info.w, sqrt(1.0 - dot(info.zw, info.zw))); + vec4 info = texture2D(sNormal, (rCoord.xy * 0.5 + 0.5) * uTexParam.zw); + vec3 normal = vec3(info.z, info.w, sqrt(1.0 - dot(info.zw, info.zw))); - vec3 light = vec3(0.0, 0.0, 1.0); - vec3 refOld = refract(-light, vec3(0.0, 0.0, 1.0), 0.75); - vec3 refNew = refract(-light, normal, 0.75); - - vOldPos = vec4(rCoord + refOld * (-1.0 / refOld.z) + refOld * ((-refOld.z - 1.0) / refOld.z), 1.0); - vNewPos = vec4(rCoord + refNew * ((info.r - 1.0) / refNew.z) + refOld * ((-refNew.z - 1.0) / refOld.z), 1.0); - - gl_Position = vec4(vNewPos.xy + refOld.xy / refOld.z, 0.0, 1.0); - #else - vOldPos = vNewPos = vec4(0.0); - gl_Position = vec4(aCoord.xyz, 1.0); - #endif - #endif - vViewVec = uViewPos - vCoord.xyz; - vLightVec = uLightPos - vCoord.xyz; - } + vec3 light = vec3(0.0, 0.0, 1.0); + vec3 refOld = refract(-light, vec3(0.0, 0.0, 1.0), 0.75); + vec3 refNew = refract(-light, normal, 0.75); + + vOldPos = vec4(rCoord + refOld * (-1.0 / refOld.z) + refOld * ((-refOld.z - 1.0) / refOld.z), 1.0); + vNewPos = vec4(rCoord + refNew * ((info.r - 1.0) / refNew.z) + refOld * ((-refNew.z - 1.0) / refOld.z), 1.0); + + gl_Position = vec4(vNewPos.xy + refOld.xy / refOld.z, 0.0, 1.0); + #else + vOldPos = vNewPos = vec4(0.0); + gl_Position = vec4(aCoord.xyz, 1.0); + #endif + #endif + vViewVec = uViewPos - vCoord.xyz; + vLightVec = uLightPos - vCoord.xyz; + } #else - uniform sampler2D sDiffuse; - uniform sampler2D sReflect; - uniform sampler2D sMask; + uniform sampler2D sDiffuse; + uniform sampler2D sReflect; + uniform sampler2D sMask; - uniform vec4 uLightColor; + uniform vec4 uLightColor; - #define PI 3.141592653589793 + #define PI 3.141592653589793 - float calcFresnel(float NdotL, float fbias, float fpow) { - float f = 1.0 - abs(NdotL); - return clamp(fbias + (1.0 - fbias) * pow(f, fpow), 0.0, 1.0); - } + float calcFresnel(float NdotL, float fbias, float fpow) { + float f = 1.0 - abs(NdotL); + return clamp(fbias + (1.0 - fbias) * pow(f, fpow), 0.0, 1.0); + } - vec3 applyFog(vec3 color, vec3 fogColor, float factor) { - float fog = clamp(1.0 / exp(factor), 0.0, 1.0); - return mix(fogColor, color, fog); - } + vec3 applyFog(vec3 color, vec3 fogColor, float factor) { + float fog = clamp(1.0 / exp(factor), 0.0, 1.0); + return mix(fogColor, color, fog); + } - vec4 drop() { - vec2 tc = gl_FragCoord.xy * uTexParam.xy; - vec4 v = texture2D(sDiffuse, tc); + vec4 drop() { + vec2 tc = gl_FragCoord.xy * uTexParam.xy; + vec4 v = texture2D(sDiffuse, tc); - float drop = max(0.0, 1.0 - length(uParam.xy - gl_FragCoord.xy) / uParam.z); - drop = 0.5 - cos(drop * PI) * 0.5; - v.x += drop * uParam.w; + float drop = max(0.0, 1.0 - length(uParam.xy - gl_FragCoord.xy) / uParam.z); + drop = 0.5 - cos(drop * PI) * 0.5; + v.x += drop * uParam.w; - return v; - } + return v; + } - vec4 calc() { - vec2 tc = gl_FragCoord.xy * uTexParam.xy; + vec3 hash33(vec3 p3) { + p3 = fract(p3 * vec3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yxz+19.19); + return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); + } - if (texture2D(sMask, tc).x < 0.5) - return vec4(0.0); + float simplex_noise(vec3 p) { // https://www.shadertoy.com/view/4sc3z2 + const float K1 = 0.333333333; + const float K2 = 0.166666667; - vec4 v = texture2D(sDiffuse, tc); // height, speed, normal.xz + vec3 i = floor(p + (p.x + p.y + p.z) * K1); + vec3 d0 = p - (i - (i.x + i.y + i.z) * K2); - vec3 d = vec3(uTexParam.xy, 0.0); - vec4 f = vec4(texture2D(sDiffuse, tc + d.xz).x, texture2D(sDiffuse, tc + d.zy).x, - texture2D(sDiffuse, tc - d.xz).x, texture2D(sDiffuse, tc - d.zy).x); - float average = dot(f, vec4(0.25)); + vec3 e = step(vec3(0.0), d0 - d0.yzx); + vec3 i1 = e * (1.0 - e.zxy); + vec3 i2 = 1.0 - e.zxy * (1.0 - e); - // normal - v.zw = normalize( vec3(f.x - f.z, 64.0 / (1024.0 * 2.0), f.y - f.w) ).xz; + vec3 d1 = d0 - (i1 - 1.0 * K2); + vec3 d2 = d0 - (i2 - 2.0 * K2); + vec3 d3 = d0 - (1.0 - 3.0 * K2); - // integrate - const float vel = 1.4; - const float vis = 0.995; + vec4 h = max(0.6 - vec4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0); + vec4 n = h * h * h * h * vec4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0))); - v.y += (average - v.x) * vel; - v.y *= vis; - v.x += v.y; + return dot(vec4(31.316), n); + } - return v; - } + float h(vec2 tc) { + return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.001; + } - vec4 caustics() { - float rOldArea = length(dFdx(vOldPos.xyz)) * length(dFdy(vOldPos.xyz)); - float rNewArea = length(dFdx(vNewPos.xyz)) * length(dFdy(vNewPos.xyz)); - float value = clamp(rOldArea / rNewArea * 0.2, 0.0, 1.0) * vOldPos.w; - return vec4(0.0, value, 0.0, 0.0); - } + vec4 calc() { + vec2 tc = gl_FragCoord.xy * uTexParam.xy; - vec4 mask() { - return vec4(0.0); - } + if (texture2D(sMask, tc).x < 0.5) + return vec4(0.0); - vec4 compose() { - vec4 value = texture2D(sNormal, vTexCoord); + vec4 v = texture2D(sDiffuse, tc); // height, speed, normal.xz - vec3 normal = vec3(value.z, -sqrt(1.0 - dot(value.zw, value.zw)), value.w); + vec3 d = vec3(uTexParam.xy, 0.0); + vec4 f = vec4(texture2D(sDiffuse, tc + d.xz).x, texture2D(sDiffuse, tc + d.zy).x, + texture2D(sDiffuse, tc - d.xz).x, texture2D(sDiffuse, tc - d.zy).x); + float average = dot(f, vec4(0.25)); - vec2 dudv = (uViewProj * vec4(normal.x, 0.0, normal.z, 0.0)).xy; + // normal + v.zw = normalize( vec3(f.x - f.z, 64.0 / (1024.0 * 2.0), f.y - f.w) ).xz; - vec3 viewVec = normalize(vViewVec); - vec3 rv = reflect(-viewVec, normal); - vec3 lv = normalize(vLightVec); + // integrate + const float vel = 1.4; + const float vis = 0.995; - float spec = pow(max(0.0, dot(rv, lv)), 64.0) * 0.5; - - vec2 tc = vProjCoord.xy / vProjCoord.w * 0.5 + 0.5; + v.y += (average - v.x) * vel; + v.y *= vis; + v.x += v.y + h(tc); - vec4 refrA = texture2D(sDiffuse, uParam.xy * clamp(tc + dudv * uParam.z, 0.0, 0.999) ); - vec4 refrB = texture2D(sDiffuse, uParam.xy * tc ); - vec4 refr = vec4(mix(refrA.xyz, refrB.xyz, refrA.w), 1.0); - vec4 refl = texture2D(sReflect, vec2(tc.x, 1.0 - tc.y) + dudv * uParam.w); + return v; + } - float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0); + vec4 caustics() { + float rOldArea = length(dFdx(vOldPos.xyz)) * length(dFdy(vOldPos.xyz)); + float rNewArea = length(dFdx(vNewPos.xyz)) * length(dFdy(vNewPos.xyz)); + float value = clamp(rOldArea / rNewArea * 0.2, 0.0, 1.0) * vOldPos.w; + return vec4(0.0, value, 0.0, 0.0); + } - vec4 color = mix(refr, refl, fresnel) + spec * 1.5; + vec4 mask() { + return vec4(0.0); + } - float d = abs((vCoord.y - uViewPos.y) / normalize(vViewVec).y); - d *= step(0.0, uViewPos.y - vCoord.y); // apply fog only when camera is underwater - color.xyz = applyFog(color.xyz, UNDERWATER_COLOR * 0.2, d * WATER_FOG_DIST); + vec4 compose() { + vec4 value = texture2D(sNormal, vTexCoord); - return color; - } - - vec4 pass() { - #ifdef WATER_DROP - return drop(); - #endif + vec3 normal = vec3(value.z, -sqrt(1.0 - dot(value.zw, value.zw)), value.w); - #ifdef WATER_STEP - return calc(); - #endif + vec2 dudv = (uViewProj * vec4(normal.x, 0.0, normal.z, 0.0)).xy; - #ifdef WATER_CAUSTICS - return caustics(); - #endif + vec3 viewVec = normalize(vViewVec); + vec3 rv = reflect(-viewVec, normal); + vec3 lv = normalize(vLightVec); - #ifdef WATER_MASK - return mask(); - #endif + float spec = pow(max(0.0, dot(rv, lv)), 64.0) * 0.5; - #ifdef WATER_COMPOSE - return compose(); - #endif + vec2 tc = vProjCoord.xy / vProjCoord.w * 0.5 + 0.5; - return vec4(1.0, 0.0, 1.0, 1.0); - } - - void main() { - gl_FragColor = pass(); - } + vec4 refrA = texture2D(sDiffuse, uParam.xy * clamp(tc + dudv * uParam.z, 0.0, 0.999) ); + vec4 refrB = texture2D(sDiffuse, uParam.xy * tc ); + vec4 refr = vec4(mix(refrA.xyz, refrB.xyz, refrA.w), 1.0); + vec4 refl = texture2D(sReflect, vec2(tc.x, 1.0 - tc.y) + dudv * uParam.w); + + float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0); + + vec4 color = mix(refr, refl, fresnel) + spec * 1.5; + + float d = abs((vCoord.y - uViewPos.y) / normalize(vViewVec).y); + d *= step(0.0, uViewPos.y - vCoord.y); // apply fog only when camera is underwater + color.xyz = applyFog(color.xyz, UNDERWATER_COLOR * 0.2, d * WATER_FOG_DIST); + + return color; + } + + vec4 pass() { + #ifdef WATER_DROP + return drop(); + #endif + + #ifdef WATER_STEP + return calc(); + #endif + + #ifdef WATER_CAUSTICS + return caustics(); + #endif + + #ifdef WATER_MASK + return mask(); + #endif + + #ifdef WATER_COMPOSE + return compose(); + #endif + + return vec4(1.0, 0.0, 1.0, 1.0); + } + + void main() { + gl_FragColor = pass(); + } #endif )====" \ No newline at end of file diff --git a/src/sprite.h b/src/sprite.h index 5d8f2a0..3bdf41e 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -62,9 +62,7 @@ struct Sprite : Controller { } virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { - Basis basis(Core::basis); - basis.translate(pos); - Core::active.shader->setParam(uBasis, basis); + Core::active.shader->setParam(uBasis, Basis(Core::mViewInv.getRot(), pos)); mesh->renderSprite(-(getEntity().modelIndex + 1), frame); } }; diff --git a/src/texture.h b/src/texture.h index 3de96df..0183f05 100644 --- a/src/texture.h +++ b/src/texture.h @@ -11,7 +11,7 @@ struct Texture { Format format; bool cube; - Texture(int width, int height, Format format, bool cube, void *data = NULL, bool filter = true) : cube(cube) { + Texture(int width, int height, Format format, bool cube, void *data = NULL, bool filter = true, bool mips = false) : cube(cube) { if (!Core::support.texNPOT) { width = nextPow2(width); height = nextPow2(height); @@ -65,8 +65,9 @@ struct Texture { float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); } + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter ? (mips ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) : ( mips ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST )); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter ? GL_LINEAR : GL_NEAREST); struct FormatDesc { GLuint ifmt, fmt; diff --git a/src/ui.h b/src/ui.h index 3f6a1f7..5b78fcb 100644 --- a/src/ui.h +++ b/src/ui.h @@ -205,7 +205,7 @@ namespace UI { " SHIFT - Walk@" " SPACE - Draw Weapon@" " CTRL - Action@" - " ALT - Jump@" + " D - Jump@" " Z - Step Left@" " X - Step Right@" " A - Roll@" diff --git a/src/utils.h b/src/utils.h index d55b9a7..a8ce1cd 100644 --- a/src/utils.h +++ b/src/utils.h @@ -140,9 +140,9 @@ int nextPow2(uint32 x) { } uint32 fnv32(const char *data, int32 size, uint32 hash = 0x811c9dc5) { - for (int i = 0; i < size; i++) - hash = (hash ^ data[i]) * 0x01000193; - return hash; + for (int i = 0; i < size; i++) + hash = (hash ^ data[i]) * 0x01000193; + return hash; } struct vec2 { @@ -271,6 +271,9 @@ struct vec4 { vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {} vec4(const vec2 &xy, const vec2 &zw) : x(xy.x), y(xy.y), z(zw.x), w(zw.y) {} + inline bool operator == (const vec4 &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } + inline bool operator != (const vec4 &v) const { return !(*this == v); } + vec4 operator + (const vec4 &v) const { return vec4(x + v.x, y + v.y, z + v.z, w + v.w); } vec4 operator - (const vec4 &v) const { return vec4(x - v.x, y - v.y, z - v.z, w - v.w); } vec4 operator * (const vec4 &v) const { return vec4(x*v.x, y*v.y, z*v.z, w*v.w); } @@ -599,7 +602,7 @@ struct mat4 { quat getRot() const { float t, s; t = 1.0f + e00 + e11 + e22; - if (t > EPS) { + if (t > 0.0001f) { s = 0.5f / sqrtf(t); return quat((e21 - e12) * s, (e02 - e20) * s, (e10 - e01) * s, 0.25f / s); } else @@ -654,7 +657,7 @@ struct mat4 { }; struct Basis { - quat rot; + quat rot; vec3 pos; float w; @@ -711,12 +714,23 @@ struct short2 { struct short3 { int16 x, y, z; + + short3() {} + short3(int16 x, int16 y, int16 z) : x(x), y(y), z(z) {} + + operator vec3() const { return vec3((float)x, (float)y, (float)z); }; + + short3 operator + (const short3 &v) const { return short3(x + v.x, y + v.y, z + v.z); } + short3 operator - (const short3 &v) const { return short3(x - v.x, y - v.y, z - v.z); } }; struct short4 { int16 x, y, z, w; - operator vec3() const { return vec3((float)x, (float)y, (float)z); }; + operator vec3() const { return vec3((float)x, (float)y, (float)z); }; + operator short3() const { return *((short3*)this); } + + inline int16& operator [] (int index) const { ASSERT(index >= 0 && index <= 3); return ((int16*)this)[index]; } }; quat rotYXZ(const vec3 &a) {