1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-03-13 23:59:41 +01:00

#8 trace obstacles for camera, #13 "look-at" triggers, #13 dart mesh, smoke & sparks sprite sequences, #15 split long time steps to 1/30 steps

This commit is contained in:
XProger 2016-09-27 01:22:16 +03:00
parent f22e45ad5f
commit ec4216f424
11 changed files with 533 additions and 297 deletions

Binary file not shown.

View File

@ -153,18 +153,17 @@ struct Frustum {
struct Camera : Controller {
Controller *owner;
Frustum *frustum;
Lara *owner;
Frustum *frustum;
float fov, znear, zfar;
vec3 target, destPos, lastDest, angleAdv;
float fov, znear, zfar;
vec3 target, destPos, lastDest, angleAdv;
int room;
int room;
Camera(TR::Level *level, Controller *owner) : Controller(level, owner->entity), owner(owner), frustum(new Frustum()) {
Camera(TR::Level *level, Lara *owner) : Controller(level, owner->entity), owner(owner), frustum(new Frustum()) {
fov = 75.0f;
znear = 0.1f * 2048.0f;
zfar = 1000.0f * 2048.0f;
znear = 128;
zfar = 100.0f * 1024.0f;
angleAdv = vec3(0.0f);
room = owner->getEntity().room;
@ -212,18 +211,28 @@ struct Camera : Controller {
angle = owner->angle + angleAdv;
angle.z = 0.0f;
//angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
target = vec3(owner->pos.x, owner->pos.y - height, owner->pos.z);
vec3 dir = getDir();
vec3 dir;
float lerpFactor = 2.0f;
if (owner->targetEntity > -1) {
TR::Entity &e = level->entities[owner->targetEntity];
dir = (vec3(e.x, e.y, e.z) - target).normal();
lerpFactor = 10.0f;
} else
dir = getDir();
if (owner->state != Lara::STATE_BACK_JUMP) {
destPos = target - dir * 1024.0f;
vec3 eye = target - dir * 1024.0f;
destPos = trace(owner->getRoomIndex(), target, eye);
lastDest = destPos;
} else
destPos = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f);
} else {
vec3 eye = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f);
destPos = trace(owner->getRoomIndex(), target, eye);
}
pos = pos.lerp(destPos, min(1.0f, Core::deltaTime * 2.0f));
pos = pos.lerp(destPos, min(1.0f, Core::deltaTime * lerpFactor));
TR::Level::FloorInfo info;
level->getFloorInfo(room, (int)pos.x, (int)pos.z, info);
@ -248,6 +257,62 @@ struct Camera : Controller {
}
}
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to) { // TODO: use Bresenham
int room = fromRoom;
vec3 pos = from, dir = to - from;
int px = (int)pos.x, py = (int)pos.y, pz = (int)pos.z;
float dist = dir.length();
dir = dir * (1.0f / dist);
int lx = -1, lz = -1;
TR::Level::FloorInfo info;
while (dist > 1.0f) {
int sx = px / 1024 * 1024 + 512,
sz = pz / 1024 * 1024 + 512;
if (lx != sx || lz != sz) {
level->getFloorInfo(room, sx, sz, info);
lx = sx;
lz = sz;
if (info.roomNext != 0xFF) room = info.roomNext;
}
if (py > info.floor && info.roomBelow != 0xFF)
room = info.roomBelow;
else if (py < info.ceiling && info.roomAbove != 0xFF)
room = info.roomAbove;
else if (py > info.floor || py < info.ceiling) {
int minX = px / 1024 * 1024;
int minZ = pz / 1024 * 1024;
int maxX = minX + 1024;
int maxZ = minZ + 1024;
pos = vec3(clamp(px, minX, maxX), pos.y, clamp(pz, minZ, maxZ)) + boxNormal(px, pz) * 256.0f;
dir = (pos - from).normal();
}
float d = min(dist, 128.0f); // STEP = 128
dist -= d;
pos = pos + dir * d;
px = (int)pos.x, py = (int)pos.y, pz = (int)pos.z;
}
return pos;
}
vec3 boxNormal(int x, int z) {
x %= 1024;
z %= 1024;
if (x > 1024 - z)
return x < z ? vec3(0, 0, 1) : vec3(1, 0, 0);
else
return x < z ? vec3(-1, 0, 0) : vec3(0, 0, -1);
}
void setup() {
Core::mViewInv = mat4(pos, target, vec3(0, -1, 0));
Core::mView = Core::mViewInv.inverse();

View File

@ -52,9 +52,9 @@ struct Controller {
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), health(100), turnTime(0.0f), nextAction(TR::Action::NONE, 0, 0.0f) {
TR::Entity &e = getEntity();
pos = vec3((float)e.x, (float)e.y, (float)e.z);
angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f);
angle = vec3(0.0f, e.rotation, 0.0f);
stand = STAND_GROUND;
animIndex = getModel().animation;
animIndex = e.modelIndex > 0 ? level->models[e.modelIndex - 1].animation : 0;
state = level->anims[animIndex].state;
}
@ -63,7 +63,7 @@ struct Controller {
e.x = int(pos.x);
e.y = int(pos.y);
e.z = int(pos.z);
e.rotation = int(angle.y / (PI * 0.5f) * 16384.0f);
e.rotation = angle.y;
}
bool insideRoom(const vec3 &pos, int room) const {
@ -80,15 +80,6 @@ struct Controller {
return level->entities[entity];
}
TR::Model& getModel() const {
TR::Entity &entity = getEntity();
for (int i = 0; i < level->modelsCount; i++)
if (entity.id == level->models[i].id)
return level->models[i];
ASSERT(false);
return level->models[0];
}
TR::Room& getRoom() const {
int index = getRoomIndex();
ASSERT(index >= 0 && index < level->roomsCount);
@ -337,7 +328,7 @@ struct Controller {
int idx = frame - anim->frameStart;
if (idx > animPrevFrame && idx <= frameIndex) {
if (getEntity().id != ENTITY_ENEMY_BAT) // temporary mute the bat
if (getEntity().id != TR::Entity::ENEMY_BAT) // temporary mute the bat
playSound(id);
}
break;
@ -375,4 +366,51 @@ struct Controller {
}
};
struct SpriteController : Controller {
enum {
FRAME_ANIMATED = -1,
FRAME_RANDOM = -2,
};
int frame;
bool instant, animated;
SpriteController(TR::Level *level, int entity, bool instant = true, int frame = FRAME_ANIMATED) : Controller(level, entity), instant(instant), animated(frame == FRAME_ANIMATED) {
if (frame >= 0) { // specific frame
this->frame = frame;
} else if (frame == FRAME_RANDOM) { // random frame
this->frame = rand() % getSequence().sCount;
} else if (frame == FRAME_ANIMATED) { // animated
this->frame = 0;
}
}
TR::SpriteSequence& getSequence() {
return level->spriteSequences[-(getEntity().modelIndex + 1)];
}
void update() {
bool remove = false;
animTime += Core::deltaTime;
if (animated) {
frame = int(animTime * 10.0f);
TR::SpriteSequence &seq = getSequence();
if (instant && frame >= seq.sCount)
remove = true;
else
frame %= seq.sCount;
} else
if (instant && animTime >= 0.1f)
remove = true;
if (remove) {
level->entityRemove(entity);
delete this;
}
}
};
#endif

View File

@ -195,19 +195,19 @@ namespace Debug {
cmd = (*fd++).cmd;
switch (cmd.func) {
case TR::FD_PORTAL :
case TR::FloorData::PORTAL :
isPortal = true;
fd++;
break; // portal
case TR::FD_FLOOR : // floor & ceiling
case TR::FD_CEILING : {
case TR::FloorData::FLOOR : // floor & ceiling
case TR::FloorData::CEILING : {
TR::FloorData::Slant slant = (*fd++).slant;
int sx = 256 * (int)slant.x;
int sz = 256 * (int)slant.z;
auto &p = cmd.func == 0x02 ? vf : vc;
if (cmd.func == 0x02) { // floor
if (cmd.func == TR::FloorData::FLOOR) { // floor
if (sx > 0) {
p[0].y += sx;
p[3].y += sx;
@ -242,28 +242,12 @@ namespace Debug {
}
break;
}
case TR::FD_TRIGGER : {
case TR::FloorData::TRIGGER : {
TR::FloorData::TriggerInfo info = (*fd++).triggerInfo;
TR::FloorData::TriggerCommand trigCmd;
glColor3f(1, 0, 1);
do {
trigCmd = (*fd++).triggerCmd; // trigger action
switch (trigCmd.func) {
case 0 : break; // activate item
case 1 : break; // switch to camera
case 2 : break; // camera delay
case 3 : break; // flip map
case 4 : break; // flip on
case 5 : break; // flip off
case 6 : break; // look at item
case 7 : break; // end level
case 8 : break; // play soundtrack
case 9 : break; // special hadrdcode trigger
case 10 : break; // secret found
case 11 : break; // clear bodies
case 12 : break; // flyby camera sequence
case 13 : break; // play cutscene
}
} while (!trigCmd.end);
break;
}
@ -468,7 +452,7 @@ namespace Debug {
matrix.rotateX(controller->angle.x);
matrix.rotateZ(controller->angle.z);
} else
matrix.rotateY(e.rotation / 16384.0f * PI * 0.5f);
matrix.rotateY(e.rotation);
for (int j = 0; j < level.modelsCount; j++) {
TR::Model &m = level.models[j];

View File

@ -5,6 +5,8 @@
#define TR1_DEMO
#define MAX_RESERVED_ENTITIES 64
namespace TR {
enum : int32 {
@ -12,14 +14,6 @@ namespace TR {
ROOM_FLAG_VISIBLE = 0x8000
};
enum {
FD_PORTAL = 1,
FD_FLOOR = 2,
FD_CEILING = 3,
FD_TRIGGER = 4,
FD_KILL = 5,
};
enum {
ANIM_CMD_MOVE = 1,
ANIM_CMD_SPEED = 2,
@ -56,7 +50,7 @@ namespace TR {
NONE = -1, // no action
ACTIVATE = 0, // activate item
CAMERA_SWITCH = 1, // switch to camera
CAMERA_DELAY = 2, // camera delay
UNDERWATER = 2, // underwater flow
FLIP_MAP = 3, // flip map
FLIP_ON = 4, // flip on
FLIP_OFF = 5, // flip off
@ -70,7 +64,6 @@ namespace TR {
CUTSCENE = 13, // play cutscene
};
#define DATA_PORTAL 0x01
#define DATA_FLOOR 0x02
#define DATA_CEILING 0x03
@ -79,67 +72,6 @@ namespace TR {
#define ENTITY_FLAG_VISIBLE 0x0100
#define ENTITY_FLAG_ACTIVE 0x3E00
#define ENTITY_LARA 0
#define ENTITY_LARA_CUT 77
#define ENTITY_ENEMY_TWIN 6
#define ENTITY_ENEMY_WOLF 7
#define ENTITY_ENEMY_BEAR 8
#define ENTITY_ENEMY_BAT 9
#define ENTITY_ENEMY_CROCODILE_LAND 10
#define ENTITY_ENEMY_CROCODILE_WATER 11
#define ENTITY_ENEMY_LION_MALE 12
#define ENTITY_ENEMY_LION_FEMALE 13
#define ENTITY_ENEMY_PUMA 14
#define ENTITY_ENEMY_GORILLA 15
#define ENTITY_ENEMY_RAT_LAND 16
#define ENTITY_ENEMY_RAT_WATER 17
#define ENTITY_ENEMY_REX 18
#define ENTITY_ENEMY_RAPTOR 19
#define ENTITY_ENEMY_MUTANT 20
#define ENTITY_ENEMY_CENTAUR 23
#define ENTITY_ENEMY_MUMMY 24
#define ENTITY_ENEMY_LARSON 27
#define ENTITY_TRAP_FLOOR 35
#define ENTITY_TRAP_BLADE 36
#define ENTITY_TRAP_SPIKES 37
#define ENTITY_TRAP_STONE 38
#define ENTITY_TRAP_DART 39
#define ENTITY_TRAP_DARTGUN 40
#define ENTITY_CRYSTAL 83
#define ENTITY_MEDIKIT_SMALL 93
#define ENTITY_MEDIKIT_BIG 94
#define ENTITY_SWITCH 55
#define ENTITY_SWITCH_WATER 56
#define ENTITY_DOOR_1 57
#define ENTITY_DOOR_2 58
#define ENTITY_DOOR_3 59
#define ENTITY_DOOR_4 60
#define ENTITY_DOOR_BIG_1 61
#define ENTITY_DOOR_BIG_2 62
#define ENTITY_DOOR_5 63
#define ENTITY_DOOR_6 64
#define ENTITY_DOOR_FLOOR_1 65
#define ENTITY_DOOR_FLOOR_2 66
#define ENTITY_GUN_SHOTGUN 85
#define ENTITY_AMMO_UZI 91
#define ENTITY_AMMO_SHOTGUN 89
#define ENTITY_AMMO_MAGNUM 90
#define ENTITY_HOLE_PUZZLE 118
#define ENTITY_HOLE_KEY 137
#define ENTITY_VIEW_TARGET 169
#pragma pack(push, 1)
struct fixed {
@ -150,6 +82,14 @@ namespace TR {
}
};
struct angle {
uint16 value;
angle() {}
angle(float value) : value(uint16(value / (PI * 0.5f) * 16384.0f)) {}
operator float() const { return value / 16384.0f * PI * 0.5f; };
};
struct RGB {
uint8 r, g, b;
};
@ -240,7 +180,7 @@ namespace TR {
struct Mesh {
int32 x, y, z;
uint16 rotation;
angle rotation;
uint16 intensity;
uint16 meshID;
uint16 flags; // ! not exists in file !
@ -259,8 +199,16 @@ namespace TR {
uint16 timer:8, once:1, mask:5, :2;
} triggerInfo;
struct TriggerCommand {
uint16 args:10, func:5, end:1;
uint16 args:10, action:5, end:1;
} triggerCmd;
enum {
PORTAL = 1,
FLOOR = 2,
CEILING = 3,
TRIGGER = 4,
KILL = 5,
};
};
struct Overlap {
@ -302,14 +250,92 @@ namespace TR {
int16 id; // Object Identifier (matched in Models[], or SpriteSequences[], as appropriate)
int16 room; // which room contains this item
int32 x, y, z; // world coords
int16 rotation; // ((0xc000 >> 14) * 90) degrees
angle rotation; // ((0xc000 >> 14) * 90) degrees
int16 intensity; // (constant lighting; -1 means use mesh lighting)
uint16 flags; // 0x0100 indicates "initially invisible", 0x3e00 is Activation Mask
// 0x3e00 indicates "open" or "activated"; these can be XORed with
// related FloorData::FDlist fields (e.g. for switches)
// not exists in file
uint16 align;
int16 modelIndex; // index of representation in models (index + 1) or spriteSequences (-(index + 1)) arrays
void *controller; // Controller implementation or NULL
enum {
LARA = 0,
ENEMY_TWIN = 6,
ENEMY_WOLF = 7,
ENEMY_BEAR = 8,
ENEMY_BAT = 9,
ENEMY_CROCODILE_LAND = 10,
ENEMY_CROCODILE_WATER = 11,
ENEMY_LION_MALE = 12,
ENEMY_LION_FEMALE = 13,
ENEMY_PUMA = 14,
ENEMY_GORILLA = 15,
ENEMY_RAT_LAND = 16,
ENEMY_RAT_WATER = 17,
ENEMY_REX = 18,
ENEMY_RAPTOR = 19,
ENEMY_MUTANT = 20,
ENEMY_CENTAUR = 23,
ENEMY_MUMMY = 24,
ENEMY_LARSON = 27,
TRAP_FLOOR = 35,
TRAP_BLADE = 36,
TRAP_SPIKES = 37,
TRAP_STONE = 38,
TRAP_DART = 39,
TRAP_DARTGUN = 40,
SWITCH = 55,
SWITCH_WATER = 56,
DOOR_1 = 57,
DOOR_2 = 58,
DOOR_3 = 59,
DOOR_4 = 60,
DOOR_BIG_1 = 61,
DOOR_BIG_2 = 62,
DOOR_5 = 63,
DOOR_6 = 64,
DOOR_FLOOR_1 = 65,
DOOR_FLOOR_2 = 66,
LARA_CUT = 77,
CRYSTAL = 83, // sprite
WEAPON_PISTOLS = 84, // sprite
WEAPON_SHOTGUN = 85, // sprite
WEAPON_MAGNUMS = 86, // sprite
WEAPON_UZIS = 87, // sprite
AMMO_SHOTGUN = 89, // sprite
AMMO_MAGNUMS = 90, // sprite
AMMO_UZIS = 91, // sprite
MEDIKIT_SMALL = 93, // sprite
MEDIKIT_BIG = 94, // sprite
HOLE_PUZZLE = 118,
HOLE_KEY = 137,
ARTIFACT = 143, // sprite
WATER_SPLASH = 153, // sprite
BUBBLE = 155, // sprite
BLOOD = 158, // sprite
SMOKE = 160, // sprite
SPARK = 164, // sprite
VIEW_TARGET = 169,
GLYPH = 190, // sprite
};
};
struct Animation {
@ -396,8 +422,8 @@ namespace TR {
MinMax box[2]; // visible (minX, maxX, minY, maxY, minZ, maxZ) & collision
uint16 flags;
void getBox(bool collision, int rotation, vec3 &min, vec3 &max) {
int k = rotation / 16384;
void getBox(bool collision, angle rotation, vec3 &min, vec3 &max) {
int k = rotation.value / 0x4000;
MinMax &m = box[collision];
@ -574,6 +600,7 @@ namespace TR {
int32 animTexturesDataSize;
uint16 *animTexturesData;
int32 entitiesBaseCount;
int32 entitiesCount;
Entity *entities;
@ -666,6 +693,8 @@ namespace TR {
stream.read(objectTextures, stream.read(objectTexturesCount));
stream.read(spriteTextures, stream.read(spriteTexturesCount));
stream.read(spriteSequences, stream.read(spriteSequencesCount));
for (int i = 0; i < spriteSequencesCount; i++)
spriteSequences[i].sCount = -spriteSequences[i].sCount;
#ifdef TR1_DEMO
stream.read(palette, 256);
@ -682,12 +711,17 @@ namespace TR {
// animated textures
stream.read(animTexturesData, stream.read(animTexturesDataSize));
// entities (enemies, items, lara etc.)
entities = new Entity[stream.read(entitiesCount)];
for (int i = 0; i < entitiesCount; i++) {
stream.raw(&entities[i], sizeof(entities[i]) - sizeof(entities[i].align) - sizeof(entities[i].controller));
entities[i].align = 0;
entities[i].controller = NULL;
entitiesCount = stream.read(entitiesBaseCount) + MAX_RESERVED_ENTITIES;
entities = new Entity[entitiesCount];
for (int i = 0; i < entitiesBaseCount; i++) {
Entity &e = entities[i];
stream.raw(&e, sizeof(e) - sizeof(e.align) - sizeof(e.controller) - sizeof(e.modelIndex));
e.align = 0;
e.controller = NULL;
e.modelIndex = getModelIndex(e.id);
}
for (int i = entitiesBaseCount; i < entitiesCount; i++)
entities[i].id = -1;
// palette
stream.seek(32 * 256); // skip lightmap palette
@ -769,6 +803,43 @@ namespace TR {
return NULL;
}
int16 getModelIndex(int16 id) const {
for (int i = 0; i < modelsCount; i++)
if (id == models[i].id)
return i + 1;
for (int i = 0; i < spriteSequencesCount; i++)
if (id == spriteSequences[i].id)
return -(i + 1);
ASSERT(false);
return 0;
}
int entityAdd(int16 id, int16 room, int32 x, int32 y, int32 z, angle rotation, int16 intensity) {
int entityIndex = -1;
for (int i = entitiesBaseCount; i < entitiesCount; i++)
if (entities[i].id == -1) {
Entity &e = entities[i];
e.id = id;
e.room = room;
e.x = x;
e.y = y;
e.z = z;
e.rotation = rotation;
e.intensity = intensity;
e.flags = 0;
e.modelIndex = getModelIndex(e.id);
e.controller = NULL;
return i;
}
return -1;
}
void entityRemove(int entityIndex) {
entities[entityIndex].id = -1;
}
struct FloorInfo {
int floor, ceiling;
int roomNext, roomBelow, roomAbove;
@ -822,16 +893,16 @@ namespace TR {
switch (cmd.func) {
case FD_PORTAL :
case FloorData::PORTAL :
info.roomNext = (*fd++).data;
break;
case FD_FLOOR : // floor & ceiling
case FD_CEILING : {
case FloorData::FLOOR : // floor & ceiling
case FloorData::CEILING : {
FloorData::Slant slant = (*fd++).slant;
int sx = (int)slant.x;
int sz = (int)slant.z;
if (cmd.func == FD_FLOOR) {
if (cmd.func == FloorData::FLOOR) {
info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2;
info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
} else {
@ -841,7 +912,7 @@ namespace TR {
break;
}
case FD_TRIGGER : {
case FloorData::TRIGGER : {
info.trigger = cmd.sub;
info.trigCmdCount = 0;
info.trigInfo = (*fd++).triggerInfo;
@ -849,28 +920,14 @@ namespace TR {
do {
trigCmd = (*fd++).triggerCmd; // trigger action
info.trigCmd[info.trigCmdCount++] = trigCmd;
switch (trigCmd.func) {
case 0 : break; // activate item
case 1 : break; // switch to camera
case 2 : break; // camera delay
case 3 : break; // flip map
case 4 : break; // flip on
case 5 : break; // flip off
case 6 : break; // look at item
case 7 : break; // end level
case 8 : break; // play soundtrack
case 9 : break; // special hadrdcode trigger
case 10 : break; // secret found (playSound(175))
case 11 : break; // clear bodies
case 12 : break; // flyby camera sequence
case 13 : break; // play cutscene
if (trigCmd.action == Action::CAMERA_SWITCH) {
}
// ..
} while (!trigCmd.end);
break;
}
case FD_KILL :
case FloorData::KILL :
info.kill = true;
break;

View File

@ -17,7 +17,7 @@
struct Lara : Controller {
// http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left
enum LaraAnim : int32 {
enum {
ANIM_STAND = 11,
ANIM_FALL = 34,
ANIM_SMASH_JUMP = 32,
@ -35,7 +35,7 @@ struct Lara : Controller {
};
// http://www.tombraiderforums.com/showthread.php?t=211681
enum LaraState : int32 {
enum {
STATE_WALK,
STATE_RUN,
STATE_STOP,
@ -94,7 +94,10 @@ struct Lara : Controller {
STATE_WATER_OUT,
STATE_MAX };
Lara(TR::Level *level, int entity) : Controller(level, entity) {
int targetEntity;
float targetTimer;
Lara(TR::Level *level, int entity) : Controller(level, entity), targetEntity(-1), targetTimer(0.0f) {
/*
// level 2 (pool)
pos = vec3(70067, -256, 29104);
@ -195,6 +198,7 @@ struct Lara : Controller {
return;
break;
default :
LOG("unsupported trigger type %d\n", info.trigger);
return;
}
@ -203,25 +207,38 @@ struct Lara : Controller {
// build trigger activation chain
for (int i = 0; i < info.trigCmdCount; i++) {
if (info.trigCmd[i].func != TR::Action::ACTIVATE) continue; // TODO: other trigger types
Controller *controller = (Controller*)level->entities[info.trigCmd[i].args].controller;
if (!controller) {
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].id);
playSound(2);
return;
} else
controller->nextAction = (i < info.trigCmdCount - 1) ? Action(TR::Action::ACTIVATE, info.trigCmd[i + 1].args, 0.0f) : Action(TR::Action::NONE, 0, 0.0f);
TR::FloorData::TriggerCommand &cmd = info.trigCmd[i];
switch (cmd.action) {
case TR::Action::ACTIVATE : {
Controller *controller = (Controller*)level->entities[cmd.args].controller;
if (!controller) {
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].id);
playSound(2);
return;
} else
controller->nextAction = (i < info.trigCmdCount - 1) ? Action(TR::Action::ACTIVATE, info.trigCmd[i + 1].args, 0.0f) : Action(TR::Action::NONE, 0, 0.0f);
break;
}
case TR::Action::LOOK_AT :
level->entities[cmd.args].flags |= ENTITY_FLAG_ACTIVE;
targetEntity = cmd.args;
targetTimer = info.trigInfo.timer;
break;
default :
LOG("unsupported trigger action %d\n", cmd.action);
return;
}
}
if (info.trigCmd[0].func != TR::Action::ACTIVATE) return; // see above TODO
// activate first entity in chain
Controller *controller = (Controller*)level->entities[info.trigCmd[0].args].controller;
if (info.trigger == TR::TRIGGER_KEY) {
nextAction = controller->nextAction;
controller->nextAction.action = TR::Action::NONE;
} else
controller->activate((float)info.trigInfo.timer);
if (controller) {
if (info.trigger == TR::TRIGGER_KEY) {
nextAction = controller->nextAction;
controller->nextAction.action = TR::Action::NONE;
} else
controller->activate((float)info.trigInfo.timer);
}
}
virtual Stand getStand() {
@ -381,6 +398,11 @@ struct Lara : Controller {
}
virtual void updateState() {
if (targetTimer > 0.0f && (targetTimer -= Core::deltaTime) <= 0.0f) {
targetEntity = -1;
targetTimer = 0.0f;
}
performTrigger();
TR::Animation *anim = &level->anims[animIndex];

View File

@ -25,7 +25,7 @@ struct Level {
Texture *atlas;
MeshBuilder *mesh;
Controller *lara;
Lara *lara;
Camera *camera;
float time;
@ -43,60 +43,59 @@ struct Level {
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &entity = level.entities[i];
switch (entity.id) {
case ENTITY_LARA :
case ENTITY_LARA_CUT :
case TR::Entity::LARA :
case TR::Entity::LARA_CUT :
entity.controller = (lara = new Lara(&level, i));
break;
case ENTITY_ENEMY_WOLF :
case TR::Entity::ENEMY_WOLF :
entity.controller = new Wolf(&level, i);
break;
case ENTITY_ENEMY_BEAR :
case TR::Entity::ENEMY_BEAR :
entity.controller = new Bear(&level, i);
break;
case ENTITY_ENEMY_BAT :
case TR::Entity::ENEMY_BAT :
entity.controller = new Bat(&level, i);
break;
case ENTITY_ENEMY_TWIN :
case ENTITY_ENEMY_CROCODILE_LAND :
case ENTITY_ENEMY_CROCODILE_WATER :
case ENTITY_ENEMY_LION_MALE :
case ENTITY_ENEMY_LION_FEMALE :
case ENTITY_ENEMY_PUMA :
case ENTITY_ENEMY_GORILLA :
case ENTITY_ENEMY_RAT_LAND :
case ENTITY_ENEMY_RAT_WATER :
case ENTITY_ENEMY_REX :
case ENTITY_ENEMY_RAPTOR :
case ENTITY_ENEMY_MUTANT :
case ENTITY_ENEMY_CENTAUR :
case ENTITY_ENEMY_MUMMY :
case ENTITY_ENEMY_LARSON :
case TR::Entity::ENEMY_TWIN :
case TR::Entity::ENEMY_CROCODILE_LAND :
case TR::Entity::ENEMY_CROCODILE_WATER :
case TR::Entity::ENEMY_LION_MALE :
case TR::Entity::ENEMY_LION_FEMALE :
case TR::Entity::ENEMY_PUMA :
case TR::Entity::ENEMY_GORILLA :
case TR::Entity::ENEMY_RAT_LAND :
case TR::Entity::ENEMY_RAT_WATER :
case TR::Entity::ENEMY_REX :
case TR::Entity::ENEMY_RAPTOR :
case TR::Entity::ENEMY_MUTANT :
case TR::Entity::ENEMY_CENTAUR :
case TR::Entity::ENEMY_MUMMY :
case TR::Entity::ENEMY_LARSON :
entity.controller = new Enemy(&level, i);
break;
case ENTITY_DOOR_1 :
case ENTITY_DOOR_2 :
case ENTITY_DOOR_3 :
case ENTITY_DOOR_4 :
case ENTITY_DOOR_5 :
case ENTITY_DOOR_6 :
case ENTITY_DOOR_BIG_1 :
case ENTITY_DOOR_BIG_2 :
case ENTITY_DOOR_FLOOR_1 :
case ENTITY_DOOR_FLOOR_2 :
case ENTITY_TRAP_FLOOR :
case ENTITY_TRAP_BLADE :
case ENTITY_TRAP_SPIKES :
case ENTITY_TRAP_STONE :
//case ENTITY_TRAP_DART :
case TR::Entity::DOOR_1 :
case TR::Entity::DOOR_2 :
case TR::Entity::DOOR_3 :
case TR::Entity::DOOR_4 :
case TR::Entity::DOOR_5 :
case TR::Entity::DOOR_6 :
case TR::Entity::DOOR_BIG_1 :
case TR::Entity::DOOR_BIG_2 :
case TR::Entity::DOOR_FLOOR_1 :
case TR::Entity::DOOR_FLOOR_2 :
case TR::Entity::TRAP_FLOOR :
case TR::Entity::TRAP_BLADE :
case TR::Entity::TRAP_SPIKES :
case TR::Entity::TRAP_STONE :
entity.controller = new Trigger(&level, i, true);
break;
case ENTITY_TRAP_DARTGUN :
entity.controller = new Dart(&level, i);
case TR::Entity::TRAP_DARTGUN :
entity.controller = new Dartgun(&level, i);
break;
case ENTITY_SWITCH :
case ENTITY_SWITCH_WATER :
case ENTITY_HOLE_PUZZLE :
case ENTITY_HOLE_KEY :
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);
break;
}
@ -111,7 +110,8 @@ struct Level {
Debug::free();
#endif
for (int i = 0; i < level.entitiesCount; i++)
delete (Controller*)level.entities[i].controller;
if (level.entities[i].id > -1)
delete (Controller*)level.entities[i].controller;
for (int i = 0; i < shMAX; i++)
delete shaders[i];
@ -246,7 +246,7 @@ struct Level {
// render static mesh
mat4 mTemp = Core::mModel;
Core::mModel.translate(offset);
Core::mModel.rotateY(rMesh.rotation / 16384.0f * PI * 0.5f);
Core::mModel.rotateY(rMesh.rotation);
renderMesh(sMesh->mesh);
Core::mModel = mTemp;
}
@ -365,7 +365,7 @@ struct Level {
fTime = controller->animTime;
} else {
anim = &level.anims[model.animation];
angle = vec3(0.0f, entity.rotation / 16384.0f * PI * 0.5f, 0.0f);
angle = vec3(0.0f, entity.rotation, 0.0f);
fTime = time;
}
@ -436,6 +436,20 @@ struct Level {
}
}
void renderSequence(const TR::Entity &entity) {
shaders[shSprite]->bind();
Core::active.shader->setParam(uModel, Core::mModel);
Core::active.shader->setParam(uColor, Core::color);
int sIndex = -(entity.modelIndex + 1);
int sFrame;
if (entity.controller)
sFrame = ((SpriteController*)entity.controller)->frame;
else
sFrame = int(time * 10.0f) % level.spriteSequences[sIndex].sCount;
mesh->renderSprite(sIndex, sFrame);
}
int getLightIndex(const vec3 &pos, int &room) {
int idx = -1;
float dist;
@ -474,6 +488,8 @@ struct Level {
}
void renderEntity(const TR::Entity &entity) {
if (entity.id < 0) return;
TR::Room &room = level.rooms[entity.room];
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) // check for room visibility
return;
@ -491,42 +507,27 @@ struct Level {
// get light parameters for entity
getLight(vec3(entity.x, entity.y, entity.z), entity.room);
// render entity models (TODO: remapping or consider model and entity id's)
bool isModel = false;
for (int i = 0; i < level.modelsCount; i++)
if (entity.id == level.models[i].id) {
isModel = true;
renderModel(level.models[i], entity);
break;
}
// render entity models
if (entity.modelIndex > 0)
renderModel(level.models[entity.modelIndex - 1], entity);
// if entity is billboard
if (!isModel) {
if (entity.modelIndex < 0) {
Core::color = vec4(c, c, c, 1.0f);
shaders[shSprite]->bind();
Core::active.shader->setParam(uModel, Core::mModel);
Core::active.shader->setParam(uColor, Core::color);
for (int i = 0; i < level.spriteSequencesCount; i++)
if (entity.id == level.spriteSequences[i].id) {
mesh->renderSprite(i);
break;
}
renderSequence(entity);
}
Core::mModel = m;
}
float tickTextureAnimation = 0.0f;
void update() {
time += Core::deltaTime;
for (int i = 0; i < level.entitiesCount; i++) {
Controller *controller = (Controller*)level.entities[i].controller;
if (controller)
controller->update();
}
for (int i = 0; i < level.entitiesCount; i++)
if (level.entities[i].id > -1) {
Controller *controller = (Controller*)level.entities[i].controller;
if (controller)
controller->update();
}
camera->update();
}
@ -578,6 +579,28 @@ struct Level {
for (int i = 0; i < level.entitiesCount; i++)
renderEntity(level.entities[i]);
/*
static int modelIndex = 0;
static bool lastStateK = false;
if (Input::down[ikM]) {
if (!lastStateK) {
lastStateK = true;
// modelIndex = (modelIndex + 1) % level.modelsCount;
modelIndex = (modelIndex + 1) % level.spriteSequencesCount;
LOG("model: %d %d\n", modelIndex, level.spriteSequences[modelIndex].id);
}
} else
lastStateK = false;
Core::mModel.translate(lara->pos + vec3(512, -512, 0));
//renderModel(level.models[modelIndex], level.entities[4]);
TR::Entity seq;
seq.modelIndex = -(modelIndex + 1);
seq.controller = NULL;
Core::color = vec4(1.0f);
renderSequence(seq);
*/
#ifdef _DEBUG
Debug::begin();
Debug::Level::rooms(level, lara->pos, lara->getEntity().room);

View File

@ -74,7 +74,7 @@ struct MeshBuilder {
int mCount;
// sprite sequences
MeshRange *spriteRanges;
MeshRange *sequenceRanges;
// indexed mesh
Mesh *mesh;
@ -160,14 +160,14 @@ struct MeshBuilder {
meshInfo = new MeshInfo[mCount];
// get size of mesh for sprite sequences
spriteRanges = new MeshRange[level.spriteSequencesCount];
sequenceRanges = new MeshRange[level.spriteSequencesCount];
for (int i = 0; i < level.spriteSequencesCount; i++) {
// TODO: sequences not only first frame
spriteRanges[i].vStart = vCount;
spriteRanges[i].iStart = iCount;
spriteRanges[i].iCount = 6;
iCount += 6;
vCount += 4;
sequenceRanges[i].vStart = vCount;
sequenceRanges[i].iStart = iCount;
sequenceRanges[i].iCount = level.spriteSequences[i].sCount * 6;
iCount += level.spriteSequences[i].sCount * 6;
vCount += level.spriteSequences[i].sCount * 4;
}
// make meshes buffer (single vertex buffer object for all geometry & sprites on level)
@ -363,10 +363,11 @@ struct MeshBuilder {
}
// build sprite sequences
for (int i = 0; i < level.spriteSequencesCount; i++) {
TR::SpriteTexture &sprite = level.spriteTextures[level.spriteSequences[i].sStart];
addSprite(indices, vertices, iCount, vCount, vCount, 0, -16, 0, sprite, 255);
}
for (int i = 0; i < level.spriteSequencesCount; i++)
for (int j = 0; j < level.spriteSequences[i].sCount; j++) {
TR::SpriteTexture &sprite = level.spriteTextures[level.spriteSequences[i].sStart + j];
addSprite(indices, vertices, iCount, vCount, vCount, 0, 0, 0, sprite, 255);
}
mesh = new Mesh(indices, iCount, vertices, vCount);
delete[] indices;
@ -378,7 +379,7 @@ struct MeshBuilder {
delete[] animTexOffsets;
delete[] roomRanges;
delete[] meshInfo;
delete[] spriteRanges;
delete[] sequenceRanges;
delete mesh;
}
@ -543,8 +544,12 @@ struct MeshBuilder {
renderMesh(&meshInfo[meshIndex]);
}
void renderSprite(int spriteIndex) {
mesh->render(spriteRanges[spriteIndex]);
void renderSprite(int sequenceIndex, int frame) {
MeshRange range = sequenceRanges[sequenceIndex];
range.iCount = 6;
range.iStart += frame * 6;
range.vStart += frame * 4;
mesh->render(range);
}
};

View File

@ -49,35 +49,73 @@ struct Trigger : Controller {
}
if (!inState())
setState(state != baseState ? baseState : (entity.id == ENTITY_TRAP_BLADE ? 2 : (baseState ^ 1)));
setState(state != baseState ? baseState : (entity.id == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
updateAnimation(true);
updateEntity();
}
};
struct Dart : Trigger {
vec3 origin;
struct Dart : Controller {
Dart(TR::Level *level, int entity) : Trigger(level, entity, true), origin(pos) {}
vec3 dir;
bool inWall; // dart starts from wall
virtual bool activate(float timer) {
bool res = Trigger::activate(timer);
if (res)
playSound(151);
return res;
Dart(TR::Level *level, int entity) : Controller(level, entity), inWall(true) {
dir = vec3(sinf(angle.y), 0, cosf(angle.y));
}
virtual void update() {
Trigger::update();
if (state != baseState) {
pos = origin + vec3(angle.x, angle.y) * (animTime * 4096.0f);
updateEntity();
} else {
pos = origin;
updateEntity();
}
velocity = dir * level->anims[animIndex].speed;
pos = pos + velocity * (Core::deltaTime * 30.0f);
updateEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info);
if (pos.y > info.floor || pos.y < info.ceiling || !insideRoom(pos, getRoomIndex())) {
if (!inWall) {
TR::Entity &e = getEntity();
vec3 p = pos - dir * 64.0f; // wall offset = 64
int sparkIndex = level->entityAdd(TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, e.rotation, -1);
if (sparkIndex > -1)
level->entities[sparkIndex].controller = new SpriteController(level, sparkIndex, true, SpriteController::FRAME_RANDOM);
level->entityRemove(entity);
delete this;
}
} else
inWall = false;
}
};
struct Dartgun : Trigger {
vec3 origin;
Dartgun(TR::Level *level, int entity) : Trigger(level, entity, true), origin(pos) {}
virtual bool activate(float timer) {
if (!Trigger::activate(timer))
return false;
// add dart (bullet)
TR::Entity &entity = getEntity();
vec3 pos = vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
pos = pos + vec3(entity.x, entity.y, entity.z);
int dartIndex = level->entityAdd(TR::Entity::TRAP_DART, entity.room, (int)pos.x, (int)pos.y, (int)pos.z, entity.rotation, entity.intensity);
if (dartIndex > -1)
level->entities[dartIndex].controller = new Dart(level, dartIndex);
int smokeIndex = level->entityAdd(TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z, entity.rotation, -1);
if (smokeIndex > -1)
level->entities[smokeIndex].controller = new SpriteController(level, smokeIndex);
playSound(151);
return true;
}
};
#endif

View File

@ -3,7 +3,7 @@
#include "game.h"
int lastTime, fpsTime, FPS;
int lastTime, fpsTime, fps;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
@ -18,23 +18,28 @@ void main_loop() {
if (time - lastTime <= 0)
return;
Core::deltaTime = (time - lastTime) * 0.001f;
lastTime = time;
if (time > fpsTime) {
fpsTime = time + 1000;
LOG("FPS: %d\n", FPS);
FPS = 0;
float delta = (time - lastTime) * 0.001f;
while (delta > EPS) {
Core::deltaTime = min(delta, 1.0f / 30.0f);
Game::update();
delta -= Core::deltaTime;
}
lastTime = time;
int f;
emscripten_get_canvas_size(&Core::width, &Core::height, &f);
Game::update();
Core::stats.dips = 0;
Core::stats.tris = 0;
Game::render();
eglSwapBuffers(display, surface);
FPS++;
if (fpsTime < getTime()) {
LOG("FPS: %d DIP: %d TRI: %d\n", fps, Core::stats.dips, Core::stats.tris);
fps = 0;
fpsTime = getTime() + 1000;
} else
fps++;
}
bool initGL() {
@ -182,7 +187,7 @@ int main() {
lastTime = getTime();
fpsTime = lastTime + 1000;
FPS = 0;
fps = 0;
emscripten_set_main_loop(main_loop, 0, true);

View File

@ -196,33 +196,31 @@ int main() {
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&WndProc);
ShowWindow(hWnd, SW_SHOWDEFAULT);
DWORD time, lastTime = getTime();
DWORD time, lastTime = getTime(), fpsTime = lastTime + 1000, fps = 0;
MSG msg;
msg.message = WM_PAINT;
DWORD fps = 0, fpsTime = getTime() + 1000;
while (msg.message != WM_QUIT)
do {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
joyUpdate();
time = getTime();
if (time <= lastTime)
continue;
Core::deltaTime = (time - lastTime) * 0.001f;
float delta = (time - lastTime) * 0.001f;
while (delta > EPS) {
Core::deltaTime = min(delta, 1.0f / 30.0f);
Game::update();
delta -= Core::deltaTime;
}
lastTime = time;
joyUpdate();
Core::stats.dips = 0;
Core::stats.tris = 0;
Game::update();
Game::render();
SwapBuffers(hDC);
if (fpsTime < getTime()) {
@ -232,6 +230,7 @@ int main() {
} else
fps++;
}
} while (msg.message != WM_QUIT);
Game::free();
freeGL(hRC);