1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-02-24 15:32:30 +01:00

№23 global IGame interface, variable size of water texture

This commit is contained in:
XProger 2017-02-02 12:27:18 +03:00
parent 6ee2aa68cf
commit 38934fb41a
14 changed files with 303 additions and 226 deletions

View File

@ -23,7 +23,7 @@ struct Camera : Controller {
vec4 *reflectPlane;
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) {
Camera(IGame *game, Lara *owner) : Controller(game, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL) {
fov = 65.0f;
znear = 16;
zfar = 40.0f * 1024.0f;

View File

@ -29,7 +29,7 @@ struct Character : Controller {
vec3 velocity;
float angleExt;
Character(TR::Level *level, int entity, int health) : Controller(level, entity), target(-1), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f) {
Character(IGame *game, int entity, int health) : Controller(game, entity), target(-1), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f) {
animation.initOverrides();
rotHead = rotChest = quat(0, 0, 0, 1);
}

View File

@ -12,7 +12,13 @@
#define MAX_LAYERS 4
struct IGame {
virtual TR::Level* getLevel() { return NULL; }
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
};
struct Controller {
IGame *game;
TR::Level *level;
int entity;
@ -44,7 +50,7 @@ struct Controller {
ActionCommand(int emitter, TR::Action action, int value, float timer, ActionCommand *next = NULL) : emitter(emitter), action(action), value(value), timer(timer), next(next) {}
} *actionCommand;
Controller(TR::Level *level, int entity) : level(level), entity(entity), animation(level, getModel()), state(animation.state), layers(NULL), actionCommand(NULL) {
Controller(IGame *game, int entity) : game(game), level(game->getLevel()), entity(entity), animation(level, getModel()), state(animation.state), layers(NULL), actionCommand(NULL) {
TR::Entity &e = getEntity();
pos = vec3((float)e.x, (float)e.y, (float)e.z);
angle = vec3(0.0f, e.rotation, 0.0f);

View File

@ -6,7 +6,7 @@
struct Enemy : Character {
bool bitten;
Enemy(TR::Level *level, int entity, int health) : Character(level, entity, health), bitten(false) {}
Enemy(IGame *game, int entity, int health) : Character(game, entity, health), bitten(false) {}
virtual bool activate(ActionCommand *cmd) {
#ifdef LEVEL_EDITOR
@ -134,7 +134,7 @@ struct Enemy : Character {
ASSERT(target > -1);
Character *c = (Character*)level->entities[target].controller;
c->hit(damage, this);
Sprite::add(level, TR::Entity::BLOOD, c->getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, Sprite::FRAME_ANIMATED);
Sprite::add(game, TR::Entity::BLOOD, c->getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, Sprite::FRAME_ANIMATED);
}
};
@ -171,7 +171,7 @@ struct Wolf : Enemy {
JOINT_HEAD = 3
};
Wolf(TR::Level *level, int entity) : Enemy(level, entity, 6) {}
Wolf(IGame *game, int entity) : Enemy(game, entity, 6) {}
virtual int getStateGround() {
TR::Entity &e = getEntity();
@ -268,7 +268,7 @@ struct Bear : Enemy {
JOINT_HEAD = 3
};
Bear(TR::Level *level, int entity) : Enemy(level, entity, 20) {}
Bear(IGame *game, int entity) : Enemy(game, entity, 20) {}
virtual int getStateGround() {
switch (state) {
@ -343,7 +343,7 @@ struct Bat : Enemy {
STATE_DEATH = 5,
};
Bat(TR::Level *level, int entity) : Enemy(level, entity, 1) { stand = STAND_AIR; }
Bat(IGame *game, int entity) : Enemy(game, entity, 1) { stand = STAND_AIR; }
virtual int getStateAir() {
if (!getEntity().flags.active) {

View File

@ -202,7 +202,7 @@ struct Lara : Character {
int lastPickUp;
int viewTarget;
Lara(TR::Level *level, int entity, bool home) : Character(level, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1) {
Lara(IGame *game, int entity, bool home) : Character(game, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1) {
if (getEntity().type == TR::Entity::LARA) {
if (getRoom().flags.water)
@ -598,10 +598,10 @@ struct Lara : Character {
if (target > -1 && checkHit(target, p, hit, hit)) {
((Character*)level->entities[target].controller)->hit(wpnGetDamage());
hit -= d * 64.0f;
Sprite::add(level, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED);
Sprite::add(game, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED);
} else {
hit -= d * 64.0f;
Sprite::add(level, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_RANDOM);
Sprite::add(game, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_RANDOM);
float dist = (hit - p).length();
if (dist < nearDist) {
@ -938,7 +938,7 @@ struct Lara : Character {
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.y = float(infoDst.floor);
//pos = dst; // set new position
specular = LARA_WET_SPECULAR;
@ -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::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation
}
@ -1416,13 +1416,25 @@ struct Lara : Character {
if (state != STATE_SURF_TREAD && state != STATE_SURF_LEFT && state != STATE_SURF_RIGHT && state != STATE_SURF_SWIM && state != STATE_SURF_BACK && state != STATE_STOP)
return animation.setAnim(ANIM_TO_ONWATER);
if (state == STATE_SURF_TREAD) {
if (animation.isFrameActive(0))
game->waterDrop(animation.getJoints(getMatrix(), 14).pos, 96.0f, 0.01f);
} else {
if (animation.frameIndex % 4 == 0)
game->waterDrop(animation.getJoints(getMatrix(), 14).pos, 128.0f, 0.05f);
}
if (input & FORTH) {
if (input & JUMP) {
angle.x = -PI * 0.25f;
game->waterDrop(pos, 256.0f, 0.2f);
return animation.setAnim(ANIM_TO_UNDERWATER);
}
if ((input & ACTION) && waterOut()) return state;
if ((input & ACTION) && waterOut()) {
game->waterDrop(pos, 128.0f, 0.2f);
return state;
}
return STATE_SURF_SWIM;
}

View File

@ -29,7 +29,7 @@ const char GUI[] =
#include "gui.glsl"
;
struct Level {
struct Level : IGame {
enum { shCompose, shShadow, shAmbient, shFilter, shWater, shGUI, shMAX };
TR::Level level;
@ -196,29 +196,70 @@ struct Level {
bool visible;
bool blank;
vec3 pos, size;
Texture *data[2];
Texture *mask;
Texture *caustics;
Texture *data[2];
Item() {
data[0] = data[1] = caustics = NULL;
mask = caustics = data[0] = data[1] = 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;
Item(int from, int to) : from(from), to(to), timer(SIMULATE_TIMESTEP), visible(true), blank(true) {
mask = caustics = data[0] = data[1] = 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);
void init(Level *level) {
TR::Room &r = level->level.rooms[to]; // underwater room
int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0, posY;
for (int z = 0; z < r.zSectors; z++)
for (int x = 0; x < r.xSectors; x++) {
TR::Room::Sector &s = r.sectors[x * r.zSectors + z];
if (s.roomAbove != TR::NO_ROOM && !level->level.rooms[s.roomAbove].flags.water) {
minX = min(minX, x);
minZ = min(minZ, z);
maxX = max(maxX, x);
maxZ = max(maxZ, z);
posY = s.ceiling * 256;
}
}
maxX++;
maxZ++;
int w = nextPow2(maxX - minX);
int h = nextPow2(maxZ - minZ);
uint8 *m = new uint8[w * h];
memset(m, 0, w * h);
for (int z = minZ; z < maxZ; z++)
for (int x = minX; x < maxX; x++) {
TR::Room::Sector &s = r.sectors[x * r.zSectors + z];
m[(x - minX) + w * (z - minZ)] = (s.roomAbove != TR::NO_ROOM && !level->level.rooms[s.roomAbove].flags.water) ? 255 : 0;
}
mask = new Texture(w, h, Texture::RED, false, m, false);
delete[] m;
size = vec3(float((maxX - minX) * 512), 1.0f, float((maxZ - minZ) * 512)); // half size
pos = vec3(r.info.x + minX * 1024 + size.x, float(posY), r.info.z + minZ * 1024 + size.z);
data[0] = new Texture(nextPow2(w * 64), nextPow2(h * 64), 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);
caustics = new Texture(512, 512, Texture::RGBA, false);
blank = false;
Core::setTarget(data[0]);
Core::clear(vec4(0.0f));
Core::setTarget(NULL);
}
void free() {
delete data[0];
delete data[1];
delete caustics;
data[0] = data[1] = caustics = NULL;
delete mask;
mask = caustics = data[0] = data[1] = NULL;
}
} items[MAX_SURFACES];
@ -265,9 +306,20 @@ struct Level {
visible = 0;
}
void setVisible(int roomIndex, int nextRoom) {
void setVisible(int roomIndex, int nextRoom = TR::NO_ROOM) {
if (!checkVisibility) return;
if (nextRoom == TR::NO_ROOM) { // setVisible(underwaterRoom)
for (int i = 0; i < count; i++)
if (items[i].to == roomIndex) {
nextRoom = items[i].from;
break;
}
if (nextRoom == TR::NO_ROOM)
return;
}
int from, to; // from surface room to underwater room
if (level->level.rooms[roomIndex].flags.water) {
from = nextRoom;
@ -289,28 +341,8 @@ struct Level {
}
if (count == MAX_SURFACES) return;
items[count++] = Item(from, to);
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++;
}
@ -331,69 +363,14 @@ struct Level {
}
}
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);
@ -402,14 +379,50 @@ struct Level {
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));
p.x = (drop.pos.x - (item.pos.x - item.size.x)) * DETAIL;
p.z = (drop.pos.z - (item.pos.z - item.size.z)) * DETAIL;
Core::active.shader->setParam(uParam, vec4(p.x, p.z, drop.radius * DETAIL, drop.strength));
item.data[0]->bind(sDiffuse);
Core::setTarget(item.data[1]);
Core::setViewport(0, 0, int(item.size.x * DETAIL * 2.0f + 0.5f), int(item.size.z * DETAIL * 2.0f + 0.5f));
level->mesh->renderQuad();
swap(item.data[0], item.data[1]);
}
dropCount = 0;
swap(item.data[0], item.data[1]);
}
void step(Item &item) {
if (item.timer < SIMULATE_TIMESTEP) return;
Core::active.shader->setParam(uType, Shader::WATER_STEP);
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, 0));
while (item.timer >= SIMULATE_TIMESTEP) {
// water step
item.data[0]->bind(sDiffuse);
Core::setTarget(item.data[1]);
Core::setViewport(0, 0, int(item.size.x * DETAIL * 2.0f + 0.5f), int(item.size.z * DETAIL * 2.0f + 0.5f));
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(uType, Shader::WATER_CAUSTICS);
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
float sx = item.size.x * DETAIL / (item.data[0]->width / 2);
float sz = item.size.z * DETAIL / (item.data[0]->height / 2);
Core::active.shader->setParam(uTexParam, vec4(1.0f, 1.0f, sx, sz));
item.data[0]->bind(sNormal);
item.caustics->unbind(sReflect);
Core::setTarget(item.caustics);
level->mesh->renderPlane();
}
void render() {
@ -420,7 +433,8 @@ struct Level {
level->atlas->bind(sNormal);
level->atlas->bind(sReflect);
Core::active.shader->setParam(uType, Shader::WATER_MASK);
Core::active.shader->setParam(uType, Shader::WATER_MASK);
Core::active.shader->setParam(uTexParam, vec4(1.0f));
Core::setBlending(bmNone);
Core::setCulling(cfNone);
Core::setDepthWrite(false);
@ -451,7 +465,7 @@ struct Level {
if (!item.visible) continue;
if (item.blank) {
item.init();
item.init(level);
item.blank = false;
}
@ -477,22 +491,26 @@ struct Level {
level->camera->setup(true);
// simulate water
Core::setBlending(bmNone);
Core::setDepthTest(false);
level->setPassShader(Core::passWater);
drop(item);
simulate(item);
if (item.timer >= SIMULATE_TIMESTEP || dropCount) {
Core::setBlending(bmNone);
Core::setDepthTest(false);
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, 1.0f, 1.0f));
item.mask->bind(sEnvironment);
drop(item);
step(item);
Core::setBlending(bmAlpha);
Core::setDepthTest(true);
}
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];
if (level->level.rooms[item.from].lightsCount) {
TR::Room::Light &light = level->level.rooms[item.from].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));
@ -506,11 +524,23 @@ struct Level {
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));
float sx = item.size.x * DETAIL / (item.data[0]->width / 2);
float sz = item.size.z * DETAIL / (item.data[0]->height / 2);
Core::active.shader->setParam(uTexParam, vec4(1.0f, 1.0f, sx, sz));
refract->bind(sDiffuse);
reflect->bind(sReflect);
item.data[0]->bind(sNormal);
Core::setCulling(cfNone);
//vec3 rPosScale[2] = { item.pos, item.size * vec3(1.0f / PLANE_DETAIL, 1.0f, 1.0f / PLANE_DETAIL) };
//Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
level->mesh->renderQuad();
//level->mesh->renderPlane();
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
Core::setCulling(cfFront);
}
}
@ -561,6 +591,16 @@ struct Level {
}
} lightCache;
*/
virtual TR::Level* getLevel() {
return &level;
}
virtual void waterDrop(const vec3 &pos, float radius, float strength) {
waterCache->addDrop(pos, radius, strength);
}
Level(Stream &stream, bool demo, bool home) : level(stream, demo), lara(NULL), time(0.0f) {
#ifdef _DEBUG
Debug::init();
@ -576,16 +616,16 @@ struct Level {
switch (entity.type) {
case TR::Entity::LARA :
case TR::Entity::CUT_1 :
entity.controller = (lara = new Lara(&level, i, home));
entity.controller = (lara = new Lara(this, i, home));
break;
case TR::Entity::ENEMY_WOLF :
entity.controller = new Wolf(&level, i);
entity.controller = new Wolf(this, i);
break;
case TR::Entity::ENEMY_BEAR :
entity.controller = new Bear(&level, i);
entity.controller = new Bear(this, i);
break;
case TR::Entity::ENEMY_BAT :
entity.controller = new Bat(&level, i);
entity.controller = new Bat(this, i);
break;
case TR::Entity::ENEMY_TWIN :
case TR::Entity::ENEMY_CROCODILE_LAND :
@ -602,7 +642,7 @@ struct Level {
case TR::Entity::ENEMY_CENTAUR :
case TR::Entity::ENEMY_MUMMY :
case TR::Entity::ENEMY_LARSON :
entity.controller = new Enemy(&level, i, 100);
entity.controller = new Enemy(this, i, 100);
break;
case TR::Entity::DOOR_1 :
case TR::Entity::DOOR_2 :
@ -612,61 +652,61 @@ struct Level {
case TR::Entity::DOOR_6 :
case TR::Entity::DOOR_BIG_1 :
case TR::Entity::DOOR_BIG_2 :
entity.controller = new Door(&level, i);
entity.controller = new Door(this, i);
break;
case TR::Entity::TRAP_DOOR_1 :
case TR::Entity::TRAP_DOOR_2 :
entity.controller = new TrapDoor(&level, i);
entity.controller = new TrapDoor(this, i);
break;
case TR::Entity::BRIDGE_0 :
case TR::Entity::BRIDGE_1 :
case TR::Entity::BRIDGE_2 :
entity.controller = new Bridge(&level, i);
entity.controller = new Bridge(this, i);
break;
case TR::Entity::GEARS_1 :
case TR::Entity::GEARS_2 :
case TR::Entity::GEARS_3 :
entity.controller = new Boulder(&level, i);
entity.controller = new Boulder(this, i);
break;
case TR::Entity::TRAP_FLOOR :
entity.controller = new TrapFloor(&level, i);
entity.controller = new TrapFloor(this, i);
break;
case TR::Entity::CRYSTAL :
entity.controller = new Crystal(&level, i);
entity.controller = new Crystal(this, i);
break;
case TR::Entity::TRAP_BLADE :
case TR::Entity::TRAP_SPIKES :
entity.controller = new Trigger(&level, i, true);
entity.controller = new Trigger(this, i, true);
break;
case TR::Entity::TRAP_BOULDER :
entity.controller = new Boulder(&level, i);
entity.controller = new Boulder(this, i);
break;
case TR::Entity::TRAP_DARTGUN :
entity.controller = new Dartgun(&level, i);
entity.controller = new Dartgun(this, i);
break;
case TR::Entity::BLOCK_1 :
case TR::Entity::BLOCK_2 :
entity.controller = new Block(&level, i);
entity.controller = new Block(this, i);
break;
case TR::Entity::SWITCH :
case TR::Entity::SWITCH_WATER :
case TR::Entity::HOLE_PUZZLE :
case TR::Entity::HOLE_KEY :
entity.controller = new Trigger(&level, i, false);
entity.controller = new Trigger(this, i, false);
break;
case TR::Entity::WATERFALL :
entity.controller = new Waterfall(&level, i);
entity.controller = new Waterfall(this, i);
break;
default :
if (entity.modelIndex > 0)
entity.controller = new Controller(&level, i);
entity.controller = new Controller(this, i);
else
entity.controller = new Sprite(&level, i, 0);
entity.controller = new Sprite(this, i, 0);
}
}
ASSERT(lara != NULL);
camera = new Camera(&level, lara);
camera = new Camera(this, lara);
level.cameraController = camera;
@ -905,6 +945,9 @@ struct Level {
// room geometry & sprites
if (!room.flags.rendered) { // skip if already rendered
if (room.flags.water)
waterCache->setVisible(roomIndex);
room.flags.rendered = true;
if (Core::pass != Core::passShadow) {
@ -1063,8 +1106,8 @@ struct Level {
}
}
}
}
}
camera->update();
waterCache->update();
}
@ -1265,7 +1308,7 @@ struct Level {
}
vec3 p = lara->pos + lara->getDir() * 256.0f;
lastEntity = level.entityAdd(level.models[modelIndex].type, lara->getRoomIndex(), p.x, p.y - 512, p.z, lara->getEntity().rotation, -1);
level.entities[lastEntity].controller = new Controller(&level, lastEntity);
level.entities[lastEntity].controller = new Controller(this, lastEntity);
}
} else
lastStateK = false;
@ -1284,19 +1327,23 @@ struct Level {
glLoadIdentity();
glOrtho(0, Core::width, 0, Core::height, 0, 1);
waterCache->reflect->bind(sDiffuse);
if (waterCache->count)
waterCache->items[0].data[0]->bind(sDiffuse);
else
atlas->bind(sDiffuse);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glColor3f(1, 1, 1);
glColor3f(10, 10, 10);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(0, 0);
glTexCoord2f(1, 0); glVertex2f(256, 0);
glTexCoord2f(1, 1); glVertex2f(256, 256);
glTexCoord2f(0, 1); glVertex2f(0, 256);
glEnd();
glColor3f(1, 1, 1);
glDisable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
@ -1310,7 +1357,7 @@ struct Level {
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
// Debug::Level::lights(level, lara->getRoomIndex());
Debug::Level::lights(level, lara->getRoomIndex());
// Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y);
// Core::setDepthTest(false);
// Debug::Level::portals(level);

View File

@ -517,17 +517,17 @@ struct MeshBuilder {
if (a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or non-portal plane primitive
continue;
int yt = abs(a.y - room.info.yTop);
int yb = abs(room.info.yBottom - a.y);
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;
TR::Room::Sector &s = room.sectors[sx * room.zSectors + sz];
int yt = abs(a.y - s.ceiling * 256);
int yb = abs(s.floor * 256 - a.y);
if (yt > 0 && yb > 0) continue;
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
@ -546,17 +546,17 @@ struct MeshBuilder {
if (a.y != b.y || a.y != c.y) // skip non-horizontal or non-portal plane primitive
continue;
int yt = abs(a.y - room.info.yTop);
int yb = abs(room.info.yBottom - a.y);
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];
int yt = abs(a.y - s.ceiling * 256);
int yb = abs(s.floor * 256 - a.y);
if (yt > 0 && yb > 0) continue;
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

View File

@ -27,6 +27,7 @@ uniform int uType;
#ifdef PASS_COMPOSE
uniform int uCaustics;
uniform vec4 uParam;
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
#endif
#ifdef VERTEX
@ -42,7 +43,6 @@ uniform int uType;
uniform vec3 uViewPos;
uniform vec2 uAnimTexRanges[MAX_RANGES];
uniform vec2 uAnimTexOffsets[MAX_OFFSETS];
uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ
#endif
attribute vec4 aCoord;
@ -102,7 +102,7 @@ uniform int uType;
float sum = coord.x + coord.y + coord.z;
vColor.xyz *= abs(sin(sum / 512.0 + uParam.x)) * 1.5 + 0.5; // color dodge
}
vTexCoord.zw = smoothstep(uRoomSize.xy, uRoomSize.zw, coord.xz);
vTexCoord.zw = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0));
vViewVec = uViewPos - coord.xyz;
vLightProj = uLightProj * coord;
@ -228,7 +228,11 @@ uniform int uType;
}
float calcCaustics(vec3 n) {
return texture2D(sReflect, vTexCoord.zw).r * (float(uCaustics) * max(0.0, -n.y));
if (uCaustics != 0) {
vec2 fade = smoothstep(uRoomSize.xy, uRoomSize.xy + vec2(256.0), vCoord.xz) * (1.0f - smoothstep(uRoomSize.zw - vec2(256.0), uRoomSize.zw, vCoord.xz));
return texture2D(sReflect, vTexCoord.zw).r * (max(0.0, -n.y)) * fade.x * fade.y;
} else
return 0.0;
}
#endif

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, uPosScale, uMAX };
enum UniformType { uType, uCaustics, uParam, uTexParam, 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", "uPosScale" };
const char *UniformName[uMAX] = { "uType", "uCaustics", "uParam", "uTexParam", "uViewProj", "uViewInv", "uBasis", "uLightProj", "uColor", "uAmbient", "uViewPos", "uLightsCount", "uLightPos", "uLightColor", "uAnimTexRanges", "uAnimTexOffsets", "uRoomSize", "uPosScale" };
struct Shader {
GLuint ID;

View File

@ -465,8 +465,8 @@ namespace Sound {
int idxA = int(t);
int idxB = (j == (count - 1)) ? idxA : (idxA + 1);
float k = t - idxA;
result[j].L += lerp(buffer[idxA].L, buffer[idxB].L, k);
result[j].R += lerp(buffer[idxA].R, buffer[idxB].R, k);
result[j].L += int(lerp(buffer[idxA].L, buffer[idxB].L, k));
result[j].R += int(lerp(buffer[idxA].R, buffer[idxB].R, k));
}
}
}

View File

@ -14,7 +14,7 @@ struct Sprite : Controller {
int frame, flag;
float time;
Sprite(TR::Level *level, int entity, bool instant = true, int frame = FRAME_ANIMATED) : Controller(level, entity), instant(instant), flag(frame), time(0.0f) {
Sprite(IGame *game, int entity, bool instant = true, int frame = FRAME_ANIMATED) : Controller(game, entity), instant(instant), flag(frame), time(0.0f) {
if (frame >= 0) { // specific frame
this->frame = frame;
} else if (frame == FRAME_RANDOM) { // random frame
@ -24,11 +24,12 @@ struct Sprite : Controller {
}
}
static void add(TR::Level *level, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1) {
static void add(IGame *game, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1) {
TR::Level *level = game->getLevel();
int index = level->entityAdd(type, room, x, y, z, 0, -1);
if (index > -1) {
level->entities[index].intensity = 0x1FFF - level->rooms[room].ambient;
level->entities[index].controller = new Sprite(level, index, true, frame);
level->entities[index].controller = new Sprite(game, index, true, frame);
}
}

View File

@ -11,12 +11,10 @@ struct Texture {
Format format;
bool cube;
Texture(int width, int height, Format format, bool cube, void *data = NULL) : width(width), height(height), cube(cube) {
Texture(int width, int height, Format format, bool cube, void *data = NULL, bool filter = true) : width(width), height(height), cube(cube) {
glGenTextures(1, &ID);
bind(0);
bool filter = true;
GLenum target = cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
if (format == SHADOW && !Core::support.shadowSampler) {

View File

@ -11,7 +11,7 @@ struct Trigger : Controller {
float timer;
int baseState;
Trigger(TR::Level *level, int entity, bool immediate) : Controller(level, entity), immediate(immediate), timer(0.0f) {
Trigger(IGame *game, int entity, bool immediate) : Controller(game, entity), immediate(immediate), timer(0.0f) {
baseState = state;
getEntity().flags.collision = false;
}
@ -65,7 +65,7 @@ struct Dart : Controller {
vec3 dir;
bool inWall; // dart starts from wall
Dart(TR::Level *level, int entity) : Controller(level, entity), inWall(true) {
Dart(IGame *game, int entity) : Controller(game, entity), inWall(true) {
dir = vec3(sinf(angle.y), 0, cosf(angle.y));
}
@ -80,7 +80,7 @@ struct Dart : Controller {
TR::Entity &e = getEntity();
vec3 p = pos - dir * 64.0f; // wall offset = 64
Sprite::add(level, TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM);
Sprite::add(game, TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM);
level->entityRemove(entity);
delete this;
@ -93,7 +93,7 @@ struct Dart : Controller {
struct Dartgun : Trigger {
vec3 origin;
Dartgun(TR::Level *level, int entity) : Trigger(level, entity, true), origin(pos) {}
Dartgun(IGame *game, int entity) : Trigger(game, entity, true), origin(pos) {}
virtual bool activate(ActionCommand *cmd) {
if (!Trigger::activate(cmd))
@ -106,9 +106,9 @@ struct Dartgun : Trigger {
int dartIndex = level->entityAdd(TR::Entity::TRAP_DART, entity.room, (int)p.x, (int)p.y, (int)p.z, entity.rotation, entity.intensity);
if (dartIndex > -1)
level->entities[dartIndex].controller = new Dart(level, dartIndex);
level->entities[dartIndex].controller = new Dart(game, dartIndex);
Sprite::add(level, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
playSound(TR::SND_DART, pos, Sound::Flags::PAN);
@ -119,7 +119,7 @@ struct Dartgun : Trigger {
struct Boulder : Trigger {
Boulder(TR::Level *level, int entity) : Trigger(level, entity, true) {}
Boulder(IGame *game, int entity) : Trigger(game, entity, true) {}
virtual void update() {
if (getEntity().flags.active) {
@ -138,7 +138,7 @@ struct Block : Controller {
STATE_PULL,
};
Block(TR::Level *level, int entity) : Controller(level, entity) {
Block(IGame *game, int entity) : Controller(game, entity) {
updateFloor(true);
}
@ -181,7 +181,7 @@ struct Block : Controller {
struct Door : Trigger {
int8 *floor[2], orig[2];
Door(TR::Level *level, int entity) : Trigger(level, entity, true) {
Door(IGame *game, int entity) : Trigger(game, entity, true) {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
vec3 p = pos - getDir() * 1024.0f;
@ -222,7 +222,7 @@ struct Door : Trigger {
struct TrapDoor : Trigger {
TrapDoor(TR::Level *level, int entity) : Trigger(level, entity, true) {
TrapDoor(IGame *game, int entity) : Trigger(game, entity, true) {
getEntity().flags.collision = true;
}
@ -245,7 +245,7 @@ struct TrapFloor : Trigger {
};
float velocity;
TrapFloor(TR::Level *level, int entity) : Trigger(level, entity, true), velocity(0) {
TrapFloor(IGame *game, int entity) : Trigger(game, entity, true), velocity(0) {
TR::Entity &e = getEntity();
e.flags.collision = true;
}
@ -283,7 +283,7 @@ struct TrapFloor : Trigger {
struct Bridge : Trigger {
Bridge(TR::Level *level, int entity) : Trigger(level, entity, true) {
Bridge(IGame *game, int entity) : Trigger(game, entity, true) {
getEntity().flags.collision = true;
}
};
@ -291,7 +291,7 @@ struct Bridge : Trigger {
struct Crystal : Controller {
Texture *environment;
Crystal(TR::Level *level, int entity) : Controller(level, entity) {
Crystal(IGame *game, int entity) : Controller(game, entity) {
environment = new Texture(64, 64, Texture::RGBA, true);
}
@ -318,7 +318,7 @@ struct Waterfall : Trigger {
float dropStrength;
vec3 dropPos;
Waterfall(TR::Level *level, int entity) : Trigger(level, entity, true), timer(0.0f) {}
Waterfall(IGame *game, int entity) : Trigger(game, entity, true), timer(0.0f) {}
virtual void update() {
drop = false;
@ -335,10 +335,10 @@ struct Waterfall : Trigger {
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;
dropRadius = randf() * 256.0f + 128.0f;
dropStrength = randf() * 0.1f + 0.1f;
Sprite::add(level, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)dropPos.x, (int)dropPos.y, (int)dropPos.z);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)dropPos.x, (int)dropPos.y, (int)dropPos.z);
}
#undef SPLASH_TIMESTEP

View File

@ -23,7 +23,9 @@ uniform int uType;
uniform vec3 uViewPos;
uniform mat4 uViewProj;
uniform vec3 uPosScale[2];
uniform float uScale;
uniform vec4 uTexParam;
uniform vec4 uParam;
uniform sampler2D sNormal;
@ -34,10 +36,9 @@ uniform sampler2D sNormal;
attribute vec4 aCoord;
void main() {
vTexCoord = aCoord.xy * 0.5 + 0.5;
vTexCoord = (aCoord.xy * 0.5 + 0.5) * uTexParam.zw;
if (uType >= WATER_MASK) {
// hardcoded pool geometry
vCoord = vec3(aCoord.x, 0.0, aCoord.y) * uPosScale[1] + uPosScale[0];
vec4 cp = uViewProj * vec4(vCoord, 1.0);
@ -48,15 +49,14 @@ uniform sampler2D sNormal;
if (uType == WATER_CAUSTICS) {
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;
vec4 info = texture2D(sNormal, (rCoord.xz * 0.5 + 0.5) * uTexParam.zw);
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 + vec3(0.0, 1.0, 0.0);
vRefPos1 = rCoord + vec3(0.0, 0.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);
@ -64,76 +64,86 @@ uniform sampler2D sNormal;
vRefPos1 = vRefPos2 = vec3(0.0);
vCoord = vec3(aCoord.xy, 0.0);
gl_Position = vec4(aCoord.xyz, 1.0);
gl_Position = vec4(aCoord.xyz, 1.0);
}
}
}
#else
uniform sampler2D sDiffuse;
uniform sampler2D sReflect;
uniform vec4 uParam; // texture size
uniform sampler2D sEnvironment;
uniform vec3 uLightPos;
uniform vec4 uLightColor;
#define PI 3.141592653589793
vec3 cubeProj(vec3 ray, vec3 cubePos, vec3 cubeMin, vec3 cubeMax) {
vec3 i1 = (cubeMax - vCoord) / ray;
vec3 i2 = (cubeMin - vCoord) / ray;
vec3 i0 = max(i1, i2);
float dist = min(min(i0.x, i0.y), i0.z);
return vCoord + ray * dist - cubePos;
}
#define PI 3.141592653589793
float calcFresnel(float NdotL, float fbias, float fpow) {
float f = (1.0 - abs(NdotL));
float f = 1.0 - abs(NdotL);
return clamp(fbias + (1.0 - fbias) * pow(f, fpow), 0.0, 1.0);
}
vec4 drop() {
vec4 value = texture2D(sDiffuse, vTexCoord);
float drop = max(0.0, 1.0 - length(uParam.xy - vTexCoord) / uParam.z);
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;
value.r += drop * uParam.w;
return value;
v.x += drop * uParam.w;
return v * texture2D(sEnvironment, tc).x; // apply coast mask
}
float getHeight(float ref, vec2 tc) {
return mix(ref, texture2D(sDiffuse, tc).x, texture2D(sEnvironment, tc).x);
}
vec4 step() {
vec2 dx = vec2(uParam.x, 0.0);
vec2 dy = vec2(0.0, uParam.y);
vec2 tc = gl_FragCoord.xy * uTexParam.xy;
vec4 v = texture2D(sDiffuse, vTexCoord);
vec4 f = vec4(texture2D(sDiffuse, vTexCoord + dx).r, texture2D(sDiffuse, vTexCoord + dy).r,
texture2D(sDiffuse, vTexCoord - dx).r, texture2D(sDiffuse, vTexCoord - dy).r);
vec4 v = texture2D(sDiffuse, tc); // height, speed, normal.xz
/*
vec4 dx = vec4(0.25, 0.96, -0.25, -0.96) * uTexParam.xyxy + tc.xyxy;
vec4 dy = vec4(0.25, 0.96, -0.25, -0.96) * uTexParam.yxyx + tc.yxyx;
float average = (texture2D(sDiffuse, dy.yx).x +
texture2D(sDiffuse, dx.zy).x +
texture2D(sDiffuse, dy.wz).x +
texture2D(sDiffuse, dx.xw).x) * 0.25;
vec3 d = vec3(uTexParam.xy, 0.0);
vec2 f = vec2(texture2D(sDiffuse, tc + d.xz).x, texture2D(sDiffuse, tc + d.zy).x);
*/
vec3 d = vec3(uTexParam.xy, 0.0);
vec4 f = vec4(getHeight(v.x, tc + d.xz), getHeight(v.x, tc + d.zy),
getHeight(v.x, tc - d.xz), getHeight(v.x, tc - d.zy));
// 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));
// normal
f.xy -= v.r;
vec3 nx = vec3(uParam.x, f.x, 0.0);
vec3 ny = vec3(0.0, f.y, uParam.y);
v.ba = normalize(cross(ny, nx)).xz;
f.xy -= v.x;
vec3 nx = vec3(d.x, f.x, 0.0);
vec3 ny = vec3(0.0, f.y, d.y);
v.zw = normalize(cross(ny, nx)).xz * 0.5;
// velocity
v.g += (average - v.r) * 2.0;
v.g *= 0.995;
v.y += (average - v.x) * 1.9;
v.y *= uParam.x; // fadeout
// amplitude
v.r += v.g * 0.4;
v.x += v.y * uParam.y;
return v;
return v * texture2D(sEnvironment, tc).x; // apply coast mask
}
vec4 caustics() {
vec4 v = texture2D(sNormal, vTexCoord);
v.ba *= 0.5;
vec3 normal = vec3(v.b, sqrt(1.0 - dot(v.ba, v.ba)), v.a);
float area1 = length(dFdx(vRefPos1)) * length(dFdy(vRefPos1));
float area2 = length(dFdx(vRefPos2)) * length(dFdy(vRefPos2));
return vec4(vec3(area1 / area2 * 0.2), 1.0);
}
@ -146,7 +156,6 @@ uniform sampler2D sNormal;
vec4 value = texture2D(sNormal, vTexCoord);
value.ba *= 0.5;
vec3 normal = normalize(vec3(value.b, -sqrt(1.0 - dot(value.ba, value.ba)), value.a));
vec2 dudv = (uViewProj * vec4(normal.x, 0.0, normal.z, 0.0)).xy;