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

#23 waterCache manager, waterfall drops and splashes; #8 increase view distance to 40 cells;

This commit is contained in:
XProger
2017-01-30 05:53:51 +03:00
parent 31410baffd
commit 6ee2aa68cf
11 changed files with 546 additions and 278 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,9 +954,12 @@ 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);
/*

View File

@@ -184,7 +184,6 @@ struct MeshBuilder {
iCount += d.rCount * 6 + d.tCount * 3;
vCount += d.rCount * 4 + d.tCount * 3;
if (roomCheckWaterPortal(r))
roomRemoveWaterSurfaces(r, iCount, vCount);
for (int j = 0; j < r.meshesCount; 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
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;
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 (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
if (a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or non-portal plane primitive
continue;
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;
int yt = abs(a.y - room.info.yTop);
int yb = abs(room.info.yBottom - a.y);
if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders
continue;
if (yt > 0 && yb > 0) continue;
f.vertices[0] = 0xFFFF; // mark it as unused
iCount -= 3 * 2;
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;
TR::Room::Sector &s = room.sectors[sx * room.zSectors + sz];
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];
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;
if (abs(a.y - pMin.y) > 1 || a.y != b.y || a.y != c.y) // skip non-horizontal or not in portal plane primitive
if (a.y != b.y || a.y != c.y) // skip non-horizontal or non-portal plane primitive
continue;
int cx = (int(a.x) + int(b.x) + int(c.x)) / 3;
int cz = (int(a.z) + int(b.z) + int(c.z)) / 3;
int yt = abs(a.y - room.info.yTop);
int yb = abs(room.info.yBottom - a.y);
if (c.x < pMin.x || c.x > pMax.x || c.z < pMin.z || c.z > pMax.z) // check for portal borders
continue;
if (yt > 1 && yb > 1) continue;
f.vertices[0] = 0xFFFF; // mark it as unused
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;
}

View File

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

View File

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

View File

@@ -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,12 +154,12 @@ 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;