1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-12 16:14:25 +02:00

Merge remote-tracking branch 'refs/remotes/XProger/master'

This commit is contained in:
Gh0stBlade
2017-09-15 09:51:34 +01:00
21 changed files with 955 additions and 866 deletions

Binary file not shown.

View File

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

View File

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

View File

@@ -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 <GL/gl.h>
@@ -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 <Carbon/Carbon.h>
#include <AudioToolbox/AudioQueue.h>
@@ -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<61> 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"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
)===="

View File

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

View File

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

View File

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

View File

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