1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-04-21 03:21:51 +02:00

#3 №14 separated lara.h & enemy.h for controllers, add basic enemy controllers, add roll animation for lara, add text in debug mode, #8 smooth following camera, side view while backflip

This commit is contained in:
XProger 2016-09-12 03:26:16 +03:00
parent b80c2790d0
commit 071081b612
11 changed files with 861 additions and 659 deletions

Binary file not shown.

View File

@ -3,10 +3,10 @@
#include "core.h"
#include "controller.h"
#include "lara.h"
#define MAX_CLIP_PLANES 10
struct Frustum {
struct Poly {
@ -47,39 +47,8 @@ struct Frustum {
planes[i].w = -(pos.dot(planes[i].xyz));
e1 = e2;
}
#ifdef _DEBUG
dbg++;
debugPoly = poly;
#endif
}
#ifdef _DEBUG
void debug() {
if (debugPoly.count < 3) return;
glUseProgram(0);
Core::setBlending(bmAdd);
glDisable(GL_DEPTH_TEST);
glBegin(GL_TRIANGLES);
for (int i = 0; i < debugPoly.count; i++) {
glVertex3fv((GLfloat*)&pos);
glVertex3fv((GLfloat*)&debugPoly.vertices[i]);
glVertex3fv((GLfloat*)&debugPoly.vertices[(i + 1) % debugPoly.count]);
}
glEnd();
glColor3f(0, 1, 0);
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= debugPoly.count; i++) {
glVertex3fv((GLfloat*)&debugPoly.vertices[i % debugPoly.count]);
}
glEnd();
glEnable(GL_DEPTH_TEST);
Core::setBlending(bmAlpha);
}
#endif
void clipPlane(const Poly &src, Poly &dst, const vec4 &plane) {
dst.count = 0;
if (!src.count) return;
@ -168,7 +137,7 @@ struct Camera : Controller {
Frustum *frustum;
float fov, znear, zfar;
vec3 pos, target;
vec3 target, destPos, lastDest;
int room;
@ -179,16 +148,17 @@ struct Camera : Controller {
angle.y += PI;
room = owner->getEntity().room;
pos = pos - getDir() * 1024.0f;
}
~Camera() {
delete frustum;
}
virtual TR::Room& getRoom() {
virtual TR::Room& getRoom() const {
return level->rooms[room];
}
virtual void update() {
#ifdef FREE_CAMERA
vec3 dir = vec3(sinf(angle.y - PI) * cosf(-angle.x), -sinf(-angle.x), cosf(angle.y - PI) * cosf(-angle.x));
@ -200,10 +170,6 @@ struct Camera : Controller {
if (Input::down[ikA]) v = v - dir.cross(vec3(0, 1, 0));
pos = pos + v.normal() * (Core::deltaTime * 2048.0f);
#endif
// deltaPos = deltaPos.lerp(targetDeltaPos, Core::deltaTime * 10.0f);
// angle = angle.lerp(targetAngle, Core::deltaTime);
if (Input::down[ikMouseL]) {
vec2 delta = Input::mouse.pos - Input::mouse.start.L;
angle.x += delta.y * 0.01f;
@ -211,25 +177,31 @@ struct Camera : Controller {
Input::mouse.start.L = Input::mouse.pos;
}
// angle.x = owner->angle.x;
angle.y = PI - owner->angle.y;
angle.z = 0.0f;
angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
vec3 dir = vec3(sinf(PI - angle.y) * cosf(-angle.x), -sinf(-angle.x), cosf(PI - angle.y) * cosf(-angle.x));
vec3 dir = getDir();
float height = owner->inWater ? 256.0f : 768.0f;
float height = owner->stand == Controller::STAND_UNDERWATER ? 256.0f : 768.0f;
target = vec3(owner->pos.x, owner->pos.y - height, owner->pos.z);
pos = target - dir * 1024.0;
if (owner->state != Lara::STATE_BACK_JUMP) {
destPos = target - dir * 1024.0f;
lastDest = destPos;
} else
destPos = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f);
pos = pos.lerp(destPos, min(1.0f, Core::deltaTime * 2.0f));
FloorInfo info = getFloorInfo((int)pos.x, (int)pos.z);
if (info.roomNext != 255)
room = info.roomNext;
if (pos.y < info.ceiling) {
if (destPos.y < info.ceiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
@ -255,18 +227,7 @@ struct Camera : Controller {
Core::viewPos = Core::mViewInv.offset.xyz;
frustum->pos = Core::viewPos;
frustum->calcPlanes(Core::mViewProj);
#ifdef _DEBUG
vec3 offset = vec3(0.0f) - (Input::down[ikR] ? (Core::mViewInv.dir.xyz * 2048 - vec3(0, 2048, 0)) : vec3(0.0f));
Core::mViewInv = mat4(pos - offset, target - offset, vec3(0, -1, 0));
Core::mView = Core::mViewInv.inverse();
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
Core::mViewProj = Core::mProj * Core::mView;
Core::viewPos = Core::mViewInv.offset.xyz;
#endif
frustum->calcPlanes(Core::mViewProj);
}
};

View File

@ -12,28 +12,43 @@ struct Controller {
TR::Level *level;
int entity;
float fTime;
int lastFrame;
enum Stand {
STAND_AIR, STAND_GROUND, STAND_UNDERWATER, STAND_ONWATER
} stand;
int state;
int mask;
vec3 pos, velocity;
vec3 angle;
enum { LEFT = 1 << 1,
RIGHT = 1 << 2,
FORTH = 1 << 3,
BACK = 1 << 4,
JUMP = 1 << 5,
WALK = 1 << 6,
ACTION = 1 << 7,
WEAPON = 1 << 8,
DEATH = 1 << 11 };
int health;
float animTime;
int animIndex;
int animPrevFrame;
float turnTime;
vec3 pos, velocity;
vec3 angle;
bool onGround;
bool inWater;
float angleExt;
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), fTime(0.0f), lastFrame(0), health(100), turnTime(0.0f) {
int health;
float turnTime;
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), health(100), turnTime(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);
onGround = inWater = false;
pos = vec3((float)e.x, (float)e.y, (float)e.z);
angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f);
stand = STAND_GROUND;
animIndex = getModel().animation;
}
virtual void update() {}
void updateEntity() {
TR::Entity &e = getEntity();
e.x = int(pos.x);
@ -42,7 +57,7 @@ struct Controller {
e.rotation = int(angle.y / (PI * 0.5f) * 16384.0f);
}
bool insideRoom(const vec3 &pos, int room) {
bool insideRoom(const vec3 &pos, int room) const {
TR::Room &r = level->rooms[room];
vec3 min = vec3(r.info.x, r.info.yTop, r.info.z);
vec3 max = min + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024);
@ -52,11 +67,11 @@ struct Controller {
pos.z >= min.z && pos.z <= max.z;
}
TR::Entity& getEntity() {
TR::Entity& getEntity() const {
return level->entities[entity];
}
TR::Model& getModel() {
TR::Model& getModel() const {
TR::Entity &entity = getEntity();
for (int i = 0; i < level->modelsCount; i++)
if (entity.id == level->models[i].id)
@ -65,13 +80,13 @@ struct Controller {
return level->models[0];
}
virtual TR::Room& getRoom() {
virtual TR::Room& getRoom() const {
int index = getEntity().room;
ASSERT(index >= 0 && index < level->roomsCount);
return level->rooms[index];
}
TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) {
TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) const {
TR::Room &room = getRoom();
int sx = x - room.info.x;
@ -88,19 +103,26 @@ struct Controller {
return room.sectors[sx * room.zSectors + sz];
}
TR::Room::Sector& getSector(int &dx, int &dz) {
TR::Room::Sector& getSector(int &dx, int &dz) const {
TR::Entity &entity = getEntity();
return getSector(entity.x, entity.z, dx, dz);
}
bool changeState(int state) {
TR::Model &model = getModel();
TR::Animation *anim = &level->anims[model.animation];
int setAnimation(int index, int frame = -1) {
animIndex = index;
TR::Animation &anim = level->anims[animIndex];
animTime = frame == -1 ? 0.0f : ((frame - anim.frameStart) / 30.0f);
animPrevFrame = -1;
return state = anim.state;
}
bool setState(int state) {
TR::Animation *anim = &level->anims[animIndex];
if (state == anim->state)
return true;
int fIndex = int(fTime * 30.0f);
int fIndex = int(animTime * 30.0f);
bool exists = false;
@ -111,8 +133,7 @@ struct Controller {
for (int j = 0; j < s.rangesCount; j++) {
TR::AnimRange &range = level->ranges[s.rangesOffset + j];
if (anim->frameStart + fIndex >= range.low && anim->frameStart + fIndex <= range.high) {
model.animation = range.nextAnimation;
fTime = (range.nextFrame - level->anims[model.animation].frameStart) / 30.0f;
setAnimation(range.nextAnimation, range.nextFrame);
break;
}
}
@ -122,7 +143,7 @@ struct Controller {
return exists;
}
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) {
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) const {
int dx, dz;
TR::Room::Sector &s = getSector(fromX, fromZ, dx, dz);
@ -236,7 +257,7 @@ struct Controller {
return info;
}
void playSound(int id) {
void playSound(int id) const {
int16 a = level->soundsMap[id];
TR::SoundInfo &b = level->soundsInfo[a];
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
@ -248,425 +269,15 @@ struct Controller {
}
}
};
#define FAST_TURN_TIME 1.0f
#define TURN_FAST PI
#define TURN_FAST_BACK PI * 3.0f / 4.0f
#define TURN_NORMAL PI / 2.0f
#define TURN_SLOW PI / 3.0f
#define TURN_TILT PI / 18.0f
#define TURN_WATER_FAST PI * 3.0f / 4.0f
#define TURN_WATER_SLOW PI * 2.0f / 3.0f
#define GLIDE_SPEED 50.0f
struct Lara : Controller {
Lara(TR::Level *level, int entity) : Controller(level, entity) {
/*
pos = vec3(70067, -256, 29104);
angle = vec3(0.0f, -0.68f, 0.0f);
getEntity().room = 15;
*/
/*
pos = vec3(41015, 3584, 34494);
angle = vec3(0.0f, -PI, 0.0f);
getEntity().room = 51;
*/
}
virtual void update() {
TR::Model &model = getModel();
TR::Animation *anim = &level->anims[model.animation];
fTime += Core::deltaTime;
// return;
float rot = 0.0f;
enum { LEFT = 1 << 1,
RIGHT = 1 << 2,
FORTH = 1 << 3,
BACK = 1 << 4,
JUMP = 1 << 5,
WALK = 1 << 6,
ACTION = 1 << 7,
WEAPON = 1 << 8,
GROUND = 1 << 9,
WATER = 1 << 10,
DEATH = 1 << 11 };
inWater = (getRoom().flags & TR::ROOM_FLAG_WATER);
int mask = 0;
if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH;
if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK;
if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT;
if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT;
if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP;
if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK;
if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION;
if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON;
if (health <= 0) mask |= DEATH;
if (onGround) mask |= GROUND;
if (inWater) mask |= WATER;
int state = anim->state;
if (mask & DEATH)
state = TR::STATE_DEATH;
else if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water surface
angle.x = 0.0f;
state = TR::STATE_SURF_TREAD;
} else if (mask & GROUND) {
angle.x = 0.0f;
if (state == TR::STATE_COMPRESS) {
switch (mask & (RIGHT | LEFT | FORTH | BACK)) {
case RIGHT : state = TR::STATE_RIGHT_JUMP; break;
case LEFT : state = TR::STATE_LEFT_JUMP; break;
case FORTH : state = TR::STATE_FORWARD_JUMP; break;
case BACK : state = TR::STATE_BACK_JUMP; break;
default : state = TR::STATE_UP_JUMP; break;
}
} else
if (mask & JUMP) { // jump button is pressed
if ((mask & FORTH) && state == TR::STATE_FORWARD_JUMP)
state = TR::STATE_RUN;
else
state = state == TR::STATE_RUN ? TR::STATE_FORWARD_JUMP : TR::STATE_COMPRESS;
} else
if (mask & WALK) { // walk button is pressed
if (mask & FORTH)
state = TR::STATE_WALK;
else if (mask & BACK)
state = TR::STATE_BACK;
else if (mask & LEFT)
state = TR::STATE_STEP_LEFT;
else if (mask & RIGHT)
state = TR::STATE_STEP_RIGHT;
else
state = TR::STATE_STOP;
} else { // only dpad buttons pressed
if (mask & FORTH)
state = TR::STATE_RUN;
else if (mask & BACK)
state = TR::STATE_FAST_BACK;
else if (mask & LEFT)
state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_LEFT : TR::STATE_FAST_TURN;
else if (mask & RIGHT)
state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_RIGHT : TR::STATE_FAST_TURN;
else
state = TR::STATE_STOP;
}
} else if (mask & WATER) { // underwater
if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_UP_JUMP || state == TR::STATE_BACK_JUMP || state == TR::STATE_LEFT_JUMP || state == TR::STATE_RIGHT_JUMP || state == TR::STATE_FALL || state == TR::STATE_REACH) {
model.animation = TR::ANIM_WATER_FALL;
fTime = 0.0f;
state = level->anims[model.animation].state;
} else if (state == TR::STATE_SWAN_DIVE) {
state = TR::STATE_DIVE;
angle.x = -PI * 0.5f;
} else
if (mask & JUMP)
state = TR::STATE_SWIM;
else
state = (state == TR::STATE_SWIM || velocity.y > GLIDE_SPEED) ? TR::STATE_GLIDE : TR::STATE_TREAD;
} else { // in the air
angle.x = 0.0f;
if (state == TR::STATE_FORWARD_JUMP) {
if (mask & ACTION)
state = TR::STATE_REACH;
else if ((mask & (FORTH | WALK)) == (FORTH | WALK))
state = TR::STATE_SWAN_DIVE;
} else if (state != TR::STATE_SWAN_DIVE && state != TR::STATE_REACH && state != TR::STATE_FALL && state != TR::STATE_UP_JUMP && state != TR::STATE_BACK_JUMP && state != TR::STATE_LEFT_JUMP && state != TR::STATE_RIGHT_JUMP) {
model.animation = TR::ANIM_FALL;
state = level->anims[model.animation].state;
}
// state = TR::STATE_FALL;
// LOG("- speed: %f\n", velocity.length());
}
// try to set new state
if (!changeState(state)) {
int stopState = TR::STATE_FALL;
if (state == TR::STATE_DIVE)
stopState = state;
else if ((mask & (GROUND | WATER)) == (GROUND | WATER))
stopState = TR::STATE_SURF_TREAD;
else if (mask & WATER)
stopState = TR::STATE_TREAD;
else if (mask & GROUND)
stopState = TR::STATE_STOP;
changeState(stopState);
/*
if (state == stopState || !changeState(stopState)) {
int stopAnim = -1;
switch (stopState) {
case TR::STATE_FALL : stopAnim = TR::ANIM_FALL; break;
}
if (stopAnim > -1) {
model.animation = stopAnim;
fTime = 0.0f;
}
}*/
}
anim = &level->anims[model.animation]; // get new animation and state (if it has been changed)
state = anim->state;
int fCount = anim->frameEnd - anim->frameStart + 1;
int fIndex = int(fTime * 30.0f);
#ifdef _DEBUG
// show state transitions for current animation
static bool lState = false;
if (Input::down[ikEnter]) {
if (!lState) {
lState = true;
static int snd_id = 0;
playSound(snd_id);
LOG("sound: %d\n", snd_id++);
}
/*
LOG("state: %d\n", anim->state);
for (int i = 0; i < anim->scCount; i++) {
auto &sc = level->states[anim->scOffset + i];
LOG("-> %d : ", (int)sc.state);
for (int j = 0; j < sc.rangesCount; j++) {
TR::AnimRange &range = level->ranges[sc.rangesOffset + j];
LOG("%d ", range.nextAnimation);
}
LOG("\n");
}*/
} else
lState = false;
#endif
// calculate turn tilt
if (state == TR::STATE_RUN && (mask & (GROUND | WATER)) == GROUND && (mask & (LEFT | RIGHT))) {
if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT;
if (mask & RIGHT) angle.z += Core::deltaTime * TURN_TILT;
angle.z = clamp(angle.z, -TURN_TILT, TURN_TILT);
} else
angle.z -= angle.z * min(Core::deltaTime * 8.0f, 1.0f);
if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_FAST_TURN)
turnTime += Core::deltaTime;
else
turnTime = 0.0f;
// get turning angle
float w = 0.0f;
if (state == TR::STATE_SWIM || state == TR::STATE_GLIDE)
w = TURN_WATER_FAST;
else if (state == TR::STATE_TREAD)
w = TURN_WATER_SLOW;
else if (state == TR::STATE_RUN || state == TR::STATE_FAST_TURN)
w = TURN_FAST;
else if (state == TR::STATE_FAST_BACK)
w = TURN_FAST_BACK;
else if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_WALK)
w = TURN_NORMAL;
else if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_BACK)
w = TURN_SLOW;
if (w != 0.0f) {
w *= Core::deltaTime;
// yaw
if (mask & LEFT) { angle.y -= w; velocity = velocity.rotateY(+w); }
if (mask & RIGHT) { angle.y += w; velocity = velocity.rotateY(-w); }
// pitch (underwater only)
if ( ((mask & (GROUND | WATER)) == WATER) && (mask & (FORTH | BACK)) ) {
angle.x += ((mask & FORTH) ? -w : w) * 0.5f;
angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f);
}
}
// get animation direction
float d = 0.0f;
switch (state) {
case TR::STATE_BACK :
case TR::STATE_BACK_JUMP :
case TR::STATE_FAST_BACK :
d = PI;
break;
case TR::STATE_STEP_LEFT :
case TR::STATE_LEFT_JUMP :
d = -PI * 0.5f;
break;
case TR::STATE_STEP_RIGHT :
case TR::STATE_RIGHT_JUMP :
d = PI * 0.5f;
break;
}
d += angle.y;
bool endFrame = fIndex >= fCount;
// calculate moving speed
float dt = Core::deltaTime * 30.0f;
if (mask & (GROUND | WATER)) {
if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water
} else if (mask & WATER) { // underwater
if (state == TR::STATE_SWIM) {
velocity = vec3(angle.x, angle.y) * 35.0f;
} else
velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f);
// TODO: apply flow velocity
} else { // on ground
float speed = anim->speed + anim->accel * (fTime * 30.0f);
velocity.x = sinf(d) * speed;
velocity.z = cosf(d) * speed;
velocity.y += GRAVITY * dt;
}
} else
velocity.y += GRAVITY * dt;
// apply animation commands
int16 *ptr = &level->commands[anim->animCommand];
for (int i = 0; i < anim->acCount; i++) {
int cmd = *ptr++;
switch (cmd) {
case 0x01 : { // cmd position
int16 sx = *ptr++;
int16 sy = *ptr++;
int16 sz = *ptr++;
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
break;
}
case 0x02 : { // cmd jump speed
int16 sy = *ptr++;
int16 sz = *ptr++;
if (endFrame) {
LOG("jump: %d %d\n", (int)sy, (int)sz);
velocity.x = sinf(d) * sz;
velocity.y = sy;
velocity.z = cosf(d) * sz;
LOG("speed: %f\n", velocity.length());
onGround = false;
}
break;
}
case 0x03 : // empty hands
break;
case 0x04 : // kill
break;
case 0x05 : { // play sound
int frame = (*ptr++);
int id = (*ptr++) & 0x3FFF;
int idx = frame - anim->frameStart;
// if (fIndex != lastFrame)
// LOG("play sound at %d current %d last %d (%d)\n", idx, fIndex, lastFrame, (int)id);
if (idx > lastFrame && idx <= fIndex) {
playSound(id);
}
break;
}
case 0x06 :
if (fIndex != lastFrame && fIndex + anim->frameStart == ptr[0]) {
if (ptr[1] == 0) {
angle = angle + PI;
}
}
ptr += 2;
break;
default :
LOG("unknown animation command %d\n", cmd);
}
}
// check for next animation
if (endFrame) {
model.animation = anim->nextAnimation;
TR::Animation *nextAnim = &level->anims[anim->nextAnimation];
fIndex = anim->nextFrame - nextAnim->frameStart;
fTime = fIndex / 30.0f;
}
move(velocity * dt);
collide();
updateEntity();
lastFrame = fIndex;
}
void move(const vec3 &offset) {
vec3 p = pos;
pos = pos + offset;
int delta;
int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta);
int state = level->anims[getModel().animation].state;
bool stop = false;
if ((d == NO_OVERLAP) ||
((state == TR::STATE_WALK || state == TR::STATE_BACK || state == TR::STATE_STEP_LEFT || state == TR::STATE_STEP_RIGHT) && delta > 256) /*||
(delta < -256) */) {
pos = p;
TR::Model &model = getModel();
TR::Animation *anim = &level->anims[model.animation];
// smashes
if (onGround) { // onGround
if (d >= 256 * 4 && anim->state == TR::STATE_RUN)
model.animation = TR::ANIM_SMASH_RUN_LEFT; // TODO: RIGHT
else
model.animation = TR::ANIM_STAND;
velocity.x = velocity.z = 0.0f;
fTime = 0;
} else if (inWater) { // in water
// do nothing
//velocity.x = velocity.z = 0.0f;
} else { // in the air
model.animation = TR::ANIM_SMASH_JUMP;
velocity.x = -velocity.x * 0.5f;
velocity.z = -velocity.z * 0.5f;
velocity.y = 0.0f;
fTime = 0;
}
} else {
TR::Entity &entity = getEntity();
entity.x = (int)pos.x;
entity.y = (int)pos.y;
entity.z = (int)pos.z;
}
vec3 getDir() const {
return vec3(sinf(PI - angle.y) * cosf(-angle.x), -sinf(-angle.x), cosf(PI - angle.y) * cosf(-angle.x));
}
void collide() {
TR::Entity &entity = getEntity();
FloorInfo info = getFloorInfo(entity.x, entity.z);
/*
float hmin = 0.0f, hmax = -768.0f;
if (inWater) {
@ -713,27 +324,125 @@ struct Lara : Controller {
entity.room = s.roomAbove;
}
*/
int state = level->anims[getModel().animation].state;
// TODO: use a brain!
float extra = 0;
if (state == TR::STATE_WALK ||
state == TR::STATE_RUN ||
state == TR::STATE_STOP ||
state == TR::STATE_FAST_BACK ||
state == TR::STATE_TURN_RIGHT ||
state == TR::STATE_TURN_LEFT ||
state == TR::STATE_BACK ||
state == TR::STATE_FAST_TURN ||
state == TR::STATE_STEP_RIGHT ||
state == TR::STATE_STEP_LEFT ||
state == TR::STATE_ROLL)
extra = 256 + 128;
onGround = (pos.y + extra >= info.floor) && (info.roomBelow == 0xFF) && !(getRoom().flags & TR::ROOM_FLAG_WATER);
}
virtual void updateVelocity() {}
virtual void move() {}
virtual Stand getStand() { return STAND_AIR; }
virtual int getStateAir() { return state; }
virtual int getStateGround() { return state; }
virtual int getStateUnderwater() { return state; }
virtual int getStateOnwater() { return state; }
virtual int getStateDeath() { return state; }
virtual int getStateDefault() { return state; }
virtual int getInputMask() { return 0; }
virtual int getState(Stand stand) {
TR::Animation *anim = &level->anims[animIndex];
int state = anim->state;
if (mask & DEATH)
state = getStateDeath();
else if (stand == STAND_GROUND)
state = getStateGround();
else if (stand == STAND_AIR)
state = getStateAir();
else if (stand == STAND_UNDERWATER)
state = getStateUnderwater();
else
state = getStateOnwater();
// try to set new state
if (!setState(state))
setState(getStateDefault());
return level->anims[animIndex].state;
}
virtual void updateBegin() {
animTime += Core::deltaTime;
mask = getInputMask();
state = getState(stand = getStand());
}
virtual void updateEnd() {
int frameIndex = int(animTime * 30.0f);
TR::Animation *anim = &level->anims[animIndex];
bool endFrame = frameIndex > anim->frameEnd - anim->frameStart;
// apply animation commands
int16 *ptr = &level->commands[anim->animCommand];
for (int i = 0; i < anim->acCount; i++) {
int cmd = *ptr++;
switch (cmd) {
case 0x01 : { // cmd position
int16 sx = *ptr++;
int16 sy = *ptr++;
int16 sz = *ptr++;
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
break;
}
case 0x02 : { // cmd jump speed
int16 sy = *ptr++;
int16 sz = *ptr++;
if (endFrame) {
LOG("jump: %d %d\n", (int)sy, (int)sz);
velocity.x = sinf(angleExt) * sz;
velocity.y = sy;
velocity.z = cosf(angleExt) * sz;
LOG("speed: %f\n", velocity.length());
stand = STAND_AIR;
}
break;
}
case 0x03 : // empty hands
break;
case 0x04 : // kill
break;
case 0x05 : { // play sound
int frame = (*ptr++);
int id = (*ptr++) & 0x3FFF;
int idx = frame - anim->frameStart;
if (idx > animPrevFrame && idx <= frameIndex) {
playSound(id);
// LOG("play sound %d\n", getEntity().id);
}
break;
}
case 0x06 : // effect
if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) {
if (ptr[1] == 0) // rolling
angle.y = angle.y + PI;
}
ptr += 2;
break;
default :
LOG("unknown animation command %d\n", cmd);
}
}
if (endFrame) // if animation is end - switch to next
setAnimation(anim->nextAnimation, anim->nextFrame);
else
animPrevFrame = frameIndex;
updateVelocity();
move();
collide();
updateEntity();
}
virtual void updateState() {}
virtual void update() {
updateBegin();
updateState();
updateEnd();
}
};
#endif

View File

@ -4,8 +4,28 @@
#include "core.h"
#include "format.h"
extern HDC hDC;
namespace Debug {
static GLuint font;
void init() {
font = glGenLists(256);
HDC hdc = hDC;
HFONT hfont = CreateFontA(-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0,
0, 0, FW_BOLD, 0, 0, 0,
ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH, "Courier New");
SelectObject(hdc, hfont);
wglUseFontBitmaps(hdc, 0, 256, font);
DeleteObject(hfont);
}
void free() {
glDeleteLists(font, 256);
}
void begin() {
glMatrixMode(GL_PROJECTION);
glLoadMatrixf((GLfloat*)&Core::mProj);
@ -111,6 +131,41 @@ namespace Debug {
glVertex3fv((GLfloat*)&p);
glEnd();
}
void text(const vec2 &pos, const vec4 &color, const char *str) {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, Core::width, Core::height, 0, 0, 1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glColor4fv((GLfloat*)&color);
glRasterPos2f(pos.x, pos.y);
glListBase(font);
glCallLists(strlen(str), GL_UNSIGNED_BYTE, str);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void text(const vec3 &pos, const vec4 &color, const char *str) {
vec4 p = Core::mViewProj * vec4(pos, 1);
if (p.w > 0) {
p.xyz = p.xyz * (1.0f / p.w);
p.y = -p.y;
p.xyz = (p.xyz * 0.5f + vec3(0.5f)) * vec3(Core::width, Core::height, 1.0f);
text(vec2(p.x, p.y), color, str);
}
}
}
namespace Level {
@ -373,12 +428,14 @@ namespace Debug {
sm->getBox(true, m.rotation, min, max);
Debug::Draw::box(offset + min - vec3(10.0f), offset + max + vec3(10.0f), vec4(1, 0, 0, 0.50));
}
/*
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2];
ASSERT(mesh->radius == 0 || mesh->radius == 0x10000);
Debug::Draw::sphere(offset + (min + max) * 0.5f, 128, mesh->radius == 0 ? vec4(0, 0, 1, 1) : vec4(0, 1, 0, 1));
{ //if (mesh->collider.info || mesh->collider.flags) {
char buf[255];
sprintf(buf, "radius %d info %d flags %d", (int)mesh->collider.radius, (int)mesh->collider.info, (int)mesh->collider.flags);
Debug::Draw::text(offset + (min + max) * 0.5f, vec4(0.5, 0.5, 0.5, 1), buf);
}
*/
}
}
// dynamic objects
@ -435,7 +492,14 @@ namespace Debug {
int offset = level.meshOffsets[m.mStart + k];
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[offset / 2];
Debug::Draw::sphere(matrix * joint * mesh->center, mesh->radius & 0x3FF, mesh->radius > 0x3FF ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
Debug::Draw::sphere(matrix * joint * mesh->center, mesh->collider.radius, mesh->collider.info ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
/*
{ //if (e.id != 0) {
char buf[255];
sprintf(buf, "(%d) radius %d info %d flags %d", e.id, (int)mesh->collider.radius, (int)mesh->collider.info, (int)mesh->collider.flags);
Debug::Draw::text(matrix * joint * mesh->center, vec4(0.5, 1, 0.5, 1), buf);
}
*/
}
break;
}

84
src/enemy.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef H_ENEMY
#define H_ENEMY
#include "controller.h"
struct Enemy : Controller {
Enemy(TR::Level *level, int entity) : Controller(level, entity) {}
virtual Stand getStand() {
return STAND_GROUND;
}
};
struct Wolf : Enemy {
enum {
STATE_STOP = 1,
STATE_WALK = 2,
STATE_RUN = 3,
STATE_STALKING = 5,
STATE_JUMP = 6,
STATE_HOWL = 7,
STATE_SLEEP = 8,
STATE_GROWL = 9,
STATE_10 = 10, // WTF?
STATE_ATTACK = 12,
};
Wolf(TR::Level *level, int entity) : Enemy(level, entity) {}
virtual int getStateGround() {
// STATE_SLEEP -> STATE_STOP
// STATE_STOP -> STATE_WALK, STATE_HOWL, STATE_SLEEP, STATE_GROWL
// STATE_WALK -> NULL
// STATE_RUN -> STaTE_JUMP, STATe_GROWL, STATE_10
// STATE_STALKING -> STATE_RUN, STATE_GROWL, STATE_BITING
// STATE_JUMP -> STATE_RUN
// STATE_GROWL -> STATE_STOP, STATE_RUN, STATE_STALKING, STATE_HOWL, STATE_ATTACK
// STATE_BITING -> NULL
// if (state == STATE_SLEEP) return STATE_STOP;
// if (state == STATE_STOP) return STATE_GROWL;
// if (state == STATE_GROWL) return STATE_ATTACK;
// if (state == STATE_RUN) return STATE_10;
return state;
}
};
struct Bear : Enemy {
enum {
STATE_STOP = 1,
};
Bear(TR::Level *level, int entity) : Enemy(level, entity) {}
virtual int getStateGround() {
return state;
}
};
struct Bat : Enemy {
enum {
STATE_AWAKE = 1,
STATE_FLY = 2,
};
Bat(TR::Level *level, int entity) : Enemy(level, entity) {}
virtual Stand getStand() {
return STAND_AIR;
}
virtual int getStateAir() {
animTime = 0.0f;
return STATE_AWAKE;
}
};
#endif

View File

@ -47,9 +47,9 @@ namespace TR {
#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_ENEMY_CENTAUR 23
#define ENTITY_ENEMY_MUMMY 24
#define ENTITY_ENEMY_LARSON 27
#define ENTITY_CRYSTAL 83
@ -71,76 +71,6 @@ namespace TR {
#define ENTITY_AMMO_SHOTGUN 89
#define ENTITY_AMMO_MAGNUM 90
// http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left
enum LaraAnim : int32 {
ANIM_STAND = 11,
ANIM_FALL = 34,
ANIM_SMASH_JUMP = 32,
ANIM_SMASH_RUN_LEFT = 53,
ANIM_SMASH_RUN_RIGHT = 54,
ANIM_WATER_FALL = 112,
};
// http://www.tombraiderforums.com/showthread.php?t=211681
enum LaraState : int32 {
STATE_WALK,
STATE_RUN,
STATE_STOP,
STATE_FORWARD_JUMP,
STATE_4,
STATE_FAST_BACK,
STATE_TURN_RIGHT,
STATE_TURN_LEFT,
STATE_DEATH,
STATE_FALL,
STATE_HANG,
STATE_REACH,
STATE_SPLAT,
STATE_TREAD,
STATE_FAST_TURN_14,
STATE_COMPRESS,
STATE_BACK,
STATE_SWIM,
STATE_GLIDE,
STATE_NULL_19,
STATE_FAST_TURN,
STATE_STEP_RIGHT,
STATE_STEP_LEFT,
STATE_ROLL,
STATE_SLIDE,
STATE_BACK_JUMP,
STATE_RIGHT_JUMP,
STATE_LEFT_JUMP,
STATE_UP_JUMP,
STATE_FALL_BACK,
STATE_HANG_LEFT,
STATE_HANG_RIGHT,
STATE_SLIDE_BACK,
STATE_SURF_TREAD,
STATE_SURF_SWIM,
STATE_DIVE,
STATE_PUSH_BLOCK,
STATE_PULL_BLOCK,
STATE_PUSH_PULL_READY,
STATE_PICK_UP,
STATE_SWITCH_ON,
STATE_SWITCH_OFF,
STATE_USE_KEY,
STATE_USE_PUZZLE,
STATE_UNDERWATER_DEATH,
STATE_ROLL_45,
STATE_SPECIAL,
STATE_SURF_BACK,
STATE_SURF_LEFT,
STATE_SURF_RIGHT,
STATE_NULL_50,
STATE_NULL_51,
STATE_SWAN_DIVE,
STATE_FAST_DIVE,
STATE_HANDSTAND,
STATE_WATER_OUT,
STATE_MAX };
#pragma pack(push, 1)
struct fixed {
@ -268,14 +198,19 @@ namespace TR {
uint16 boxIndex:15, end:1;
};
struct Collider {
uint16 radius:10, info:6;
uint16 flags:16;
};
struct Mesh {
Vertex center;
int32 radius;
Vertex center;
Collider collider;
int16 vCount;
Vertex *vertices; // List of vertices (relative coordinates)
int16 nCount;
uint16 vCount;
Vertex *vertices; // List of vertices (relative coordinates)
int16 nCount;
union {
Vertex *normals;
int16 *lights; // if nCount < 0 -> (abs(nCount))
@ -303,7 +238,9 @@ namespace TR {
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)
uint16 align; // ! not exists in file !
// not exists in file
uint16 align;
void *controller; // Controller implementation or NULL
};
struct Animation {
@ -474,7 +411,7 @@ namespace TR {
int16 floor; // Height value in global units
uint16 overlap; // Index into Overlaps[].
bool contains(int x, int z) {
bool contains(uint32 x, uint32 z) {
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
}
};
@ -673,8 +610,11 @@ namespace TR {
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));
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;
}
// palette
stream.seek(32 * 256); // skip lightmap palette

401
src/lara.h Normal file
View File

@ -0,0 +1,401 @@
#ifndef H_LARA
#define H_LARA
#include "controller.h"
#define FAST_TURN_TIME 1.0f
#define TURN_FAST PI
#define TURN_FAST_BACK PI * 3.0f / 4.0f
#define TURN_NORMAL PI / 2.0f
#define TURN_SLOW PI / 3.0f
#define TURN_TILT PI / 18.0f
#define TURN_WATER_FAST PI * 3.0f / 4.0f
#define TURN_WATER_SLOW PI * 2.0f / 3.0f
#define GLIDE_SPEED 50.0f
struct Lara : Controller {
// http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left
enum LaraAnim : int32 {
ANIM_STAND = 11,
ANIM_FALL = 34,
ANIM_SMASH_JUMP = 32,
ANIM_SMASH_RUN_LEFT = 53,
ANIM_SMASH_RUN_RIGHT = 54,
ANIM_WATER_FALL = 112,
ANIM_STAND_ROLL = 146,
};
// http://www.tombraiderforums.com/showthread.php?t=211681
enum LaraState : int32 {
STATE_WALK,
STATE_RUN,
STATE_STOP,
STATE_FORWARD_JUMP,
STATE_4,
STATE_FAST_BACK,
STATE_TURN_RIGHT,
STATE_TURN_LEFT,
STATE_DEATH,
STATE_FALL,
STATE_HANG,
STATE_REACH,
STATE_SPLAT,
STATE_TREAD,
STATE_FAST_TURN_14,
STATE_COMPRESS,
STATE_BACK,
STATE_SWIM,
STATE_GLIDE,
STATE_NULL_19,
STATE_FAST_TURN,
STATE_STEP_RIGHT,
STATE_STEP_LEFT,
STATE_ROLL,
STATE_SLIDE,
STATE_BACK_JUMP,
STATE_RIGHT_JUMP,
STATE_LEFT_JUMP,
STATE_UP_JUMP,
STATE_FALL_BACK,
STATE_HANG_LEFT,
STATE_HANG_RIGHT,
STATE_SLIDE_BACK,
STATE_SURF_TREAD,
STATE_SURF_SWIM,
STATE_DIVE,
STATE_PUSH_BLOCK,
STATE_PULL_BLOCK,
STATE_PUSH_PULL_READY,
STATE_PICK_UP,
STATE_SWITCH_ON,
STATE_SWITCH_OFF,
STATE_USE_KEY,
STATE_USE_PUZZLE,
STATE_UNDERWATER_DEATH,
STATE_ROLL_45,
STATE_SPECIAL,
STATE_SURF_BACK,
STATE_SURF_LEFT,
STATE_SURF_RIGHT,
STATE_NULL_50,
STATE_NULL_51,
STATE_SWAN_DIVE,
STATE_FAST_DIVE,
STATE_HANDSTAND,
STATE_WATER_OUT,
STATE_MAX };
Lara(TR::Level *level, int entity) : Controller(level, entity) {
/*
// level 2 (pool)
pos = vec3(70067, -256, 29104);
angle = vec3(0.0f, -0.68f, 0.0f);
getEntity().room = 15;
*/
/*
// level 2 (wolf)
pos = vec3(75671, -1024, 22862);
angle = vec3(0.0f, -PI * 0.25f, 0.0f);
getEntity().room = 13;
*/
/*
// level 3a
pos = vec3(41015, 3584, 34494);
angle = vec3(0.0f, -PI, 0.0f);
getEntity().room = 51;
*/
/*
// level 1
pos = vec3(20215, 6656, 52942);
angle = vec3(0.0f, PI, 0.0f);
getEntity().room = 14;
*/
}
bool isMovingState(int state) {
return state == STATE_RUN || state == STATE_WALK || state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT;
}
virtual Stand getStand() {
if (getRoom().flags & TR::ROOM_FLAG_WATER)
return STAND_UNDERWATER; // TODO: ONWATER
int extra = isMovingState(state) ? 256 : 0;
TR::Entity &e = getEntity();
FloorInfo info = getFloorInfo(e.x, e.z);
if (info.roomBelow == 0xFF && e.y + extra >= info.floor)
return STAND_GROUND;
return STAND_AIR;
}
virtual int getStateAir() {
angle.x = 0.0f;
if (state == STATE_FORWARD_JUMP) {
if (mask & ACTION) return STATE_REACH;
if ((mask & (FORTH | WALK)) == (FORTH | WALK)) return STATE_SWAN_DIVE;
} else
if (state != STATE_SWAN_DIVE && state != STATE_REACH && state != STATE_FALL && state != STATE_UP_JUMP && state != STATE_BACK_JUMP && state != STATE_LEFT_JUMP && state != STATE_RIGHT_JUMP && state != STATE_ROLL)
return setAnimation(ANIM_FALL);
return state;
}
virtual int getStateGround() {
angle.x = 0.0f;
if ( (mask & (FORTH | BACK)) == (FORTH | BACK) && (state == STATE_STOP || state == STATE_RUN) )
return setAnimation(ANIM_STAND_ROLL);
// ready to jump
if (state == STATE_COMPRESS) {
switch (mask & (RIGHT | LEFT | FORTH | BACK)) {
case RIGHT : return STATE_RIGHT_JUMP;
case LEFT : return STATE_LEFT_JUMP;
case FORTH : return STATE_FORWARD_JUMP;
case BACK : return STATE_BACK_JUMP;
default : return STATE_UP_JUMP;
}
}
// jump button is pressed
if (mask & JUMP) {
if ((mask & FORTH) && state == STATE_FORWARD_JUMP)
return STATE_RUN;
return state == STATE_RUN ? STATE_FORWARD_JUMP : STATE_COMPRESS;
}
// walk button is pressed
if (mask & WALK) {
if (mask & FORTH) return STATE_WALK;
if (mask & BACK) return STATE_BACK;
if (mask & LEFT) return STATE_STEP_LEFT;
if (mask & RIGHT) return STATE_STEP_RIGHT;
return STATE_STOP;
}
// only dpad buttons pressed
if (mask & FORTH) return STATE_RUN;
if (mask & BACK) return STATE_FAST_BACK;
if (mask & LEFT) return turnTime < FAST_TURN_TIME ? STATE_TURN_LEFT : STATE_FAST_TURN;
if (mask & RIGHT) return turnTime < FAST_TURN_TIME ? STATE_TURN_RIGHT : STATE_FAST_TURN;
return STATE_STOP;
}
virtual int getStateUnderwater() {
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)
return setAnimation(ANIM_WATER_FALL);
if (state == STATE_SWAN_DIVE) {
angle.x = -PI * 0.5f;
return STATE_DIVE;
}
if (mask & JUMP) return STATE_SWIM;
return (state == STATE_SWIM || velocity.y > GLIDE_SPEED) ? STATE_GLIDE : STATE_TREAD;
}
virtual int getStateOnwater() {
angle.x = 0.0f;
return STATE_SURF_TREAD;
}
virtual int getStateDeath() {
return STATE_DEATH;
}
virtual int getStateDefault() {
if (state == STATE_DIVE) return state;
if (stand == STAND_ONWATER) return STATE_SURF_TREAD;
if (stand == STAND_UNDERWATER) return STATE_TREAD;
if (stand == STAND_GROUND) return STATE_STOP;
return STATE_FALL;
}
virtual int getInputMask() {
mask = 0;
if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH;
if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK;
if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT;
if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT;
if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP;
if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK;
if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION;
if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON;
if (health <= 0) mask = DEATH;
return mask;
}
virtual void updateState() {
TR::Animation *anim = &level->anims[animIndex];
int fCount = anim->frameEnd - anim->frameStart;
int fIndex = int(animTime * 30.0f);
float rot = 0.0f;
#ifdef _DEBUG
// show state transitions for current animation
static bool lState = false;
if (Input::down[ikEnter]) {
if (!lState) {
lState = true;
static int snd_id = 0;
playSound(snd_id);
LOG("sound: %d\n", snd_id++);
}
/*
LOG("state: %d\n", anim->state);
for (int i = 0; i < anim->scCount; i++) {
auto &sc = level->states[anim->scOffset + i];
LOG("-> %d : ", (int)sc.state);
for (int j = 0; j < sc.rangesCount; j++) {
AnimRange &range = level->ranges[sc.rangesOffset + j];
LOG("%d ", range.nextAnimation);
}
LOG("\n");
}*/
} else
lState = false;
#endif
// calculate turn tilt
if (state == STATE_RUN && (mask & (LEFT | RIGHT))) {
if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT;
if (mask & RIGHT) angle.z += Core::deltaTime * TURN_TILT;
angle.z = clamp(angle.z, -TURN_TILT, TURN_TILT);
} else
angle.z -= angle.z * min(Core::deltaTime * 8.0f, 1.0f);
if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_FAST_TURN)
turnTime += Core::deltaTime;
else
turnTime = 0.0f;
// get turning angle
float w = 0.0f;
if (state == STATE_SWIM || state == STATE_GLIDE)
w = TURN_WATER_FAST;
else if (state == STATE_TREAD)
w = TURN_WATER_SLOW;
else if (state == STATE_RUN || state == STATE_FAST_TURN)
w = TURN_FAST;
else if (state == STATE_FAST_BACK)
w = TURN_FAST_BACK;
else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK)
w = TURN_NORMAL;
else if (state == STATE_FORWARD_JUMP || state == STATE_BACK)
w = TURN_SLOW;
if (w != 0.0f) {
w *= Core::deltaTime;
// yaw
if (mask & LEFT) { angle.y -= w; velocity = velocity.rotateY(+w); }
if (mask & RIGHT) { angle.y += w; velocity = velocity.rotateY(-w); }
// pitch (underwater only)
if (stand == STAND_UNDERWATER && (mask & (FORTH | BACK)) ) {
angle.x += ((mask & FORTH) ? -w : w) * 0.5f;
angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f);
}
}
// get animation direction
angleExt = angle.y;
switch (state) {
case STATE_BACK :
case STATE_BACK_JUMP :
case STATE_FAST_BACK :
angleExt += PI;
break;
case STATE_STEP_LEFT :
case STATE_LEFT_JUMP :
angleExt -= PI * 0.5f;
break;
case STATE_STEP_RIGHT :
case STATE_RIGHT_JUMP :
angleExt += PI * 0.5f;
break;
}
}
virtual void updateVelocity() {
// calculate moving speed
float dt = Core::deltaTime * 30.0f;
if (stand == STAND_AIR) {
velocity.y += GRAVITY * dt;
} else if (stand == STAND_ONWATER) {
} else if (stand == STAND_UNDERWATER) {
if (state == STATE_SWIM)
velocity = vec3(angle.x, angle.y) * 35.0f;
else
velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f);
// TODO: apply flow velocity
} else if (stand == STAND_GROUND) {
TR::Animation *anim = &level->anims[animIndex];
float speed = anim->speed + anim->accel * (animTime * 30.0f);
velocity.x = sinf(angleExt) * speed;
velocity.z = cosf(angleExt) * speed;
velocity.y += GRAVITY * dt;
}
}
virtual void move() {
vec3 offset = velocity * Core::deltaTime * 30.0f;
vec3 p = pos;
pos = pos + offset;
int delta;
int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta);
int state = level->anims[animIndex].state;
bool stop = false;
if ((d == NO_OVERLAP) ||
((state == STATE_WALK || state == STATE_BACK || state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT) && delta > 256) /*||
(delta < -256) */) {
pos = p;
TR::Animation *anim = &level->anims[animIndex];
// smashes
if (stand == STAND_GROUND) { // onGround
if (d >= 256 * 4 && anim->state == STATE_RUN)
setAnimation(ANIM_SMASH_RUN_LEFT); // TODO: RIGHT
else
setAnimation(ANIM_STAND);
velocity.x = velocity.z = 0.0f;
} else if (stand == STAND_UNDERWATER) { // in water
// do nothing
//velocity.x = velocity.z = 0.0f;
} else { // in the air
setAnimation(ANIM_SMASH_JUMP);
velocity.x = -velocity.x * 0.5f;
velocity.z = -velocity.z * 0.5f;
velocity.y = 0.0f;
}
} else {
TR::Entity &entity = getEntity();
entity.x = (int)pos.x;
entity.y = (int)pos.y;
entity.z = (int)pos.z;
}
}
};
#endif

View File

@ -4,10 +4,10 @@
#include "core.h"
#include "utils.h"
#include "format.h"
#include "controller.h"
#include "lara.h"
#include "enemy.h"
#include "camera.h"
#ifdef _DEBUG
#include "debug.h"
#endif
@ -30,31 +30,68 @@ struct Level {
float time;
Level(Stream &stream) : level{stream}, time(0.0f) {
#ifdef _DEBUG
Debug::init();
#endif
mesh = new MeshBuilder(level);
initAtlas();
initShaders();
initOverrides();
int entity = 0;
for (int i = 0; i < level.entitiesCount; i++)
if (level.entities[i].id == ENTITY_LARA) {
entity = i;
break;
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &entity = level.entities[i];
switch (entity.id) {
case ENTITY_LARA :
entity.controller = (lara = new Lara(&level, i));
break;
case ENTITY_ENEMY_WOLF :
entity.controller = new Wolf(&level, i);
break;
case ENTITY_ENEMY_BEAR :
entity.controller = new Bear(&level, i);
break;
case 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 :
entity.controller = new Enemy(&level, i);
break;
}
}
lara = new Lara(&level, entity);
camera = new Camera(&level, lara);
ASSERT(lara != NULL);
camera = new Camera(&level, lara);
}
~Level() {
#ifdef _DEBUG
Debug::free();
#endif
for (int i = 0; i < level.entitiesCount; i++)
delete (Controller*)level.entities[i].controller;
for (int i = 0; i < shMAX; i++)
delete shaders[i];
delete atlas;
delete mesh;
delete camera;
delete lara;
delete camera;
}
void initAtlas() {
@ -236,11 +273,6 @@ struct Level {
renderRoom(p.roomIndex, roomIndex);
}
camera->frustum = camFrustum; // pop camera frustum
#ifdef _DEBUG
glColor3f(0, 0.05, 0);
camera->frustum->debug();
#endif
}
MeshBuilder::MeshInfo* getMeshInfoByOffset(uint32 meshOffset) {
@ -250,16 +282,15 @@ struct Level {
for (int i = 0; i < mesh->mCount; i++)
if (mesh->meshInfo[i].offset == level.meshOffsets[meshOffset])
return &mesh->meshInfo[i];
ASSERT(false);
return NULL;
}
void renderMesh(uint32 meshOffset) {
MeshBuilder::MeshInfo *m = getMeshInfoByOffset(meshOffset);
ASSERT(m != NULL);
if (!m) return;
if (!m) return; // invisible mesh (level.meshOffsets[meshOffset] == 0) camera target entity etc.
if ((m->radius & 0xFFFF) == 0 || camera->frustum->isVisible(Core::mModel * m->center, (m->radius & 0x3FF)) * 2) {
if (!m->collider.radius || camera->frustum->isVisible(Core::mModel * m->center, m->collider.radius * 2)) {
Core::active.shader->setParam(uModel, Core::mModel);
mesh->renderMesh(m);
}
@ -295,14 +326,21 @@ struct Level {
return ma.getRot().slerp(mb.getRot(), t).normal();
}
void renderModel(const TR::Model &model, vec3 angle) {
TR::Animation *anim = &level.anims[model.animation];
void renderModel(const TR::Model &model, const TR::Entity &entity) {
TR::Animation *anim;
float fTime;
vec3 angle;
float fTime = time;
Controller *controller = (Controller*)entity.controller;
if (model.id == ENTITY_LARA) {
fTime = lara->fTime;
angle = lara->angle;
if (controller) {
angle = controller->angle;
fTime = controller->animTime;
anim = &level.anims[controller->animIndex];
} else {
anim = &level.anims[model.animation];
angle = vec3(0.0f, entity.rotation / 16384.0f * PI * 0.5f, 0.0f);
fTime = time;
}
if (angle.y != 0.0f) Core::mModel.rotateY(angle.y);
@ -405,9 +443,6 @@ struct Level {
}
void renderEntity(const TR::Entity &entity) {
// if (!(entity.flags & ENTITY_FLAG_VISIBLE))
// return;
TR::Room &room = level.rooms[entity.room];
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) // check for room visibility
return;
@ -431,7 +466,7 @@ struct Level {
for (int i = 0; i < level.modelsCount; i++)
if (entity.id == level.models[i].id) {
isModel = true;
renderModel(level.models[i], vec3(0, entity.rotation / 16384.0f * PI * 0.5f, 0));
renderModel(level.models[i], entity);
break;
}
@ -455,7 +490,13 @@ struct Level {
void update() {
time += Core::deltaTime;
lara->update();
for (int i = 0; i < level.entitiesCount; i++) {
Controller *controller = (Controller*)level.entities[i].controller;
if (controller)
controller->update();
}
camera->update();
}
@ -511,7 +552,7 @@ struct Level {
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
// Debug::Level::lights(level);
// Debug::Level::portals(level);
Debug::Level::meshes(level);
// Debug::Level::meshes(level);
Debug::end();
#endif
}

View File

@ -67,9 +67,9 @@ struct MeshBuilder {
// objects meshes
struct MeshInfo : MeshRange {
int offset;
TR::Vertex center;
int32 radius;
int offset;
TR::Vertex center;
TR::Collider collider;
} *meshInfo;
int mCount;
@ -131,7 +131,7 @@ struct MeshBuilder {
mCount = 0;
TR::Mesh *ptr = (TR::Mesh*)level.meshData;
while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * 2 ) {
while ( ((intptr_t)ptr - (intptr_t)level.meshData) < level.meshDataSize * 2 ) {
mCount++;
OFFSET(ptr->vCount * sizeof(TR::Vertex));
@ -155,7 +155,7 @@ struct MeshBuilder {
iCount += ptr->ctCount * 3;
vCount += ptr->ctCount * 3;
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
ptr = (TR::Mesh*)(((int)ptr + 3) & -4);
ptr = (TR::Mesh*)(((intptr_t)ptr + 3) & -4);
}
meshInfo = new MeshInfo[mCount];
@ -229,15 +229,14 @@ struct MeshBuilder {
}
// build objects geometry
mCount = 0;
ptr = (TR::Mesh*)level.meshData;
while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * sizeof(uint16) ) {
MeshInfo &info = meshInfo[mCount++];
info.offset = (int)ptr - (int)level.meshData;
info.vStart = vCount;
info.iStart = iCount;
info.center = ptr->center;
info.radius = ptr->radius;
for (int i = 0; i < mCount; i++) {
MeshInfo &info = meshInfo[i];
info.offset = (intptr_t)ptr - (intptr_t)level.meshData;
info.vStart = vCount;
info.iStart = iCount;
info.center = ptr->center;
info.collider = ptr->collider;
TR::Vertex *mVertices = (TR::Vertex*)&ptr->vertices;
@ -326,7 +325,7 @@ struct MeshBuilder {
} else {
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
vertices[vCount].normal = { 0, 0, 0, 1 };
vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color
vertices[vCount].color = { a, a, a, 255 };
}
vCount++;
}
@ -352,16 +351,15 @@ struct MeshBuilder {
} else {
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
vertices[vCount].normal = { 0, 0, 0, 1 };
vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color
vertices[vCount].color = { a, a, a, 255 };
}
vCount++;
}
}
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
ptr = (TR::Mesh*)(((int)ptr + 3) & -4);
info.iCount = iCount - info.iStart;
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
ptr = (TR::Mesh*)(((intptr_t)ptr + 3) & -4);
}
// build sprite sequences
@ -388,8 +386,8 @@ struct MeshBuilder {
int tile = tex.tile.index;
int tx = (tile % 4) * 256;
int ty = (tile / 4) * 256;
return vec2( ((tx + tex.vertices[0].Xpixel) << 5) + 16,
((ty + tex.vertices[0].Ypixel) << 5) + 16 );
return vec2( (float)(((tx + tex.vertices[0].Xpixel) << 5) + 16),
(float)(((ty + tex.vertices[0].Ypixel) << 5) + 16) );
}
void initAnimTextures(TR::Level &level) {

View File

@ -100,8 +100,10 @@
<ClInclude Include="..\controller.h" />
<ClInclude Include="..\core.h" />
<ClInclude Include="..\debug.h" />
<ClInclude Include="..\enemy.h" />
<ClInclude Include="..\game.h" />
<ClInclude Include="..\input.h" />
<ClInclude Include="..\lara.h" />
<ClInclude Include="..\level.h" />
<ClInclude Include="..\mesh.h" />
<ClInclude Include="..\shader.h" />

View File

@ -174,6 +174,8 @@ void freeGL(HGLRC hRC) {
wglDeleteContext(hRC);
}
HDC hDC;
int main() {
#ifdef _DEBUG
_CrtMemState _ms;
@ -188,7 +190,7 @@ int main() {
joyInit();
HDC hDC = GetDC(hWnd);
hDC = GetDC(hWnd);
HGLRC hRC = initGL(hDC);
Game::init();