diff --git a/src/camera.h b/src/camera.h index c126702..87f00ca 100644 --- a/src/camera.h +++ b/src/camera.h @@ -26,7 +26,7 @@ struct Camera : Controller { Camera(TR::Level *level, Lara *owner) : Controller(level, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL) { fov = 65.0f; znear = 16; - zfar = 32.0f * 1024.0f; + zfar = 40.0f * 1024.0f; angleAdv = vec3(0.0f); if (owner) { @@ -207,13 +207,15 @@ struct Camera : Controller { virtual void setup(bool calcMatrices) { if (calcMatrices) { - if (reflectPlane) + if (reflectPlane) { Core::mViewInv = mat4(*reflectPlane) * mViewInv; - else + Core::mViewInv.scale(vec3(1.0f, -1.0f, 1.0f)); + } else Core::mViewInv = mViewInv; Core::mView = Core::mViewInv.inverse(); Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar); + // TODO: camera shake // TODO: temporal anti-aliasing // Core::mProj.e02 = (randf() - 0.5f) * 32.0f / Core::width; diff --git a/src/core.h b/src/core.h index 4afd3a3..56fac80 100644 --- a/src/core.h +++ b/src/core.h @@ -150,6 +150,8 @@ namespace Core { vec4 lightColor[MAX_LIGHTS]; vec4 color; + Texture *blackTex, *whiteTex; + enum Pass { passCompose, passShadow, passAmbient, passFilter, passWater } pass; GLuint FBO; @@ -281,9 +283,16 @@ namespace Core { lightColor[i] = vec4(0, 0, 0, 1); frameIndex = 0; + + uint32 data = 0xFF000000; + blackTex = new Texture(1, 1, Texture::RGBA, false, &data); + data = 0xFFFFFFFF; + whiteTex = new Texture(1, 1, Texture::RGBA, false, &data); } void free() { + delete blackTex; + delete whiteTex; // glDeleteRenderBuffers(MAX_RENDER_BUFFERS * 2, &renderBuffers[0][0]); // glDeleteFrameBuffers(1, &FBO); Sound::free(); @@ -337,6 +346,21 @@ namespace Core { glEnable(GL_BLEND); } + void setColorWrite(bool r, bool g, bool b, bool a) { + glColorMask(r, g, b, a); + } + + void setDepthWrite(bool write) { + glDepthMask(write); + } + + void setDepthTest(bool test) { + if (test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + } + void setTarget(Texture *target, int face = 0) { if (!target) { glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -366,6 +390,11 @@ namespace Core { setViewport(0, 0, target->width, target->height); } + void copyTarget(Texture *texture, int xOffset, int yOffset, int x, int y, int width, int height) { + texture->bind(sDiffuse); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); + } + void resetStates() { memset(&active, 0, sizeof(active)); glEnable(GL_DEPTH_TEST); diff --git a/src/debug.h b/src/debug.h index fe87eaf..074a7c3 100644 --- a/src/debug.h +++ b/src/debug.h @@ -627,7 +627,7 @@ namespace Debug { case_name(TR::Entity, KEY_4 ); case_name(TR::Entity, HOLE_KEY ); case_name(TR::Entity, VIEW_TARGET ); - case_name(TR::Entity, SOURCE_WATER ); + case_name(TR::Entity, WATERFALL ); } return "UNKNOWN"; } diff --git a/src/filter.glsl b/src/filter.glsl index ec40d3e..31acd3d 100644 --- a/src/filter.glsl +++ b/src/filter.glsl @@ -16,7 +16,6 @@ uniform int uType; void main() { vTexCoord = aCoord.zw; gl_Position = vec4(aCoord.xy, 0.0, 1.0); - } #else uniform sampler2D sDiffuse; diff --git a/src/format.h b/src/format.h index eececfe..430261b 100644 --- a/src/format.h +++ b/src/format.h @@ -3,7 +3,7 @@ #include "utils.h" -#define MAX_RESERVED_ENTITIES 64 +#define MAX_RESERVED_ENTITIES 128 #define MAX_SECRETS_COUNT 16 #define MAX_TRIGGER_COMMANDS 32 #define MAX_MESHES 512 @@ -12,6 +12,7 @@ namespace TR { enum { FLOOR_BLOCK = -127, + NO_ROOM = 0xFF, }; enum { @@ -256,6 +257,18 @@ namespace TR { uint16 roomIndex; Vertex normal; Vertex vertices[4]; + + vec3 getCenter() const { + return vec3(float( (int(vertices[0].x) + int(vertices[1].x) + int(vertices[2].x) + int(vertices[3].x)) / 4 ), + float( (int(vertices[0].y) + int(vertices[1].y) + int(vertices[2].y) + int(vertices[3].y)) / 4 ), + float( (int(vertices[0].z) + int(vertices[1].z) + int(vertices[2].z) + int(vertices[3].z)) / 4 )); + } + + vec3 getSize() const { + return vec3(float( abs(int(vertices[0].x) - int(vertices[2].x)) / 2 ), + float( abs(int(vertices[0].y) - int(vertices[2].y)) / 2 ), + float( abs(int(vertices[0].z) - int(vertices[2].z)) / 2 )); + } } *portals; struct Sector { @@ -461,8 +474,8 @@ namespace TR { MUZZLE_FLASH = 166, - VIEW_TARGET = 169, - SOURCE_WATER = 170, + VIEW_TARGET = 169, // invisible + WATERFALL = 170, // invisible (water splash generator) GLYPH = 190, // sprite @@ -897,14 +910,14 @@ namespace TR { stream.read(d.vertices, stream.read(d.vCount)); if (version == VER_TR1_PSX) - for (int i = 0; i < d.vCount; i++) // convert vertex luminance from PSX to PC format - d.vertices[i].lighting = 0x1FFF - (d.vertices[i].lighting << 5); + for (int j = 0; j < d.vCount; j++) // convert vertex luminance from PSX to PC format + d.vertices[j].lighting = 0x1FFF - (d.vertices[j].lighting << 5); stream.read(d.rectangles, stream.read(d.rCount)); if (version == VER_TR1_PSX) - for (int i = 0; i < d.rCount; i++) // swap indices (quad strip -> quad list) - swap(d.rectangles[i].vertices[2], d.rectangles[i].vertices[3]); + for (int j = 0; j < d.rCount; j++) // swap indices (quad strip -> quad list) + swap(d.rectangles[j].vertices[2], d.rectangles[j].vertices[3]); stream.read(d.triangles, stream.read(d.tCount)); stream.read(d.sprites, stream.read(d.sCount)); diff --git a/src/lara.h b/src/lara.h index a60743f..6440df0 100644 --- a/src/lara.h +++ b/src/lara.h @@ -229,28 +229,28 @@ struct Lara : Character { arms[i].rot = quat(0, 0, 0, 1); arms[i].rotAbs = quat(0, 0, 0, 1); } - + /* pos = vec3(40448, 3584, 60928); angle = vec3(0.0f, PI * 0.5f, 0.0f); getEntity().room = 14; stand = STAND_ONWATER; animation.setAnim(ANIM_TO_ONWATER); updateEntity(); - + */ #ifdef _DEBUG /* // gym pos = vec3(43182, 2473, 51556); angle = vec3(0.0f, PI * 0.5f, 0.0f); getEntity().room = 12; - */ + // gym (pool) pos = vec3(40448, 3584, 60928); angle = vec3(0.0f, PI * 0.5f, 0.0f); getEntity().room = 14; stand = STAND_ONWATER; - animation.setAnim(ANIM_TO_ONWATER); - /* + animation.setAnim(ANIM_TO_ONWATER); + // level 2 (pool) pos = vec3(70067, -256, 29104); angle = vec3(0.0f, -0.68f, 0.0f); @@ -936,7 +936,7 @@ struct Lara : Character { int h = int(pos.y - infoDst.floor); - if (h > 0 && h <= 256 && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) { // possibility check + if (h >= 0 && h <= 356 && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) { // possibility check alignToWall(-96.0f); pos.y = infoDst.floor; //pos = dst; // set new position @@ -1394,7 +1394,7 @@ struct Lara : Character { return STATE_PICK_UP; if (state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH) { - Sprite::add(level, TR::Entity::Type::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z); + Sprite::add(level, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z); return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation } diff --git a/src/level.h b/src/level.h index b93c283..dc9857b 100644 --- a/src/level.h +++ b/src/level.h @@ -42,13 +42,6 @@ struct Level { Camera *camera; Texture *shadow; - Texture *water[2]; - - Texture *waterRefract; - Texture *waterReflect; - Texture *waterCaustics; - float waterTimer; - float time; float clipHeight; float clipSign; @@ -186,6 +179,348 @@ struct Level { } } *ambientCache; + struct WaterCache { + #define MAX_SURFACES 5 + #define MAX_INVISIBLE_TIME 5.0f + #define SIMULATE_TIMESTEP (1.0f / 40.0f) + #define DETAIL (64.0f / 1024.0f) + #define MAX_DROPS 32 + + Level *level; + Texture *refract; + Texture *reflect; + + struct Item { + int from, to; + float timer; + bool visible; + bool blank; + vec3 pos, size; + Texture *data[2]; + Texture *caustics; + + Item() { + data[0] = data[1] = caustics = NULL; + } + + Item(int from, int to, const vec3 &pos, const vec3 &size) : from(from), to(to), timer(SIMULATE_TIMESTEP), visible(true), blank(true), pos(pos), size(size) { + data[0] = data[1] = caustics = NULL; + } + + void init() { + data[0] = new Texture(nextPow2(int(size.x * DETAIL * 2.0f) - 1), nextPow2(int(size.z * DETAIL * 2.0f) - 1), Texture::RGBA_HALF, false); + data[1] = new Texture(data[0]->width, data[0]->height, Texture::RGBA_HALF, false); + caustics = new Texture(256, 256, Texture::RGBA, false); + blank = false; + } + + void free() { + delete data[0]; + delete data[1]; + delete caustics; + data[0] = data[1] = caustics = NULL; + } + + } items[MAX_SURFACES]; + int count, visible; + bool checkVisibility; + + int dropCount; + struct Drop { + vec3 pos; + float radius; + float strength; + Drop() {} + Drop(const vec3 &pos, float radius, float strength) : pos(pos), radius(radius), strength(strength) {} + } drops[MAX_DROPS]; + + WaterCache(Level *level) : level(level), refract(NULL), count(0), checkVisibility(false), dropCount(0) { + reflect = new Texture(512, 512, Texture::RGBA, false); + } + + ~WaterCache() { + delete refract; + delete reflect; + for (int i = 0; i < count; i++) + items[i].free(); + } + + void update() { + int i = 0; + while (i < count) { + Item &item = items[i]; + if (item.timer > MAX_INVISIBLE_TIME) { + items[i].free(); + items[i] = items[--count]; + continue; + } + item.timer += Core::deltaTime; + i++; + } + } + + void reset() { + for (int i = 0; i < count; i++) + items[i].visible = false; + visible = 0; + } + + void setVisible(int roomIndex, int nextRoom) { + if (!checkVisibility) return; + + int from, to; // from surface room to underwater room + if (level->level.rooms[roomIndex].flags.water) { + from = nextRoom; + to = roomIndex; + } else { + from = roomIndex; + to = nextRoom; + } + + for (int i = 0; i < count; i++) { + Item &item = items[i]; + if (item.from == from && item.to == to) { + if (!item.visible) { + visible++; + item.visible = true; + } + return; + } + } + if (count == MAX_SURFACES) return; + + + TR::Room &r = level->level.rooms[to]; // underwater room + + int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0; + + for (int z = 0; z < r.zSectors; z++) + for (int x = 0; x < r.xSectors; x++) { + int above = r.sectors[x * r.zSectors + z].roomAbove; + if (above != TR::NO_ROOM && !level->level.rooms[above].flags.water) { + minX = min(minX, x); + minZ = min(minZ, z); + maxX = max(maxX, x); + maxZ = max(maxZ, z); + } + } + maxX++; + maxZ++; + + vec3 size(float((maxX - minX) * 512), 0.0f, float((maxZ - minZ) * 512)); // half size + vec3 pos(r.info.x + minX * 1024 + size.x, r.info.yTop, r.info.z + minZ * 1024 + size.z); + + items[count++] = Item(from, to, pos, size); + visible++; + } + + void bindCaustics(int roomIndex) { + Item *item = NULL; + for (int i = 0; i < count; i++) + if (items[i].to == roomIndex) { + item = &items[i]; + break; + } + + if (!item || !item->caustics) { + Core::blackTex->bind(sReflect); + Core::active.shader->setParam(uRoomSize, vec4(0.0f)); + } else { + item->caustics->bind(sReflect); + Core::active.shader->setParam(uRoomSize, vec4(item->pos.x - item->size.x, item->pos.z - item->size.z, item->pos.x + item->size.x, item->pos.z + item->size.z)); + } + } + + void simulate(Item &item) { + if (item.timer < SIMULATE_TIMESTEP) return; + + Core::active.shader->setParam(uParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 0.0f, 0.0f)); + Core::active.shader->setParam(uType, Shader::WATER_STEP); + + while (item.timer >= SIMULATE_TIMESTEP) { + // water step + item.data[0]->bind(sDiffuse); + Core::setTarget(item.data[1]); + level->mesh->renderQuad(); + swap(item.data[0], item.data[1]); + item.timer -= SIMULATE_TIMESTEP; + } + + // calc caustics + vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f / PLANE_DETAIL) }; + Core::active.shader->setParam(uPosScale, rPosScale[0], 2); + item.data[0]->bind(sNormal); + item.caustics->unbind(sReflect); + Core::setTarget(item.caustics); + Core::active.shader->setParam(uType, Shader::WATER_CAUSTICS); + level->mesh->renderPlane(); + } + + void addDrop(const vec3 &pos, float radius, float strength) { + if (dropCount >= MAX_DROPS) return; + drops[dropCount++] = Drop(pos, radius, strength); + } + + void drop(Item &item) { + static vec3 lastPos = vec3(0.0); + Lara *lara = level->lara; + vec3 head = lara->animation.getJoints(lara->getMatrix(), 14).pos; + bool flag = (head - lastPos).length() > 16.0f; + bool fall = (lara->animation.index == Lara::ANIM_WATER_FALL || lara->animation.index == 152) && lara->animation.frameIndex == 0; + bool flag2 = lara->animation.frameIndex == 20 || fall; + flag &= lara->stand == Lara::STAND_ONWATER || fall; + flag2 &= lara->stand == Lara::STAND_ONWATER || fall; + + if (Input::down[ikU] || flag || flag2) { + vec3 p(randf(), 0.0f, randf()); + if (flag || flag2) { + p = head; + lastPos = head; + } + float radius = (flag2 ? 1.0f : randf() + 0.2f); + float strength = flag2 ? 0.01f : randf() * 0.03f; + + if (fall) { + radius = 1.5f; + strength = 0.05f; + } + + addDrop(p, radius, strength); + + Input::down[ikU] = false; + } + + if (!dropCount) return; + + item.data[0]->bind(sDiffuse); + Core::setTarget(item.data[1]); + vec3 rPosScale[2] = { vec3(0.0f), vec3(1.0f) }; + Core::active.shader->setParam(uPosScale, rPosScale[0], 2); + Core::active.shader->setParam(uType, Shader::WATER_DROP); + + for (int i = 0; i < dropCount; i++) { + Drop &drop = drops[i]; + + vec3 p; + p.x = (drop.pos.x - item.pos.x) / (item.size.x * 2.0f) + 0.5; + p.z = (drop.pos.z - item.pos.z) / (item.size.z * 2.0f) + 0.5; + Core::active.shader->setParam(uParam, vec4(p.x, p.z, 128.0f / (item.size.x * 2.0f) * drop.radius, drop.strength)); + level->mesh->renderQuad(); + } + dropCount = 0; + + swap(item.data[0], item.data[1]); + } + + void render() { + if (!visible) return; + + // mask underwater geometry by zero alpha + level->setPassShader(Core::passWater); + level->atlas->bind(sNormal); + level->atlas->bind(sReflect); + + Core::active.shader->setParam(uType, Shader::WATER_MASK); + Core::setBlending(bmNone); + Core::setCulling(cfNone); + Core::setDepthWrite(false); + Core::setColorWrite(false, false, false, true); + + for (int i = 0; i < count; i++) { + Item &item = items[i]; + if (!item.visible) continue; + + Core::active.shader->setParam(uPosScale, item.pos, 2); + + level->mesh->renderQuad(); + } + + Core::setColorWrite(true, true, true, true); + Core::setDepthWrite(true); + Core::setCulling(cfFront); + + // get refraction texture + if (!refract || Core::width > refract->width || Core::height > refract->height) { + delete refract; + refract = new Texture(nextPow2(Core::width), nextPow2(Core::height), Texture::RGBA, false); + } + Core::copyTarget(refract, 0, 0, 0, 0, Core::width, Core::height); // copy framebuffer into refraction texture + + for (int i = 0; i < count; i++) { + Item &item = items[i]; + if (!item.visible) continue; + + if (item.blank) { + item.init(); + item.blank = false; + } + + // render mirror reflection + Core::setTarget(reflect); + 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->level.rooms[level->camera->getRoomIndex()].flags.water; + + //bool underwater = level->camera->pos.y > item.pos.y; + + level->camera->reflectPlane = &reflectPlane; + level->clipSign = underwater ? -1.0f : 1.0f; + level->clipHeight = item.pos.y * level->clipSign; + level->renderCompose(underwater ? item.from : item.to); + level->clipHeight = 1000000.0f; + level->clipSign = 1.0f; + + level->camera->reflectPlane = NULL; + level->camera->setup(true); + + // simulate water + Core::setBlending(bmNone); + Core::setDepthTest(false); + + level->setPassShader(Core::passWater); + drop(item); + simulate(item); + Core::setTarget(NULL); + + Core::setBlending(bmAlpha); + Core::setDepthTest(true); + + // render water plane + int dx, dz; + TR::Room::Sector &s = level->level.getSector(item.from, int(item.pos.x), int(item.pos.z), dx, dz); + if (s.roomAbove != TR::NO_ROOM && level->level.rooms[s.roomAbove].lightsCount) { + TR::Room::Light &light = level->level.rooms[s.roomAbove].lights[0]; + Core::lightPos[0] = vec3(float(light.x), float(light.y), float(light.z)); + float lum = intensityf(light.intensity); + Core::lightColor[0] = vec4(lum, lum, lum, float(light.radius) * float(light.radius)); + } + + Core::active.shader->setParam(uType, Shader::WATER_COMPOSE); + Core::active.shader->setParam(uViewProj, Core::mViewProj); + Core::active.shader->setParam(uPosScale, item.pos, 2); + Core::active.shader->setParam(uViewPos, Core::viewPos); + 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.075f, 0.02f)); + + refract->bind(sDiffuse); + reflect->bind(sReflect); + item.data[0]->bind(sNormal); + Core::setCulling(cfNone); + level->mesh->renderQuad(); + Core::setCulling(cfFront); + } + } + + #undef MAX_WATER_SURFACES + #undef MAX_WATER_INVISIBLE_TIME + #undef WATER_SIMULATE_TIMESTEP + #undef DETAIL + } *waterCache; + /* struct LightCache { @@ -319,6 +654,9 @@ struct Level { case TR::Entity::HOLE_KEY : entity.controller = new Trigger(&level, i, false); break; + case TR::Entity::WATERFALL : + entity.controller = new Waterfall(&level, i); + break; default : if (entity.modelIndex > 0) entity.controller = new Controller(&level, i); @@ -332,9 +670,10 @@ struct Level { level.cameraController = camera; - initReflections(); - ambientCache = new AmbientCache(this); + waterCache = new WaterCache(this); + + initReflections(); } ~Level() { @@ -349,13 +688,7 @@ struct Level { delete shadow; delete ambientCache; - - delete water[0]; - delete water[1]; - - delete waterRefract; - delete waterReflect; - delete waterCaustics; + delete waterCache; delete atlas; delete cube; @@ -364,24 +697,9 @@ struct Level { delete camera; } - #define WATER_SIMULATE_TIMESTEP (1.0f / 40.0f) - void initTextures() { shadow = new Texture(1024, 1024, Texture::SHADOW, false); - water[0] = new Texture(256, 256, Texture::RGBA_HALF, false); - water[1] = new Texture(256, 256, Texture::RGBA_HALF, false); - - waterRefract = NULL; - waterReflect = new Texture( 512, 512, Texture::RGBA, false); - waterCaustics = new Texture( 256, 256, Texture::RGBA, false); - - waterTimer = WATER_SIMULATE_TIMESTEP; - - Core::setTarget(waterRefract); // clear refract mask (frame borders) - Core::clear(vec4(0.0f)); - Core::setTarget(NULL); - if (!level.tilesCount) { atlas = NULL; return; @@ -423,12 +741,6 @@ struct Level { level.tiles = NULL; } - void checkRefractTexture() { - if (waterRefract && Core::width <= waterRefract->width && Core::height <= waterRefract->height) return; - delete waterRefract; - waterRefract = new Texture(nextPow2(Core::width), nextPow2(Core::height), Texture::RGBA, false); - } - void initShaders() { char def[255], ext[255]; @@ -567,25 +879,24 @@ struct Level { } #endif - void setRoomParams(const TR::Room &room, float intensity) { + void setRoomParams(int roomIndex, float intensity) { if (Core::pass == Core::passShadow) return; + TR::Room &room = level.rooms[roomIndex]; + if (room.flags.water) { Core::color = vec4(0.6f, 0.9f, 0.9f, intensity); Core::active.shader->setParam(uCaustics, 1); + waterCache->bindCaustics(roomIndex); } else { Core::color = vec4(1.0f, 1.0f, 1.0f, intensity); Core::active.shader->setParam(uCaustics, 0); } Core::active.shader->setParam(uColor, Core::color); - - waterCaustics->bind(sReflect); - vec4 roomSize = vec4(room.info.x, room.info.z, room.info.x + room.xSectors * 1024, room.info.z + room.zSectors * 1024); - Core::active.shader->setParam(uRoomSize, roomSize); } - void renderRoom(int roomIndex, int from = -1) { + void renderRoom(int roomIndex, int from = TR::NO_ROOM) { ASSERT(roomIndex >= 0 && roomIndex < level.roomsCount); PROFILE_MARKER("ROOM"); @@ -597,7 +908,7 @@ struct Level { room.flags.rendered = true; if (Core::pass != Core::passShadow) { - setRoomParams(room, intensityf(room.ambient)); + setRoomParams(roomIndex, intensityf(room.ambient)); Basis qTemp = Core::basis; Core::basis.translate(offset); @@ -643,8 +954,11 @@ struct Level { }; frustum = *camFrustum; - if (frustum.clipByPortal(v, 4, p.normal)) + if (frustum.clipByPortal(v, 4, p.normal)) { + if ((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 } @@ -708,7 +1022,7 @@ struct Level { return; int16 lum = entity.intensity == -1 ? room.ambient : entity.intensity; - setRoomParams(room, isModel ? controller->specular : intensityf(lum)); + setRoomParams(entity.room, isModel ? controller->specular : intensityf(lum)); if (isModel) { // model vec3 pos = controller->getPos(); @@ -735,16 +1049,24 @@ struct Level { void update() { time += Core::deltaTime; - waterTimer += Core::deltaTime; - for (int i = 0; i < level.entitiesCount; i++) - if (level.entities[i].type != TR::Entity::NONE) { - Controller *controller = (Controller*)level.entities[i].controller; - if (controller) + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + if (e.type != TR::Entity::NONE) { + Controller *controller = (Controller*)e.controller; + if (controller) { controller->update(); + + if (e.type == TR::Entity::WATERFALL && ((Waterfall*)controller)->drop) { // add water drops for waterfalls + Waterfall *w = (Waterfall*)controller; + waterCache->addDrop(w->dropPos, w->dropRadius, w->dropStrength); + } + } } + } camera->update(); + waterCache->update(); } void setup() { @@ -906,165 +1228,22 @@ struct Level { renderScene(roomIndex); } - void waterSimulate() { - if (waterTimer < WATER_SIMULATE_TIMESTEP) return; - - Core::active.shader->setParam(uParam, vec4(1.0f / water[0]->width, 1.0f / water[0]->height, 0.0f, 0.0f)); - Core::active.shader->setParam(uType, Shader::WATER_STEP); - - while (waterTimer >= WATER_SIMULATE_TIMESTEP) { - // water step - water[0]->bind(sDiffuse); - Core::setTarget(water[1]); - mesh->renderQuad(); - swap(water[0], water[1]); - waterTimer -= WATER_SIMULATE_TIMESTEP; - } - - // calc normals - //water[0]->bind(sDiffuse); - //Core::setTarget(water[1]); - //Core::active.shader->setParam(uType, Shader::WATER_NORMAL); - //mesh->renderQuad(); - //swap(water[0], water[1]); - - // calc caustics - Core::active.shader->setParam(uScale, 1.0f / PLANE_DETAIL); - water[0]->bind(sNormal); - waterCaustics->unbind(sReflect); - Core::setTarget(waterCaustics); - Core::active.shader->setParam(uType, Shader::WATER_CAUSTICS); - mesh->renderPlane(); - Core::active.shader->setParam(uScale, 1.0f); - } - - void waterDrop() { - static vec3 lastPos = vec3(0.0); - vec3 head = lara->animation.getJoints(lara->getMatrix(), 14).pos; - bool flag = (head - lastPos).length() > 16.0f; - bool fall = (lara->animation.index == Lara::ANIM_WATER_FALL || lara->animation.index == 152) && lara->animation.frameIndex == 0; - bool flag2 = lara->animation.frameIndex == 20 || fall; - flag &= lara->stand == Lara::STAND_ONWATER || fall; - flag2 &= lara->stand == Lara::STAND_ONWATER || fall; - - if (Input::down[ikU] || flag || flag2) { - vec2 p(randf(), randf()); - if (flag || flag2) { - vec3 c(40448, 0.0, 60928); - p.x = (head.x - c.x) / 5120.0f + 0.5; - p.y = (head.z - c.z) / 5120.0f + 0.5; - lastPos = head; - } - float radius = (flag2 ? 1.0f : randf() + 0.2f); - float strength = flag2 ? 0.01f : randf() * 0.03f; - - if (fall) { - radius = 1.5f; - strength = 0.05f; - } - - water[0]->bind(sDiffuse); - Core::setTarget(water[1]); - Core::active.shader->setParam(uType, Shader::WATER_DROP); - Core::active.shader->setParam(uParam, vec4(p.x, p.y, 128.0f / 5120.0f * radius, strength)); - mesh->renderQuad(); - swap(water[0], water[1]); - Input::down[ikU] = false; - } - } - - void renderWater() { - bool underwater = level.rooms[camera->getRoomIndex()].flags.water; - - // mask underwater geometry by zero alpha - setPassShader(Core::passWater); - atlas->bind(sNormal); - atlas->bind(sReflect); - - Core::active.shader->setParam(uType, Shader::WATER_MASK); - Core::active.shader->setParam(uViewProj, Core::mViewProj); - Core::active.shader->setParam(uScale, 1.0f); - Core::setBlending(bmNone); - Core::setCulling(cfNone); - glDepthMask(false); - glColorMask(false, false, false, true); - mesh->renderQuad(); - glColorMask(true, true, true, true); - glDepthMask(true); - - // get refraction texture - checkRefractTexture(); - waterRefract->bind(sDiffuse); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Core::width, Core::height); - - // render mirror reflection - Core::setTarget(waterReflect); - vec3 p = vec3(40448, 3584, 60928); - vec3 n = vec3(0, 1, 0); - - vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); - - camera->reflectPlane = &reflectPlane; - Core::setCulling(cfBack); - clipSign = underwater ? -1.0f : 1.0f; - clipHeight = 3584.0 * clipSign; - renderCompose(underwater ? 13 : lara->getRoomIndex()); - clipHeight = 1000000.0f; - clipSign = 1.0f; - Core::setCulling(cfFront); - camera->reflectPlane = NULL; - - - // simulate water - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - - setPassShader(Core::passWater); - waterDrop(); - waterSimulate(); - Core::setTarget(NULL); - - glEnable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - - // render water plane - int dx, dz; - TR::Room::Sector &s = level.getSector(lara->getRoomIndex(), int(lara->pos.x), int(lara->pos.z), dx, dz); - if (s.roomAbove != 0xFF && level.rooms[s.roomAbove].lightsCount) { - TR::Room::Light &light = level.rooms[s.roomAbove].lights[0]; - Core::lightPos[0] = vec3(float(light.x), float(light.y), float(light.z)); - float lum = intensityf(light.intensity); - Core::lightColor[0] = vec4(lum, lum, lum, float(light.radius) * float(light.radius)); - } - - Core::active.shader->setParam(uType, Shader::WATER_COMPOSE); - Core::active.shader->setParam(uViewProj, Core::mViewProj); - Core::active.shader->setParam(uViewPos, Core::viewPos); - 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) / waterRefract->width, float(Core::height) / waterRefract->height, 0.075f, 0.02f)); -// Core::active.shader->setParam(uParam, vec4(1.0f, 1.0f, 0.075f, 0.02f)); - Core::active.shader->setParam(uScale, 1.0f); - - waterRefract->bind(sDiffuse); - waterReflect->bind(sReflect); - water[0]->bind(sNormal); - Core::setCulling(cfNone); - mesh->renderQuad(); - Core::setCulling(cfFront); - } - void render() { clipHeight = 1000000.0f; clipSign = 1.0f; Core::resetStates(); ambientCache->precessQueue(); + waterCache->reset(); + renderShadows(lara->getRoomIndex()); Core::setViewport(0, 0, Core::width, Core::height); - renderCompose(camera->getRoomIndex()); - renderWater(); + waterCache->checkVisibility = true; + renderCompose(camera->getRoomIndex()); + waterCache->checkVisibility = false; + + waterCache->render(); // Core::mViewInv = camera->mViewInv; // Core::mView = Core::mViewInv.inverse(); @@ -1105,10 +1284,11 @@ struct Level { glLoadIdentity(); glOrtho(0, Core::width, 0, Core::height, 0, 1); - waterCaustics->bind(sDiffuse); + waterCache->reflect->bind(sDiffuse); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); glColor3f(1, 1, 1); glBegin(GL_QUADS); @@ -1121,6 +1301,7 @@ struct Level { glDisable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -1131,7 +1312,9 @@ struct Level { // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex()); // Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); - Debug::Level::portals(level); + // Core::setDepthTest(false); + // Debug::Level::portals(level); + // Core::setDepthTest(true); // Debug::Level::meshes(level); // Debug::Level::entities(level); /* diff --git a/src/mesh.h b/src/mesh.h index 547ff53..8a58efa 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -184,8 +184,7 @@ struct MeshBuilder { iCount += d.rCount * 6 + d.tCount * 3; vCount += d.rCount * 4 + d.tCount * 3; - if (roomCheckWaterPortal(r)) - roomRemoveWaterSurfaces(r, iCount, vCount); + roomRemoveWaterSurfaces(r, iCount, vCount); for (int j = 0; j < r.meshesCount; j++) { TR::Room::Mesh &m = r.meshes[j]; @@ -503,57 +502,64 @@ struct MeshBuilder { return false; } - void roomRemoveWaterSurfaces(TR::Room room, int &iCount, int &vCount) { + void roomRemoveWaterSurfaces(TR::Room &room, int &iCount, int &vCount) { + if (!roomCheckWaterPortal(room)) return; + // remove animated water polygons from room geometry - for (int j = 0; j < room.portalsCount; j++) { - TR::Room::Portal &p = room.portals[j]; - if (!(room.flags.water ^ level->rooms[p.roomIndex].flags.water)) // check if portal is not on water surface + for (int i = 0; i < room.data.rCount; i++) { + TR::Rectangle &f = room.data.rectangles[i]; + if (f.vertices[0] == 0xFFFF) continue; + + TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; + TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; + TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; + TR::Vertex &d = room.data.vertices[f.vertices[3]].vertex; + + if (a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or non-portal plane primitive continue; - TR::Vertex pMin, pMax; - pMin.x = min(min(min(p.vertices[0].x, p.vertices[1].x), p.vertices[2].x), p.vertices[3].x); - pMin.z = min(min(min(p.vertices[0].z, p.vertices[1].z), p.vertices[2].z), p.vertices[3].z); - pMax.x = max(max(max(p.vertices[0].x, p.vertices[1].x), p.vertices[2].x), p.vertices[3].x); - pMax.z = max(max(max(p.vertices[0].z, p.vertices[1].z), p.vertices[2].z), p.vertices[3].z); - pMin.y = pMax.y = p.vertices[0].y; + int yt = abs(a.y - room.info.yTop); + int yb = abs(room.info.yBottom - a.y); - for (int i = 0; i < room.data.rCount; i++) { - TR::Rectangle &f = room.data.rectangles[i]; - TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; - TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; - TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; - TR::Vertex &d = room.data.vertices[f.vertices[3]].vertex; + if (yt > 0 && yb > 0) continue; + + int sx = (int(a.x) + int(b.x) + int(c.x) + int(d.x)) / 4 / 1024; + int sz = (int(a.z) + int(b.z) + int(c.z) + int(d.z)) / 4 / 1024; - if (abs(a.y - pMin.y) > 1 || a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or not in portal plane primitive - continue; + TR::Room::Sector &s = room.sectors[sx * room.zSectors + sz]; - int cx = (int(a.x) + int(b.x) + int(c.x) + int(d.x)) / 4; - int cz = (int(a.z) + int(b.z) + int(c.z) + int(d.z)) / 4; - - if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders - continue; - - f.vertices[0] = 0xFFFF; // mark it as unused - iCount -= 3 * 2; + if ((yt == 0 && s.roomAbove != TR::NO_ROOM && (level->rooms[s.roomAbove].flags.water ^ room.flags.water)) || + (yb == 0 && s.roomBelow != TR::NO_ROOM && (level->rooms[s.roomBelow].flags.water ^ room.flags.water))) { + f.vertices[0] = 0xFFFF; // mark as unused + iCount -= 6; vCount -= 4; } + } - for (int i = 0; i < room.data.tCount; i++) { - TR::Triangle &f = room.data.triangles[i]; - TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; - TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; - TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; + for (int i = 0; i < room.data.tCount; i++) { + TR::Triangle &f = room.data.triangles[i]; + if (f.vertices[0] == 0xFFFF) continue; - if (abs(a.y - pMin.y) > 1 || a.y != b.y || a.y != c.y) // skip non-horizontal or not in portal plane primitive - continue; + TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; + TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; + TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; - int cx = (int(a.x) + int(b.x) + int(c.x)) / 3; - int cz = (int(a.z) + int(b.z) + int(c.z)) / 3; + if (a.y != b.y || a.y != c.y) // skip non-horizontal or non-portal plane primitive + continue; - if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders - continue; + int yt = abs(a.y - room.info.yTop); + int yb = abs(room.info.yBottom - a.y); - f.vertices[0] = 0xFFFF; // mark it as unused + if (yt > 1 && yb > 1) continue; + + int sx = (int(a.x) + int(b.x) + int(c.x)) / 3 / 1024; + int sz = (int(a.z) + int(b.z) + int(c.z)) / 3 / 1024; + + TR::Room::Sector &s = room.sectors[sx * room.zSectors + sz]; + + if ((yt <= 1 && s.roomAbove != TR::NO_ROOM && (level->rooms[s.roomAbove].flags.water ^ room.flags.water)) || + (yb <= 1 && s.roomBelow != TR::NO_ROOM && (level->rooms[s.roomBelow].flags.water ^ room.flags.water))) { + f.vertices[0] = 0xFFFF; // mark as unused iCount -= 3; vCount -= 3; } diff --git a/src/shader.h b/src/shader.h index 5a864a7..1949f1e 100644 --- a/src/shader.h +++ b/src/shader.h @@ -5,11 +5,11 @@ enum AttribType { aCoord, aTexCoord, aNormal, aColor, aMAX }; enum SamplerType { sDiffuse, sNormal, sReflect, sShadow, sEnvironment, sMAX }; -enum UniformType { uType, uCaustics, uParam, uViewProj, uViewInv, uBasis, uLightProj, uColor, uAmbient, uViewPos, uLightsCount, uLightPos, uLightColor, uAnimTexRanges, uAnimTexOffsets, uRoomSize, uScale, uMAX }; +enum UniformType { uType, uCaustics, uParam, uViewProj, uViewInv, uBasis, uLightProj, uColor, uAmbient, uViewPos, uLightsCount, uLightPos, uLightColor, uAnimTexRanges, uAnimTexOffsets, uRoomSize, uPosScale, uMAX }; const char *AttribName[aMAX] = { "aCoord", "aTexCoord", "aNormal", "aColor" }; const char *SamplerName[sMAX] = { "sDiffuse", "sNormal", "sReflect", "sShadow", "sEnvironment" }; -const char *UniformName[uMAX] = { "uType", "uCaustics", "uParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightsCount", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize", "uScale" }; +const char *UniformName[uMAX] = { "uType", "uCaustics", "uParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightsCount", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize", "uPosScale" }; struct Shader { GLuint ID; diff --git a/src/trigger.h b/src/trigger.h index b41cd0c..c964743 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -309,4 +309,39 @@ struct Crystal : Controller { } }; +struct Waterfall : Trigger { + #define SPLASH_TIMESTEP (1.0f / 30.0f) + + float timer; + bool drop; + float dropRadius; + float dropStrength; + vec3 dropPos; + + Waterfall(TR::Level *level, int entity) : Trigger(level, entity, true), timer(0.0f) {} + + virtual void update() { + drop = false; + Trigger::update(); + if (!getEntity().flags.active) return; + + vec3 delta = (((Controller*)level->cameraController)->pos - pos) * (1.0f / 1024.0f); + if (delta.length2() > 100.0f) + return; + + timer -= Core::deltaTime; + if (timer > 0.0f) return; + timer += SPLASH_TIMESTEP * (1.0f + randf() * 0.25f); + + drop = true; + dropPos = pos + vec3(randf() * 1024.0f - 512.0f, 0.0f, randf() * 1024.0f - 512.0f); + dropRadius = randf() + 0.5f; + dropStrength = randf() * 0.25f; + + Sprite::add(level, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)dropPos.x, (int)dropPos.y, (int)dropPos.z); + } + + #undef SPLASH_TIMESTEP +}; + #endif \ No newline at end of file diff --git a/src/water.glsl b/src/water.glsl index e60bc41..38533c2 100644 --- a/src/water.glsl +++ b/src/water.glsl @@ -22,6 +22,7 @@ varying vec3 vRefPos2; uniform int uType; uniform vec3 uViewPos; uniform mat4 uViewProj; +uniform vec3 uPosScale[2]; uniform float uScale; uniform sampler2D sNormal; @@ -33,12 +34,11 @@ uniform sampler2D sNormal; attribute vec4 aCoord; void main() { - vec4 rCoord = aCoord * uScale; + vTexCoord = aCoord.xy * 0.5 + 0.5; if (uType >= WATER_MASK) { // hardcoded pool geometry - vec2 p = rCoord.xy * 2560.0; - vCoord = vec3(p.x + 40448.0, 3584.0, p.y + 60928.0); + vCoord = vec3(aCoord.x, 0.0, aCoord.y) * uPosScale[1] + uPosScale[0]; vec4 cp = uViewProj * vec4(vCoord, 1.0); vProjCoord = cp; @@ -46,26 +46,27 @@ uniform sampler2D sNormal; } else { vProjCoord = vec4(0.0); if (uType == WATER_CAUSTICS) { - vec4 info = texture2D(sNormal, rCoord.xy * 0.5 + 0.5); + vec3 rCoord = vec3(aCoord.x, 0.0, aCoord.y) * uPosScale[1]; + + vec4 info = texture2D(sNormal, rCoord.xz * 0.5 + 0.5); info.ba *= 0.5; vec3 normal = vec3(info.b, -sqrt(1.0 - dot(info.ba, info.ba)), info.a); vec3 light = vec3(0.0, -1.0, 0.0); vec3 refractedLight = refract(-light, vec3(0.0, 1.0, 0.0), ETA_AIR / ETA_WATER); vec3 ray = refract(-light, normal, ETA_AIR / ETA_WATER); - vRefPos1 = rCoord.xzy + vec3(0.0, 1.0, 0.0); - vRefPos2 = rCoord.xzy + vec3(0.0, info.r, 0.0) + ray / ray.y; + + vRefPos1 = rCoord + vec3(0.0, 1.0, 0.0); + vRefPos2 = rCoord + vec3(0.0, info.r, 0.0) + ray / ray.y; gl_Position = vec4((vRefPos2.xz + 0.0 * refractedLight.xz / refractedLight.y), 0.0, 1.0); } else { vRefPos1 = vRefPos2 = vec3(0.0); - vCoord = vec3(rCoord.xy, 0.0); + vCoord = vec3(aCoord.xy, 0.0); - gl_Position = vec4(rCoord.xyz, 1.0); + gl_Position = vec4(aCoord.xyz, 1.0); } - } - - vTexCoord = rCoord.xy * 0.5 + 0.5; + } } #else uniform sampler2D sDiffuse; @@ -153,15 +154,15 @@ uniform sampler2D sNormal; vec3 rv = reflect(-viewVec, normal); vec3 lv = normalize(uLightPos - vCoord.xyz); - float spec = pow(max(0.0, dot(rv, -lv)), 64.0) * 0.5; + float spec = pow(max(0.0, dot(rv, lv)), 64.0) * 0.5; 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, tc + dudv * uParam.w); + vec4 refl = texture2D(sReflect, vec2(tc.x, 1.0 - tc.y) + dudv * uParam.w); float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0); - return mix(refr, refl, fresnel) + spec; + return mix(refr, refl, fresnel) + spec; } vec4 pass() {