mirror of
https://github.com/XProger/OpenLara.git
synced 2025-01-17 21:09:00 +01:00
code refactoring; animation, character and sprite classes, compiler warnings etc.; fixed #19; fixed #20
This commit is contained in:
parent
7f004415e1
commit
251f99339b
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
253
src/animation.h
Normal file
253
src/animation.h
Normal file
@ -0,0 +1,253 @@
|
||||
#ifndef H_ANIMATION
|
||||
#define H_ANIMATION
|
||||
|
||||
#include "utils.h"
|
||||
#include "format.h"
|
||||
|
||||
struct Animation {
|
||||
TR::Level *level;
|
||||
TR::Model *model;
|
||||
TR::Animation *anims;
|
||||
int state;
|
||||
float time, timeMax, delta, dir;
|
||||
int index, prev, next;
|
||||
int frameIndex, framePrev, framesCount;
|
||||
|
||||
TR::AnimFrame *frameA, *frameB;
|
||||
vec3 offset, jump;
|
||||
bool isEnded, isPrepareToNext, flip;
|
||||
|
||||
quat *overrides; // left & right arms animation frames
|
||||
int overrideMask;
|
||||
|
||||
Animation() : overrides(NULL) {}
|
||||
|
||||
Animation(TR::Level *level, TR::Model *model) : level(level), model(model), anims(model ? &level->anims[model->animation] : NULL), time(0), delta(0), dir(1.0f),
|
||||
index(-1), prev(0), next(0), overrides(NULL), overrideMask(0) {
|
||||
if (anims) setAnim(0);
|
||||
}
|
||||
|
||||
~Animation() {
|
||||
delete[] overrides;
|
||||
}
|
||||
|
||||
inline operator TR::Animation* () const { return anims + index; }
|
||||
|
||||
void initOverrides() {
|
||||
overrides = new quat[model->mCount];
|
||||
overrideMask = 0;
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (!isEnded) {
|
||||
time += dir * Core::deltaTime;
|
||||
isEnded = time <= 0.0f || time >= timeMax;
|
||||
time = clamp(time, 0.0f, timeMax - EPS);
|
||||
}
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
int setAnim(int animIndex, int animFrame = 0, bool lerpToNext = true) {
|
||||
TR::Animation *anim = anims + animIndex;
|
||||
isEnded = isPrepareToNext = false;
|
||||
offset = jump = vec3(0.0f);
|
||||
prev = index;
|
||||
index = animIndex;
|
||||
next = anims[index].nextAnimation - model->animation;
|
||||
time = (animFrame <= 0 ? -animFrame : (animFrame - anim->frameStart)) / 30.0f;
|
||||
timeMax = (anim->frameEnd - anim->frameStart + lerpToNext) / 30.0f;
|
||||
framesCount = anim->frameEnd - anim->frameStart + 1;
|
||||
updateInfo();
|
||||
framePrev = frameIndex - 1;
|
||||
getCommand(anim, frameIndex, &offset, &jump, NULL);
|
||||
return state = anim->state;
|
||||
}
|
||||
|
||||
void playNext() {
|
||||
setAnim(next, anims[index].nextFrame);
|
||||
}
|
||||
|
||||
void updateInfo() {
|
||||
ASSERT(model);
|
||||
ASSERT(anims);
|
||||
TR::Animation *anim = anims + index;
|
||||
|
||||
// framePrev = frameIndex;
|
||||
frameIndex = int(time * 30.0f);
|
||||
|
||||
// get count of real frames
|
||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
||||
// real frame index & lerp delta
|
||||
int fIndex = int(time * 30.0f) / anim->frameRate;
|
||||
int k = fIndex * anim->frameRate;
|
||||
delta = (time * 30.0f - k) / min((int)anim->frameRate, framesCount - k); // min is because in some cases framesCount > realFramesCount / frameRate * frameRate
|
||||
|
||||
// size of frame (in bytes)
|
||||
int fSize = sizeof(TR::AnimFrame) + model->mCount * sizeof(uint16) * 2;
|
||||
|
||||
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
|
||||
frameA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1]; // >> 1 (div 2) because frameData is array of shorts
|
||||
|
||||
int frameNext = frameIndex + 1;
|
||||
isPrepareToNext = !fIndexB;
|
||||
if (isPrepareToNext) {
|
||||
frameNext = anim->nextFrame;
|
||||
anim = &level->anims[anim->nextAnimation];
|
||||
frameNext -= anim->frameStart;
|
||||
fIndexB = frameNext / anim->frameRate;
|
||||
}
|
||||
|
||||
getCommand(anim, frameNext, NULL, NULL, &flip);
|
||||
|
||||
frameB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
|
||||
}
|
||||
|
||||
bool isFrameActive(int index) {
|
||||
return index > framePrev && index <= frameIndex;
|
||||
}
|
||||
|
||||
bool canSetState(int state) {
|
||||
TR::Animation *anim = anims + index;
|
||||
|
||||
if (state == anim->state)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
TR::AnimState &s = level->states[anim->scOffset + i];
|
||||
if (s.state == state)
|
||||
for (int j = 0; j < s.rangesCount; j++) {
|
||||
TR::AnimRange &range = level->ranges[s.rangesOffset + j];
|
||||
if (anim->frameStart + frameIndex >= range.low && anim->frameStart + frameIndex <= range.high)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool setState(int state) {
|
||||
TR::Animation *anim = anims + index;
|
||||
|
||||
if (state == anim->state)
|
||||
return true;
|
||||
|
||||
bool exists = false;
|
||||
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
TR::AnimState &s = level->states[anim->scOffset + i];
|
||||
if (s.state == state) {
|
||||
exists = true;
|
||||
for (int j = 0; j < s.rangesCount; j++) {
|
||||
TR::AnimRange &range = level->ranges[s.rangesOffset + j];
|
||||
if (anim->frameStart + frameIndex >= range.low && anim->frameStart + frameIndex <= range.high) {
|
||||
setAnim(range.nextAnimation - model->animation, range.nextFrame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
float getSpeed() {
|
||||
TR::Animation *anim = anims + index;
|
||||
return anim->speed + anim->accel * (time * 30.0f);
|
||||
}
|
||||
|
||||
void getCommand(TR::Animation *anim, int frameIndex, vec3 *offset, vec3 *jump, bool *flip) {
|
||||
int16 *ptr = &level->commands[anim->animCommand];
|
||||
|
||||
if (offset) *offset = vec3(0.0f);
|
||||
if (flip) *flip = false;
|
||||
|
||||
for (int i = 0; i < anim->acCount; i++) {
|
||||
int cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case TR::ANIM_CMD_OFFSET :
|
||||
if (offset) {
|
||||
offset->x = (float)*ptr++;
|
||||
offset->y = (float)*ptr++;
|
||||
offset->z = (float)*ptr++;
|
||||
} else
|
||||
ptr += 3;
|
||||
break;
|
||||
case TR::ANIM_CMD_JUMP :
|
||||
if (jump) {
|
||||
jump->y = (float)*ptr++;
|
||||
jump->z = (float)*ptr++;
|
||||
} else
|
||||
ptr += 2;
|
||||
break;
|
||||
case TR::ANIM_CMD_SOUND : ptr += 2; break;
|
||||
case TR::ANIM_CMD_EFFECT :
|
||||
if (flip) {
|
||||
int frame = (*ptr++) - anim->frameStart;
|
||||
int fx = (*ptr++) & 0x3FFF;
|
||||
*flip = fx == TR::EFFECT_ROTATE_180 && frame == frameIndex;
|
||||
} else
|
||||
ptr += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quat getJointRot(int joint) {
|
||||
return lerpAngle(frameA->getAngle(joint), frameB->getAngle(joint), delta);
|
||||
}
|
||||
|
||||
mat4 getJoints(mat4 matrix, int joint, bool postRot = false, mat4 *joints = NULL) {
|
||||
TR::Animation *anim = anims + index;
|
||||
|
||||
vec3 offset = isPrepareToNext ? this->offset : vec3(0.0f);
|
||||
matrix.translate(((vec3)frameA->pos).lerp(offset + frameB->pos, delta));
|
||||
|
||||
TR::Node *node = (int)model->node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model->node] : NULL;
|
||||
|
||||
int sIndex = 0;
|
||||
mat4 stack[16];
|
||||
|
||||
for (int i = 0; i < model->mCount; i++) {
|
||||
|
||||
if (i > 0 && node) {
|
||||
TR::Node &t = node[i - 1];
|
||||
|
||||
if (t.flags & 0x01) matrix = stack[--sIndex];
|
||||
if (t.flags & 0x02) stack[sIndex++] = matrix;
|
||||
|
||||
ASSERT(sIndex >= 0 && sIndex < 16);
|
||||
|
||||
matrix.translate(vec3((float)t.x, (float)t.y, (float)t.z));
|
||||
}
|
||||
|
||||
if (i == joint && !postRot)
|
||||
return matrix;
|
||||
|
||||
quat q;
|
||||
if (overrideMask & (1 << i))
|
||||
q = overrides[i];
|
||||
else
|
||||
q = getJointRot(i);
|
||||
matrix = matrix * mat4(q, vec3(0.0f));
|
||||
|
||||
if (i == joint && postRot)
|
||||
return matrix;
|
||||
|
||||
if (joints)
|
||||
joints[i] = matrix;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
Box getBoundingBox(const vec3 &pos, int dir) {
|
||||
vec3 min = frameA->box.min().lerp(frameB->box.min(), delta);
|
||||
vec3 max = frameA->box.max().lerp(frameB->box.max(), delta);
|
||||
Box box(min, max);
|
||||
box.rotate90(dir);
|
||||
box.min += pos;
|
||||
box.max += pos;
|
||||
return box;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
27
src/camera.h
27
src/camera.h
@ -56,7 +56,9 @@ struct Camera : Controller {
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
actTargetEntity = owner->target;
|
||||
int lookAt = -1;
|
||||
if (owner->target > -1) lookAt = owner->target;
|
||||
if (actTargetEntity > -1) lookAt = actTargetEntity;
|
||||
|
||||
if (timer > 0.0f) {
|
||||
timer -= Core::deltaTime;
|
||||
@ -65,6 +67,7 @@ struct Camera : Controller {
|
||||
if (room != getRoomIndex())
|
||||
pos = lastDest;
|
||||
actTargetEntity = actCamera = -1;
|
||||
target = owner->getViewPoint();
|
||||
}
|
||||
}
|
||||
#ifdef FREE_CAMERA
|
||||
@ -91,28 +94,28 @@ struct Camera : Controller {
|
||||
angle.z = 0.0f;
|
||||
//angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
|
||||
|
||||
float lerpFactor = (actTargetEntity == -1) ? 6.0f : 10.0f;
|
||||
float lerpFactor = (lookAt == -1) ? 6.0f : 10.0f;
|
||||
vec3 dir;
|
||||
target = target.lerp(owner->getViewPoint(), lerpFactor * Core::deltaTime);
|
||||
|
||||
if (actCamera > -1) {
|
||||
TR::Camera &c = level->cameras[actCamera];
|
||||
destPos = vec3(c.x, c.y, c.z);
|
||||
destPos = vec3(float(c.x), float(c.y), float(c.z));
|
||||
if (room != getRoomIndex())
|
||||
pos = destPos;
|
||||
if (actTargetEntity > -1) {
|
||||
TR::Entity &e = level->entities[actTargetEntity];
|
||||
target = vec3(e.x, e.y, e.z);
|
||||
if (lookAt > -1) {
|
||||
TR::Entity &e = level->entities[lookAt];
|
||||
target = ((Controller*)e.controller)->pos;
|
||||
}
|
||||
} else {
|
||||
if (actTargetEntity > -1) {
|
||||
TR::Entity &e = level->entities[actTargetEntity];
|
||||
dir = (vec3(e.x, e.y, e.z) - target).normal();
|
||||
if (lookAt > -1) {
|
||||
TR::Entity &e = level->entities[lookAt];
|
||||
dir = (((Controller*)e.controller)->pos - target).normal();
|
||||
} else
|
||||
dir = getDir();
|
||||
|
||||
int destRoom;
|
||||
if ((owner->wpnState != Lara::Weapon::IS_HIDDEN || owner->state != Lara::STATE_BACK_JUMP) || actTargetEntity > -1) {
|
||||
if ((!owner->emptyHands() || owner->state != Lara::STATE_BACK_JUMP) || lookAt > -1) {
|
||||
vec3 eye = target - dir * CAMERA_OFFSET;
|
||||
destPos = trace(owner->getRoomIndex(), target, eye, destRoom, true);
|
||||
lastDest = destPos;
|
||||
@ -138,7 +141,7 @@ struct Camera : Controller {
|
||||
room = info.roomAbove;
|
||||
else
|
||||
if (info.ceiling != 0xffff8100)
|
||||
pos.y = info.ceiling;
|
||||
pos.y = (float)info.ceiling;
|
||||
}
|
||||
|
||||
if (pos.y > info.floor) {
|
||||
@ -146,7 +149,7 @@ struct Camera : Controller {
|
||||
room = info.roomBelow;
|
||||
else
|
||||
if (info.floor != 0xffff8100)
|
||||
pos.y = info.floor;
|
||||
pos.y = (float)info.floor;
|
||||
}
|
||||
|
||||
// play underwater sound when camera goes under water
|
||||
|
137
src/character.h
Normal file
137
src/character.h
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef H_CHARACTER
|
||||
#define H_CHARACTER
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
struct Character : Controller {
|
||||
int target;
|
||||
int health;
|
||||
float tilt;
|
||||
quat rotHead, rotChest;
|
||||
|
||||
enum Stand {
|
||||
STAND_AIR, STAND_GROUND, STAND_SLIDE, STAND_HANG, STAND_UNDERWATER, STAND_ONWATER
|
||||
} stand;
|
||||
int input;
|
||||
|
||||
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 << 9 };
|
||||
|
||||
vec3 velocity;
|
||||
float angleExt;
|
||||
|
||||
Character(TR::Level *level, int entity, int health) : Controller(level, entity), target(-1), health(100), tilt(0.0f), stand(STAND_GROUND), velocity(0.0f) {
|
||||
animation.initOverrides();
|
||||
rotHead = rotChest = quat(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
virtual void hit(int damage) {
|
||||
health -= damage;
|
||||
};
|
||||
|
||||
virtual void checkRoom() {
|
||||
TR::Level::FloorInfo info;
|
||||
TR::Entity &e = getEntity();
|
||||
level->getFloorInfo(e.room, e.x, e.z, info);
|
||||
|
||||
if (info.roomNext != 0xFF)
|
||||
e.room = info.roomNext;
|
||||
|
||||
if (info.roomBelow != 0xFF && e.y > info.floor)
|
||||
e.room = info.roomBelow;
|
||||
|
||||
if (info.roomAbove != 0xFF && e.y <= info.ceiling) {
|
||||
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
|
||||
stand = STAND_ONWATER;
|
||||
velocity.y = 0;
|
||||
pos.y = float(info.ceiling);
|
||||
updateEntity();
|
||||
} else
|
||||
if (stand != STAND_ONWATER)
|
||||
e.room = info.roomAbove;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void cmdKill() {
|
||||
health = 0;
|
||||
}
|
||||
|
||||
virtual void updatePosition() {}
|
||||
virtual Stand getStand() { return stand; }
|
||||
virtual int getHeight() { return 0; }
|
||||
virtual int getStateAir() { return state; }
|
||||
virtual int getStateGround() { return state; }
|
||||
virtual int getStateSlide() { return state; }
|
||||
virtual int getStateHang() { return state; }
|
||||
virtual int getStateUnderwater() { return state; }
|
||||
virtual int getStateOnwater() { return state; }
|
||||
virtual int getStateDeath() { return state; }
|
||||
virtual int getStateDefault() { return state; }
|
||||
virtual int getInput() { return 0; }
|
||||
|
||||
virtual void updateState() {
|
||||
int state = animation.state;
|
||||
|
||||
if (input & DEATH)
|
||||
state = getStateDeath();
|
||||
else if (stand == STAND_GROUND)
|
||||
state = getStateGround();
|
||||
else if (stand == STAND_SLIDE)
|
||||
state = getStateSlide();
|
||||
else if (stand == STAND_HANG)
|
||||
state = getStateHang();
|
||||
else if (stand == STAND_AIR)
|
||||
state = getStateAir();
|
||||
else if (stand == STAND_UNDERWATER)
|
||||
state = getStateUnderwater();
|
||||
else
|
||||
state = getStateOnwater();
|
||||
|
||||
// try to set new state
|
||||
if (!animation.setState(state))
|
||||
animation.setState(getStateDefault());
|
||||
}
|
||||
|
||||
virtual void updateTilt(bool active, float tiltSpeed, float tiltMax) {
|
||||
// calculate turning tilt
|
||||
if (active && (input & (LEFT | RIGHT)) && (tilt == 0.0f || (tilt < 0.0f && (input & LEFT)) || (tilt > 0.0f && (input & RIGHT)))) {
|
||||
if (input & LEFT) tilt -= tiltSpeed * Core::deltaTime;
|
||||
if (input & RIGHT) tilt += tiltSpeed * Core::deltaTime;
|
||||
} else
|
||||
if (fabsf(tilt) > 0.01f) {
|
||||
if (tilt > 0.0f)
|
||||
tilt -= min(tilt, tiltSpeed * 4.0f * Core::deltaTime);
|
||||
else
|
||||
tilt -= max(tilt, -tiltSpeed * 4.0f * Core::deltaTime);
|
||||
} else
|
||||
tilt = 0.0f;
|
||||
tilt = clamp(tilt, -tiltMax, tiltMax);
|
||||
|
||||
angle.z = tilt;
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
input = getInput();
|
||||
stand = getStand();
|
||||
updateState();
|
||||
Controller::update();
|
||||
updateVelocity();
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
virtual void cmdJump(const vec3 &vel) {
|
||||
velocity.x = sinf(angleExt) * vel.z;
|
||||
velocity.y = vel.y;
|
||||
velocity.z = cosf(angleExt) * vel.z;
|
||||
stand = STAND_AIR;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
541
src/controller.h
541
src/controller.h
@ -4,51 +4,25 @@
|
||||
#include "format.h"
|
||||
#include "frustum.h"
|
||||
#include "mesh.h"
|
||||
#include "animation.h"
|
||||
|
||||
#define GRAVITY 6.0f
|
||||
#define NO_OVERLAP 0x7FFFFFFF
|
||||
|
||||
#define SPRITE_FPS 10.0f
|
||||
|
||||
struct Controller {
|
||||
TR::Level *level;
|
||||
int entity;
|
||||
|
||||
Animation animation;
|
||||
int &state;
|
||||
|
||||
enum Stand {
|
||||
STAND_AIR, STAND_GROUND, STAND_SLIDE, STAND_HANG, STAND_UNDERWATER, STAND_ONWATER
|
||||
} stand;
|
||||
int state;
|
||||
int mask;
|
||||
|
||||
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 << 9 };
|
||||
|
||||
float animTime;
|
||||
int animIndex, animPrev;
|
||||
int animPrevFrame;
|
||||
|
||||
vec3 pos, velocity;
|
||||
vec3 pos;
|
||||
vec3 angle;
|
||||
|
||||
float angleExt;
|
||||
|
||||
int *meshes;
|
||||
int mCount;
|
||||
|
||||
// TODO: Character class
|
||||
quat *animOverrides; // left & right arms animation frames
|
||||
int animOverrideMask;
|
||||
mat4 *joints;
|
||||
int health;
|
||||
float tilt;
|
||||
|
||||
struct ActionCommand {
|
||||
TR::Action action;
|
||||
int value;
|
||||
@ -59,145 +33,39 @@ struct Controller {
|
||||
ActionCommand(TR::Action action, int value, float timer, ActionCommand *next = NULL) : action(action), value(value), timer(timer), next(next) {}
|
||||
} *actionCommand;
|
||||
|
||||
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), actionCommand(NULL), mCount(0), meshes(NULL), animOverrides(NULL), animOverrideMask(0), joints(NULL) {
|
||||
Controller(TR::Level *level, int entity) : level(level), entity(entity), animation(level, getModel()), state(animation.state), actionCommand(NULL), mCount(0), meshes(NULL) {
|
||||
TR::Entity &e = getEntity();
|
||||
pos = vec3((float)e.x, (float)e.y, (float)e.z);
|
||||
angle = vec3(0.0f, e.rotation, 0.0f);
|
||||
stand = STAND_GROUND;
|
||||
animIndex = e.modelIndex > 0 ? getModel().animation : 0;
|
||||
animPrev = animIndex;
|
||||
state = level->anims[animIndex].state;
|
||||
TR::Model &model = getModel();
|
||||
health = 100;
|
||||
tilt = 0.0f;
|
||||
}
|
||||
|
||||
virtual ~Controller() {
|
||||
delete[] meshes;
|
||||
delete[] animOverrides;
|
||||
delete[] joints;
|
||||
}
|
||||
|
||||
void initMeshOverrides() {
|
||||
TR::Model &model = getModel();
|
||||
mCount = model.mCount;
|
||||
TR::Model *model = getModel();
|
||||
mCount = model->mCount;
|
||||
meshes = mCount ? new int[mCount] : NULL;
|
||||
for (int i = 0; i < mCount; i++)
|
||||
meshes[i] = model.mStart + i;
|
||||
meshes[i] = model->mStart + i;
|
||||
}
|
||||
|
||||
void initAnimOverrides() {
|
||||
TR::Model &model = getModel();
|
||||
|
||||
animOverrides = new quat[model.mCount];
|
||||
animOverrideMask = 0;
|
||||
|
||||
joints = new mat4[model.mCount];
|
||||
}
|
||||
|
||||
void meshSwap(TR::Model &model, int mask) {
|
||||
for (int i = 0; i < model.mCount; i++) {
|
||||
int index = model.mStart + i;
|
||||
void meshSwap(TR::Model *model, int mask) {
|
||||
for (int i = 0; i < model->mCount; i++) {
|
||||
int index = model->mStart + i;
|
||||
if (((1 << i) & mask) && level->meshOffsets[index])
|
||||
meshes[i] = index;
|
||||
}
|
||||
}
|
||||
|
||||
int getFramesCount(int animIndex) {
|
||||
TR::Animation &anim = level->anims[animIndex];
|
||||
return (anim.frameEnd - anim.frameStart) / anim.frameRate + 1;
|
||||
}
|
||||
|
||||
int getFrameIndex(int animIndex, float t) {
|
||||
TR::Animation &anim = level->anims[animIndex];
|
||||
return int(t * 30.0f / anim.frameRate) % ((anim.frameEnd - anim.frameStart) / anim.frameRate + 1);
|
||||
}
|
||||
|
||||
void getFrames(TR::AnimFrame **frameA, TR::AnimFrame **frameB, float &t, int animIndex, float animTime, bool nextAnim = false, vec3 *move = NULL) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
t = animTime * 30.0f / anim->frameRate;
|
||||
int fIndex = (int)t;
|
||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
||||
|
||||
int fSize = sizeof(TR::AnimFrame) + getModel().mCount * sizeof(uint16) * 2;
|
||||
t -= fIndex;
|
||||
|
||||
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
|
||||
*frameA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1];
|
||||
|
||||
if (!fIndexB) {
|
||||
if (move)
|
||||
*move = getAnimMove();
|
||||
if (nextAnim) {
|
||||
int nextFrame = anim->nextFrame;
|
||||
anim = &level->anims[anim->nextAnimation];
|
||||
fIndexB = (nextFrame - anim->frameStart) / anim->frameRate;
|
||||
}
|
||||
}
|
||||
*frameB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
|
||||
}
|
||||
|
||||
mat4 getJoint(int index, bool postRot = false) {
|
||||
mat4 matrix;
|
||||
matrix.identity();
|
||||
|
||||
matrix.translate(pos);
|
||||
if (angle.y != 0.0f) matrix.rotateY(angle.y);
|
||||
if (angle.x != 0.0f) matrix.rotateX(angle.x);
|
||||
if (angle.z != 0.0f) matrix.rotateZ(angle.z);
|
||||
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
TR::Model &model = getModel();
|
||||
|
||||
float t;
|
||||
vec3 move(0.0f);
|
||||
TR::AnimFrame *frameA, *frameB;
|
||||
getFrames(&frameA, &frameB, t, animIndex, animTime, true, &move);
|
||||
|
||||
TR::Node *node = (int)model.node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model.node] : NULL;
|
||||
|
||||
matrix.translate(((vec3)frameA->pos).lerp(move + frameB->pos, t));
|
||||
|
||||
int sIndex = 0;
|
||||
mat4 stack[20];
|
||||
|
||||
for (int i = 0; i < model.mCount; i++) {
|
||||
|
||||
if (i > 0 && node) {
|
||||
TR::Node &t = node[i - 1];
|
||||
|
||||
if (t.flags & 0x01) matrix = stack[--sIndex];
|
||||
if (t.flags & 0x02) stack[sIndex++] = matrix;
|
||||
|
||||
ASSERT(sIndex >= 0 && sIndex < 20);
|
||||
|
||||
matrix.translate(vec3(t.x, t.y, t.z));
|
||||
}
|
||||
|
||||
if (i == index && !postRot)
|
||||
return matrix;
|
||||
|
||||
quat q;
|
||||
if (animOverrideMask & (1 << i))
|
||||
q = animOverrides[i];
|
||||
else
|
||||
q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), t);
|
||||
matrix = matrix * mat4(q, vec3(0.0f));
|
||||
|
||||
if (i == index && postRot)
|
||||
return matrix;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
|
||||
if (target > -1) {
|
||||
TR::Entity &e = level->entities[target];
|
||||
Box box = ((Controller*)e.controller)->getBoundingBox();
|
||||
vec3 t = (box.min + box.max) * 0.5f;
|
||||
|
||||
mat4 m = getJoint(joint);
|
||||
mat4 m = animation.getJoints(getMatrix(), joint);
|
||||
vec3 delta = (m.inverse() * t).normal();
|
||||
|
||||
float angleY = clampAngle(atan2(delta.x, delta.z));
|
||||
@ -233,16 +101,17 @@ struct Controller {
|
||||
|
||||
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);
|
||||
vec3 min = vec3((float)r.info.x, (float)r.info.yTop, (float)r.info.z);
|
||||
vec3 max = min + vec3(float(r.xSectors * 1024), float(r.info.yBottom - r.info.yTop), float(r.zSectors * 1024));
|
||||
|
||||
return pos.x >= min.x && pos.x <= max.x &&
|
||||
pos.y >= min.y && pos.y <= max.y &&
|
||||
pos.z >= min.z && pos.z <= max.z;
|
||||
}
|
||||
|
||||
TR::Model& getModel() const {
|
||||
return level->models[getEntity().modelIndex - 1];
|
||||
TR::Model* getModel() const {
|
||||
int index = getEntity().modelIndex;
|
||||
return index > 0 ? &level->models[index - 1] : NULL;
|
||||
}
|
||||
|
||||
TR::Entity& getEntity() const {
|
||||
@ -259,64 +128,6 @@ struct Controller {
|
||||
return getEntity().room;
|
||||
}
|
||||
|
||||
int setAnimation(int index, int frame = 0) {
|
||||
animPrev = animIndex;
|
||||
animIndex = index;
|
||||
TR::Animation &anim = level->anims[animIndex];
|
||||
animTime = (frame <= 0 ? -frame : (frame - anim.frameStart)) / 30.0f;
|
||||
ASSERT(anim.frameStart <= anim.frameEnd);
|
||||
animPrevFrame = int(animTime * 30.0f) - 1;
|
||||
return state = anim.state;
|
||||
}
|
||||
|
||||
bool canSetState(int state) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
if (state == anim->state)
|
||||
return true;
|
||||
|
||||
int fIndex = int(animTime * 30.0f);
|
||||
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
TR::AnimState &s = level->states[anim->scOffset + i];
|
||||
if (s.state == state)
|
||||
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)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool setState(int state) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
if (state == anim->state)
|
||||
return true;
|
||||
|
||||
int fIndex = int(animTime * 30.0f);
|
||||
|
||||
bool exists = false;
|
||||
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
TR::AnimState &s = level->states[anim->scOffset + i];
|
||||
if (s.state == state) {
|
||||
exists = true;
|
||||
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) {
|
||||
setAnimation(range.nextAnimation, range.nextFrame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ) const {
|
||||
int dx, dz;
|
||||
TR::Room::Sector &s = level->getSector(getEntity().room, fromX, fromZ, dx, dz);
|
||||
@ -391,15 +202,7 @@ struct Controller {
|
||||
}
|
||||
|
||||
virtual Box getBoundingBox() {
|
||||
float t;
|
||||
TR::AnimFrame *frameA, *frameB;
|
||||
getFrames(&frameA, &frameB, t, animIndex, animTime, true);
|
||||
|
||||
Box box(frameA->box.min().lerp(frameB->box.min(), t), frameA->box.max().lerp(frameB->box.max(), t));
|
||||
box.rotate90(getEntity().rotation.value / 0x4000);
|
||||
box.min += pos;
|
||||
box.max += pos;
|
||||
return box;
|
||||
return animation.getBoundingBox(pos, getEntity().rotation.value / 0x4000);
|
||||
}
|
||||
|
||||
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham
|
||||
@ -439,7 +242,7 @@ struct Controller {
|
||||
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;
|
||||
pos = vec3(float(clamp(px, minX, maxX)), pos.y, float(clamp(pz, minZ, maxZ))) + boxNormal(px, pz) * 256.0f;
|
||||
dir = (pos - from).normal();
|
||||
}
|
||||
} else {
|
||||
@ -472,7 +275,7 @@ struct Controller {
|
||||
if (rand() % 10 <= 6) return;
|
||||
playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN);
|
||||
}
|
||||
|
||||
/*
|
||||
void collide() {
|
||||
TR::Entity &entity = getEntity();
|
||||
|
||||
@ -510,7 +313,7 @@ struct Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
void activateNext() { // activate next entity (for triggers)
|
||||
if (!actionCommand || !actionCommand->next) {
|
||||
actionCommand = NULL;
|
||||
@ -555,93 +358,27 @@ struct Controller {
|
||||
actionCommand = NULL;
|
||||
}
|
||||
|
||||
virtual bool activate(ActionCommand *cmd) { actionCommand = cmd; return true; }
|
||||
virtual void doCustomCommand (int curFrame, int prevFrame) {}
|
||||
virtual void updateVelocity() {}
|
||||
virtual void checkRoom() {}
|
||||
virtual void move() {}
|
||||
virtual Stand getStand() { return STAND_AIR; }
|
||||
virtual int getHeight() { return 0; }
|
||||
virtual int getStateAir() { return state; }
|
||||
virtual int getStateGround() { return state; }
|
||||
virtual int getStateSlide() { return state; }
|
||||
virtual int getStateHang() { 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 void hit(int damage) { };
|
||||
virtual bool activate(ActionCommand *cmd) { actionCommand = cmd; return true; }
|
||||
virtual void doCustomCommand (int curFrame, int prevFrame) {}
|
||||
virtual void updateVelocity() {}
|
||||
virtual void checkRoom() {}
|
||||
|
||||
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_SLIDE)
|
||||
state = getStateSlide();
|
||||
else if (stand == STAND_HANG)
|
||||
state = getStateHang();
|
||||
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() {
|
||||
mask = getInputMask();
|
||||
state = getState(stand = getStand());
|
||||
}
|
||||
|
||||
virtual void updateEnd() {
|
||||
TR::Entity &e = getEntity();
|
||||
move();
|
||||
virtual void cmdOffset(const vec3 &offset) {
|
||||
pos = pos + offset.rotateY(-angle.y);
|
||||
updateEntity();
|
||||
checkRoom();
|
||||
}
|
||||
|
||||
virtual void updateState() {}
|
||||
virtual void cmdJump(const vec3 &vel) {}
|
||||
virtual void cmdKill() {}
|
||||
virtual void cmdEmpty() {}
|
||||
|
||||
virtual vec3 getAnimMove() {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
int16 *ptr = &level->commands[anim->animCommand];
|
||||
|
||||
for (int i = 0; i < anim->acCount; i++) {
|
||||
int cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case TR::ANIM_CMD_MOVE : { // cmd position
|
||||
int16 sx = *ptr++;
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
return vec3((float)sx, (float)sy, (float)sz);
|
||||
break;
|
||||
}
|
||||
case TR::ANIM_CMD_SPEED : // cmd jump speed
|
||||
case TR::ANIM_CMD_SOUND : // play sound
|
||||
case TR::ANIM_CMD_EFFECT : // special commands
|
||||
ptr += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vec3(0.0f);
|
||||
}
|
||||
|
||||
virtual void updateAnimation(bool commands) {
|
||||
int frameIndex = int((animTime += Core::deltaTime) * 30.0f);
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
bool endFrame = frameIndex > anim->frameEnd - anim->frameStart;
|
||||
|
||||
animation.update();
|
||||
|
||||
TR::Animation *anim = animation;
|
||||
|
||||
// apply animation commands
|
||||
if (commands) {
|
||||
int16 *ptr = &level->commands[anim->animCommand];
|
||||
@ -649,50 +386,24 @@ struct Controller {
|
||||
for (int i = 0; i < anim->acCount; i++) {
|
||||
int cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case TR::ANIM_CMD_MOVE : { // cmd position
|
||||
int16 sx = *ptr++;
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
if (endFrame) {
|
||||
pos = pos + vec3(sx, sy, sz).rotateY(-angle.y);
|
||||
updateEntity();
|
||||
checkRoom();
|
||||
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TR::ANIM_CMD_SPEED : { // 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;
|
||||
stand = STAND_AIR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TR::ANIM_CMD_EMPTY : // empty hands
|
||||
break;
|
||||
case TR::ANIM_CMD_KILL : // kill
|
||||
break;
|
||||
case TR::ANIM_CMD_SOUND : // play sound
|
||||
case TR::ANIM_CMD_EFFECT : { // special commands
|
||||
int frame = (*ptr++);
|
||||
int id = (*ptr++) & 0x3FFF;
|
||||
int idx = frame - anim->frameStart;
|
||||
|
||||
if (idx > animPrevFrame && idx <= frameIndex) {
|
||||
case TR::ANIM_CMD_OFFSET : ptr += 3; break;
|
||||
case TR::ANIM_CMD_JUMP : ptr += 2; break;
|
||||
case TR::ANIM_CMD_EMPTY : cmdEmpty(); break;
|
||||
case TR::ANIM_CMD_KILL : cmdKill(); break;
|
||||
case TR::ANIM_CMD_SOUND :
|
||||
case TR::ANIM_CMD_EFFECT : {
|
||||
int frame = (*ptr++) - anim->frameStart;
|
||||
int fx = (*ptr++) & 0x3FFF;
|
||||
if (animation.isFrameActive(frame)) {
|
||||
if (cmd == TR::ANIM_CMD_EFFECT) {
|
||||
switch (id) {
|
||||
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
||||
switch (fx) {
|
||||
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
||||
case TR::EFFECT_LARA_BUBBLES : doBubbles(); break;
|
||||
case TR::EFFECT_LARA_HANDSFREE : break;
|
||||
default : LOG("unknown special cmd %d (anim %d)\n", id, animIndex);
|
||||
default : LOG("unknown special cmd %d (anim %d)\n", fx, animation.index);
|
||||
}
|
||||
} else
|
||||
playSound(id, pos, Sound::Flags::PAN);
|
||||
playSound(fx, pos, Sound::Flags::PAN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -700,21 +411,20 @@ struct Controller {
|
||||
}
|
||||
}
|
||||
|
||||
doCustomCommand(frameIndex, animPrevFrame);
|
||||
if (animation.frameIndex != animation.framePrev)
|
||||
doCustomCommand(animation.frameIndex, animation.framePrev);
|
||||
|
||||
if (endFrame) { // if animation is end - switch to next
|
||||
setAnimation(anim->nextAnimation, anim->nextFrame);
|
||||
if (animation.isEnded) { // if animation is end - switch to next
|
||||
if (animation.offset != 0.0f) cmdOffset(animation.offset);
|
||||
if (animation.jump != 0.0f) cmdJump(animation.jump);
|
||||
animation.playNext();
|
||||
activateNext();
|
||||
} else
|
||||
animPrevFrame = frameIndex;
|
||||
animation.framePrev = animation.frameIndex;
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
updateBegin();
|
||||
updateState();
|
||||
updateAnimation(true);
|
||||
updateVelocity();
|
||||
updateEnd();
|
||||
updateAnimation(true);
|
||||
}
|
||||
|
||||
void renderMesh(const mat4 &matrix, MeshBuilder *mesh, uint32 offsetIndex) {
|
||||
@ -739,139 +449,40 @@ struct Controller {
|
||||
mesh->renderShadowSpot();
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh) {
|
||||
TR::Entity &entity = getEntity();
|
||||
TR::Model &model = getModel();
|
||||
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
mat4 matrix(Core::mModel);
|
||||
mat4 getMatrix() {
|
||||
mat4 matrix;
|
||||
matrix.identity();
|
||||
matrix.translate(pos);
|
||||
if (angle.y != 0.0f) matrix.rotateY(angle.y);
|
||||
if (angle.y != 0.0f) matrix.rotateY(angle.y - (animation.flip ? PI * animation.delta : 0.0f));
|
||||
if (angle.x != 0.0f) matrix.rotateX(angle.x);
|
||||
if (angle.z != 0.0f) matrix.rotateZ(angle.z);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
float t;
|
||||
vec3 move(0.0f);
|
||||
TR::AnimFrame *frameA, *frameB;
|
||||
getFrames(&frameA, &frameB, t, animIndex, animTime, true, &move);
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh) { // TODO: animation.calcJoints
|
||||
mat4 matrix = getMatrix();
|
||||
|
||||
vec3 bmin = frameA->box.min().lerp(frameB->box.min(), t);
|
||||
vec3 bmax = frameA->box.max().lerp(frameB->box.max(), t);
|
||||
if (frustum && !frustum->isVisible(matrix, bmin, bmax))
|
||||
Box box = animation.getBoundingBox(vec3(0, 0, 0), 0);
|
||||
if (frustum && !frustum->isVisible(matrix, box.min, box.max))
|
||||
return;
|
||||
|
||||
TR::Entity &entity = getEntity();
|
||||
TR::Model *model = getModel();
|
||||
entity.flags.rendered = true;
|
||||
|
||||
TR::Node *node = (int)model.node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model.node] : NULL;
|
||||
mat4 joints[32]; // TODO: UBO heap
|
||||
ASSERT(model->mCount <= 32);
|
||||
|
||||
matrix.translate(((vec3)frameA->pos).lerp(move + frameB->pos, t));
|
||||
|
||||
int sIndex = 0;
|
||||
mat4 stack[20];
|
||||
|
||||
for (int i = 0; i < model.mCount; i++) {
|
||||
|
||||
if (i > 0 && node) {
|
||||
TR::Node &t = node[i - 1];
|
||||
|
||||
if (t.flags & 0x01) matrix = stack[--sIndex];
|
||||
if (t.flags & 0x02) stack[sIndex++] = matrix;
|
||||
|
||||
ASSERT(sIndex >= 0 && sIndex < 20);
|
||||
|
||||
matrix.translate(vec3(t.x, t.y, t.z));
|
||||
}
|
||||
|
||||
quat q;
|
||||
if (animOverrideMask & (1 << i))
|
||||
q = animOverrides[i];
|
||||
else
|
||||
q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), t);
|
||||
matrix = matrix * mat4(q, vec3(0.0f));
|
||||
|
||||
if (meshes)
|
||||
renderMesh(matrix, mesh, meshes[i]);
|
||||
else
|
||||
renderMesh(matrix, mesh, model.mStart + i);
|
||||
|
||||
if (joints)
|
||||
joints[i] = matrix;
|
||||
}
|
||||
animation.getJoints(matrix, -1, true, joints);
|
||||
for (int i = 0; i < model->mCount; i++)
|
||||
renderMesh(joints[i], mesh, meshes ? meshes[i] : (model->mStart + i));
|
||||
|
||||
if (TR::castShadow(entity.type)) {
|
||||
TR::Level::FloorInfo info;
|
||||
level->getFloorInfo(entity.room, entity.x, entity.z, info, true);
|
||||
renderShadow(mesh, vec3(entity.x, info.floor - 16.0f, entity.z), (bmax + bmin) * 0.5f, (bmax - bmin) * 0.8f, entity.rotation);
|
||||
renderShadow(mesh, vec3(float(entity.x), info.floor - 16.0f, float(entity.z)), box.center(), box.size() * 0.8f, angle.y);
|
||||
}
|
||||
}
|
||||
|
||||
quat lerpFrames(TR::AnimFrame *frameA, TR::AnimFrame *frameB, float t, int index) {
|
||||
return lerpAngle(frameA->getAngle(index), frameB->getAngle(index), t);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SpriteController : Controller {
|
||||
|
||||
enum {
|
||||
FRAME_ANIMATED = -1,
|
||||
FRAME_RANDOM = -2,
|
||||
};
|
||||
|
||||
int frame, flag;
|
||||
bool instant;
|
||||
|
||||
SpriteController(TR::Level *level, int entity, bool instant = true, int frame = FRAME_ANIMATED) : Controller(level, entity), instant(instant), flag(frame) {
|
||||
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() {
|
||||
if (flag >= 0) return;
|
||||
|
||||
bool remove = false;
|
||||
animTime += Core::deltaTime;
|
||||
|
||||
if (flag == FRAME_ANIMATED) {
|
||||
frame = int(animTime * SPRITE_FPS);
|
||||
TR::SpriteSequence &seq = getSequence();
|
||||
if (instant && frame >= seq.sCount)
|
||||
remove = true;
|
||||
else
|
||||
frame %= seq.sCount;
|
||||
} else
|
||||
if (instant && animTime >= (1.0f / SPRITE_FPS))
|
||||
remove = true;
|
||||
|
||||
if (remove) {
|
||||
level->entityRemove(entity);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh) {
|
||||
mat4 m(Core::mModel);
|
||||
m.translate(pos);
|
||||
Core::active.shader->setParam(uModel, m);
|
||||
mesh->renderSprite(-(getEntity().modelIndex + 1), frame);
|
||||
}
|
||||
};
|
||||
|
||||
void addSprite(TR::Level *level, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1) {
|
||||
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 SpriteController(level, index, true, frame);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
20
src/debug.h
20
src/debug.h
@ -449,13 +449,13 @@ namespace Debug {
|
||||
TR::Node *node = m.node < level.nodesDataSize ? (TR::Node*)&level.nodesData[m.node] : NULL;
|
||||
|
||||
if (!node) continue; // ???
|
||||
|
||||
/*
|
||||
if (e.type == m.type) {
|
||||
ASSERT(m.animation < 0xFFFF);
|
||||
|
||||
int fSize = sizeof(TR::AnimFrame) + m.mCount * sizeof(uint16) * 2;
|
||||
|
||||
TR::Animation *anim = controller ? &level.anims[controller->animIndex] : &level.anims[m.animation];
|
||||
TR::Animation *anim = controller->animation;
|
||||
TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[(anim->frameOffset + (controller ? int((controller->animTime * 30.0f / anim->frameRate)) * fSize : 0) >> 1)];
|
||||
|
||||
//mat4 m;
|
||||
@ -495,35 +495,39 @@ 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->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);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
Debug::Draw::box(matrix, frame->box.min(), frame->box.max(), vec4(1.0));
|
||||
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void info(const TR::Level &level, const TR::Entity &entity, int state, int anim, int frame) {
|
||||
void info(const TR::Level &level, const TR::Entity &entity, Animation &anim) {
|
||||
char buf[255];
|
||||
sprintf(buf, "DIP = %d, TRI = %d, SND = %d", Core::stats.dips, Core::stats.tris, Sound::channelsCount);
|
||||
Debug::Draw::text(vec2(16, 16), vec4(1.0f), buf);
|
||||
sprintf(buf, "pos = (%d, %d, %d), room = %d, state = %d, anim = %d, frame = %d", entity.x, entity.y, entity.z, entity.room, state, anim, frame);
|
||||
sprintf(buf, "pos = (%d, %d, %d), room = %d", entity.x, entity.y, entity.z, entity.room);
|
||||
Debug::Draw::text(vec2(16, 32), vec4(1.0f), buf);
|
||||
int rate = anim.anims[anim.index].frameRate;
|
||||
sprintf(buf, "state = %d, anim = %d, next = %d, rate = %d, frame = %.2f / %d (%f)", anim.state, anim.index, anim.next, rate, anim.time * 30.0f, anim.framesCount, anim.delta);
|
||||
Debug::Draw::text(vec2(16, 48), vec4(1.0f), buf);
|
||||
|
||||
TR::Level::FloorInfo info;
|
||||
level.getFloorInfo(entity.room, entity.x, entity.z, info);
|
||||
sprintf(buf, "floor = %d, roomBelow = %d, roomAbove = %d, height = %d", info.floorIndex, info.roomBelow, info.roomAbove, info.floor - info.ceiling);
|
||||
Debug::Draw::text(vec2(16, 48), vec4(1.0f), buf);
|
||||
Debug::Draw::text(vec2(16, 64), vec4(1.0f), buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
120
src/enemy.h
120
src/enemy.h
@ -1,26 +1,11 @@
|
||||
#ifndef H_ENEMY
|
||||
#define H_ENEMY
|
||||
|
||||
#include "controller.h"
|
||||
#include "character.h"
|
||||
|
||||
struct Enemy : Controller {
|
||||
int target;
|
||||
quat rotHead, rotChest;
|
||||
int baseAnim;
|
||||
struct Enemy : Character {
|
||||
|
||||
Enemy(TR::Level *level, int entity) : Controller(level, entity), target(-1) {
|
||||
initAnimOverrides();
|
||||
rotHead = rotChest = quat(0, 0, 0, 1);
|
||||
baseAnim = animIndex;
|
||||
}
|
||||
|
||||
virtual Stand getStand() {
|
||||
return STAND_GROUND;
|
||||
}
|
||||
|
||||
virtual void hit(int damage) {
|
||||
health -= damage;
|
||||
};
|
||||
Enemy(TR::Level *level, int entity, int health) : Character(level, entity, health) {}
|
||||
|
||||
virtual bool activate(ActionCommand *cmd) {
|
||||
Controller::activate(cmd);
|
||||
@ -38,66 +23,40 @@ struct Enemy : Controller {
|
||||
}
|
||||
|
||||
virtual void updateVelocity() {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
float speed = anim->speed + anim->accel * (animTime * 30.0f);
|
||||
TR::Animation *anim = animation;
|
||||
float speed = anim->speed + anim->accel * (animation.time * 30.0f);
|
||||
velocity = getDir() * speed;
|
||||
}
|
||||
|
||||
virtual void move() {
|
||||
virtual void updatePosition() {
|
||||
if (!getEntity().flags.active) return;
|
||||
vec3 p = pos;
|
||||
pos += velocity * Core::deltaTime * 30.0f;
|
||||
TR::Level::FloorInfo info;
|
||||
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info);
|
||||
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info, true);
|
||||
if (pos.y - info.floor > 1024) {
|
||||
pos = p;
|
||||
return;
|
||||
}
|
||||
if (stand == STAND_GROUND)
|
||||
pos.y = info.floor;
|
||||
pos.y = float(info.floor);
|
||||
updateEntity();
|
||||
checkRoom();
|
||||
}
|
||||
|
||||
virtual void checkRoom() {
|
||||
TR::Level::FloorInfo info;
|
||||
TR::Entity &e = getEntity();
|
||||
level->getFloorInfo(e.room, e.x, e.z, info);
|
||||
|
||||
if (info.roomNext != 0xFF)
|
||||
e.room = info.roomNext;
|
||||
|
||||
if (info.roomBelow != 0xFF && e.y > info.floor)
|
||||
e.room = info.roomBelow;
|
||||
|
||||
if (info.roomAbove != 0xFF && e.y <= info.ceiling) {
|
||||
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
|
||||
stand = STAND_ONWATER;
|
||||
velocity.y = 0;
|
||||
pos.y = info.ceiling;
|
||||
updateEntity();
|
||||
} else
|
||||
if (stand != STAND_ONWATER)
|
||||
e.room = info.roomAbove;
|
||||
}
|
||||
}
|
||||
|
||||
void setOverrides(bool active, int chest, int head) {
|
||||
int mask = 0;
|
||||
if (head > -1) mask |= (1 << head);
|
||||
if (chest > -1) mask |= (1 << chest);
|
||||
if (active && head > -1) {
|
||||
animation.overrides[head] = animation.getJointRot(head);
|
||||
animation.overrideMask |= (1 << head);
|
||||
} else
|
||||
animation.overrideMask &= ~(1 << head);
|
||||
|
||||
if (active)
|
||||
animOverrideMask |= mask;
|
||||
else
|
||||
animOverrideMask &= ~mask;
|
||||
|
||||
TR::AnimFrame *frameA, *frameB;
|
||||
float t;
|
||||
|
||||
getFrames(&frameA, &frameB, t, animIndex, animTime, true);
|
||||
animOverrides[chest] = lerpFrames(frameA, frameB, t, chest);
|
||||
animOverrides[head] = lerpFrames(frameA, frameB, t, head);
|
||||
if (active && chest > -1) {
|
||||
animation.overrides[chest] = animation.getJointRot(chest);
|
||||
animation.overrideMask |= (1 << chest);
|
||||
} else
|
||||
animation.overrideMask &= ~(1 << chest);
|
||||
}
|
||||
|
||||
void lookAt(int target, int chest, int head) {
|
||||
@ -105,11 +64,11 @@ struct Enemy : Controller {
|
||||
quat rot;
|
||||
|
||||
if (chest > -1) {
|
||||
if (aim(target, chest, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.75f, PI * 0.75f), rot))
|
||||
if (aim(target, chest, vec4(-PI * 0.8f, PI * 0.8f, -PI * 0.75f, PI * 0.75f), rot))
|
||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||
else
|
||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
||||
animOverrides[chest] = rotChest * animOverrides[chest];
|
||||
animation.overrides[chest] = rotChest * animation.overrides[chest];
|
||||
}
|
||||
|
||||
if (head > -1) {
|
||||
@ -117,14 +76,16 @@ struct Enemy : Controller {
|
||||
rotHead = rotHead.slerp(rot, speed);
|
||||
else
|
||||
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
|
||||
animOverrides[head] = rotHead * animOverrides[head];
|
||||
animation.overrides[head] = rotHead * animation.overrides[head];
|
||||
}
|
||||
}
|
||||
|
||||
virtual int getInputMask() {
|
||||
virtual int getInput() {
|
||||
if (target > -1) {
|
||||
vec3 v = (((Controller*)level->entities[target].controller)->pos - pos).normal();
|
||||
float d = atan2(v.x, v.z) - angle.y;
|
||||
vec3 a = (((Controller*)level->entities[target].controller)->pos - pos).normal();
|
||||
vec3 b = getDir();
|
||||
vec3 n = vec3(0, 1, 0);
|
||||
float d = atan2(b.cross(a).dot(n), a.dot(b));
|
||||
if (fabsf(d) > 0.01f)
|
||||
return d < 0 ? LEFT : RIGHT;
|
||||
}
|
||||
@ -135,6 +96,9 @@ struct Enemy : Controller {
|
||||
|
||||
#define WOLF_TURN_FAST PI
|
||||
#define WOLF_TURN_SLOW (PI / 3.0f)
|
||||
#define WOLF_TILT_MAX (PI / 6.0f)
|
||||
#define WOLF_TILT_SPEED WOLF_TILT_MAX
|
||||
|
||||
|
||||
struct Wolf : Enemy {
|
||||
|
||||
@ -163,7 +127,7 @@ struct Wolf : Enemy {
|
||||
JOINT_HEAD = 3
|
||||
};
|
||||
|
||||
Wolf(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
Wolf(TR::Level *level, int entity) : Enemy(level, entity, 100) {}
|
||||
|
||||
virtual int getStateGround() {
|
||||
// STATE_SLEEP -> STATE_STOP
|
||||
@ -178,9 +142,9 @@ struct Wolf : Enemy {
|
||||
|
||||
if (health <= 0) {
|
||||
switch (state) {
|
||||
case STATE_RUN : return setAnimation(baseAnim + ANIM_DEATH_RUN);
|
||||
case STATE_JUMP : return setAnimation(baseAnim + ANIM_DEATH_JUMP);
|
||||
default : return setAnimation(baseAnim + ANIM_DEATH);
|
||||
case STATE_RUN : return animation.setAnim(ANIM_DEATH_RUN);
|
||||
case STATE_JUMP : return animation.setAnim(ANIM_DEATH_JUMP);
|
||||
default : return animation.setAnim(ANIM_DEATH);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,7 +155,7 @@ struct Wolf : Enemy {
|
||||
switch (state) {
|
||||
case STATE_SLEEP : return STATE_STOP;
|
||||
case STATE_STOP : return STATE_HOWL;
|
||||
case STATE_GROWL : return STATE_STALKING;
|
||||
case STATE_GROWL : return randf() > 0.5f ? STATE_STALKING : STATE_RUN;
|
||||
case STATE_STALKING : if (health < 70) return STATE_RUN; break;
|
||||
}
|
||||
|
||||
@ -215,7 +179,7 @@ struct Wolf : Enemy {
|
||||
float w = 0.0f;
|
||||
if (state == STATE_RUN || state == STATE_STALKING) {
|
||||
w = state == STATE_RUN ? WOLF_TURN_FAST : WOLF_TURN_SLOW;
|
||||
if (mask & LEFT) w = -w;
|
||||
if (input & LEFT) w = -w;
|
||||
|
||||
if (w != 0.0f) {
|
||||
w *= Core::deltaTime;
|
||||
@ -226,12 +190,14 @@ struct Wolf : Enemy {
|
||||
velocity = vec3(0.0f);
|
||||
}
|
||||
|
||||
virtual void move() {
|
||||
virtual void updatePosition() {
|
||||
updateTilt(state == STATE_RUN, WOLF_TILT_SPEED, WOLF_TILT_MAX);
|
||||
|
||||
if (state == STATE_DEATH) {
|
||||
animOverrideMask = 0;
|
||||
animation.overrideMask = 0;
|
||||
return;
|
||||
}
|
||||
Enemy::move();
|
||||
Enemy::updatePosition();
|
||||
setOverrides(state == STATE_STALKING || state == STATE_RUN, JOINT_CHEST, JOINT_HEAD);
|
||||
lookAt(target, JOINT_CHEST, JOINT_HEAD);
|
||||
}
|
||||
@ -244,7 +210,7 @@ struct Bear : Enemy {
|
||||
STATE_STOP = 1,
|
||||
};
|
||||
|
||||
Bear(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
Bear(TR::Level *level, int entity) : Enemy(level, entity, 100) {}
|
||||
|
||||
virtual int getStateGround() {
|
||||
return state;
|
||||
@ -259,14 +225,14 @@ struct Bat : Enemy {
|
||||
STATE_FLY = 2,
|
||||
};
|
||||
|
||||
Bat(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
Bat(TR::Level *level, int entity) : Enemy(level, entity, 100) {}
|
||||
|
||||
virtual Stand getStand() {
|
||||
return STAND_AIR;
|
||||
}
|
||||
|
||||
virtual int getStateAir() {
|
||||
animTime = 0.0f;
|
||||
animation.time = 0.0f;
|
||||
return STATE_AWAKE;
|
||||
}
|
||||
};
|
||||
|
40
src/format.h
40
src/format.h
@ -5,13 +5,14 @@
|
||||
|
||||
#define MAX_RESERVED_ENTITIES 64
|
||||
#define MAX_SECRETS_COUNT 16
|
||||
#define MAX_TRIGGER_COMMANDS 32
|
||||
|
||||
namespace TR {
|
||||
|
||||
enum {
|
||||
ANIM_CMD_NONE ,
|
||||
ANIM_CMD_MOVE ,
|
||||
ANIM_CMD_SPEED ,
|
||||
ANIM_CMD_OFFSET ,
|
||||
ANIM_CMD_JUMP ,
|
||||
ANIM_CMD_EMPTY ,
|
||||
ANIM_CMD_KILL ,
|
||||
ANIM_CMD_SOUND ,
|
||||
@ -106,6 +107,15 @@ namespace TR {
|
||||
SND_SECRET = 173,
|
||||
};
|
||||
|
||||
enum {
|
||||
MODEL_LARA = 0,
|
||||
MODEL_PISTOLS = 1,
|
||||
MODEL_SHOTGUN = 2,
|
||||
MODEL_MAGNUMS = 3,
|
||||
MODEL_UZIS = 4,
|
||||
MODEL_LARA_SPEC = 5,
|
||||
};
|
||||
|
||||
enum Action : uint16 {
|
||||
ACTIVATE , // activate item
|
||||
CAMERA_SWITCH , // switch to camera
|
||||
@ -419,6 +429,17 @@ namespace TR {
|
||||
bool isEnemy() {
|
||||
return type >= ENEMY_TWIN && type <= ENEMY_LARSON;
|
||||
}
|
||||
|
||||
int isItem() {
|
||||
return (type >= WEAPON_PISTOLS && type <= AMMO_UZIS) ||
|
||||
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
|
||||
(type >= KEY_1 && type <= KEY_4) ||
|
||||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == ARTIFACT || type == PICKUP);
|
||||
}
|
||||
|
||||
bool isBlock() {
|
||||
return type >= TR::Entity::BLOCK_1 && type <= TR::Entity::BLOCK_2;
|
||||
}
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
@ -463,11 +484,13 @@ namespace TR {
|
||||
vec3 max() const { return vec3((float)maxX, (float)maxY, (float)maxZ); }
|
||||
};
|
||||
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4200 ) // zero-sized array warning
|
||||
struct AnimFrame {
|
||||
MinMax box;
|
||||
Vertex pos; // Starting offset for this model
|
||||
Vertex pos;
|
||||
int16 aCount;
|
||||
uint16 angles[0]; // angle frames in YXZ order
|
||||
uint16 angles[0]; // angle frames in YXZ order
|
||||
|
||||
vec3 getAngle(int index) {
|
||||
#define ANGLE_SCALE (2.0f * PI / 1024.0f)
|
||||
@ -475,7 +498,7 @@ namespace TR {
|
||||
uint16 b = angles[index * 2 + 0];
|
||||
uint16 a = angles[index * 2 + 1];
|
||||
|
||||
return vec3((a & 0x3FF0) >> 4, ( ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10)), b & 0x03FF) * ANGLE_SCALE;
|
||||
return vec3(float((a & 0x3FF0) >> 4), float( ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10)), float(b & 0x03FF)) * ANGLE_SCALE;
|
||||
}
|
||||
};
|
||||
|
||||
@ -483,6 +506,7 @@ namespace TR {
|
||||
int16 count; // number of texture offsets - 1 in group
|
||||
int16 textures[0]; // offsets into objectTextures[]
|
||||
};
|
||||
#pragma warning( push )
|
||||
|
||||
struct Node {
|
||||
uint32 flags;
|
||||
@ -715,7 +739,7 @@ namespace TR {
|
||||
int trigCmdCount;
|
||||
Trigger trigger;
|
||||
FloorData::TriggerInfo trigInfo;
|
||||
FloorData::TriggerCommand trigCmd[16];
|
||||
FloorData::TriggerCommand trigCmd[MAX_TRIGGER_COMMANDS];
|
||||
|
||||
vec3 getNormal() {
|
||||
return vec3((float)-slantX, -4.0f, (float)-slantZ).normal();
|
||||
@ -734,7 +758,8 @@ namespace TR {
|
||||
bool secrets[MAX_SECRETS_COUNT];
|
||||
void *cameraController;
|
||||
|
||||
Level(Stream &stream, bool demo) {
|
||||
Level(const char *name, bool demo) {
|
||||
Stream stream(name);
|
||||
// read version
|
||||
stream.read(version);
|
||||
// tiles
|
||||
@ -1039,6 +1064,7 @@ namespace TR {
|
||||
info.trigInfo = (*fd++).triggerInfo;
|
||||
FloorData::TriggerCommand trigCmd;
|
||||
do {
|
||||
ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS);
|
||||
trigCmd = (*fd++).triggerCmd; // trigger action
|
||||
info.trigCmd[info.trigCmdCount++] = trigCmd;
|
||||
} while (!trigCmd.end);
|
||||
|
11
src/game.h
11
src/game.h
@ -12,8 +12,9 @@ namespace Game {
|
||||
|
||||
void init() {
|
||||
Core::init();
|
||||
Stream stream("LEVEL2_DEMO.PHD");
|
||||
level = new Level(stream, true);
|
||||
level = new Level("LEVEL2_DEMO.PHD", true, false);
|
||||
//level = new Level("GYM.PHD", false, true);
|
||||
//level = new Level("LEVEL4.PHD", false, false);
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
||||
@ -29,7 +30,13 @@ namespace Game {
|
||||
}
|
||||
|
||||
void update() {
|
||||
float dt = Core::deltaTime;
|
||||
if (Input::down[ikR]) // slow motion (for animation debugging)
|
||||
Core::deltaTime /= 50.0f;
|
||||
|
||||
level->update();
|
||||
|
||||
Core::deltaTime = dt;
|
||||
}
|
||||
|
||||
void render() {
|
||||
|
685
src/lara.h
685
src/lara.h
File diff suppressed because it is too large
Load Diff
24
src/level.h
24
src/level.h
@ -30,7 +30,7 @@ struct Level {
|
||||
|
||||
float time;
|
||||
|
||||
Level(Stream &stream, bool demo) : level{stream, demo}, time(0.0f), lara(NULL) {
|
||||
Level(const char *name, bool demo, bool home) : level(name, demo), time(0.0f), lara(NULL) {
|
||||
#ifdef _DEBUG
|
||||
Debug::init();
|
||||
#endif
|
||||
@ -44,8 +44,10 @@ struct Level {
|
||||
TR::Entity &entity = level.entities[i];
|
||||
switch (entity.type) {
|
||||
case TR::Entity::LARA :
|
||||
entity.controller = (lara = new Lara(&level, i, home));
|
||||
break;
|
||||
case TR::Entity::LARA_CUT :
|
||||
entity.controller = (lara = new Lara(&level, i));
|
||||
entity.controller = (lara = new Lara(&level, i, false));
|
||||
break;
|
||||
case TR::Entity::ENEMY_WOLF :
|
||||
entity.controller = new Wolf(&level, i);
|
||||
@ -71,7 +73,7 @@ struct Level {
|
||||
case TR::Entity::ENEMY_CENTAUR :
|
||||
case TR::Entity::ENEMY_MUMMY :
|
||||
case TR::Entity::ENEMY_LARSON :
|
||||
entity.controller = new Enemy(&level, i);
|
||||
entity.controller = new Enemy(&level, i, 100);
|
||||
break;
|
||||
case TR::Entity::DOOR_1 :
|
||||
case TR::Entity::DOOR_2 :
|
||||
@ -108,7 +110,7 @@ struct Level {
|
||||
if (entity.modelIndex > 0)
|
||||
entity.controller = new Controller(&level, i);
|
||||
else
|
||||
entity.controller = new SpriteController(&level, i, 0);
|
||||
entity.controller = new Sprite(&level, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +234,7 @@ struct Level {
|
||||
PROFILE_MARKER("ROOM");
|
||||
|
||||
TR::Room &room = level.rooms[roomIndex];
|
||||
vec3 offset = vec3(room.info.x, 0.0f, room.info.z);
|
||||
vec3 offset = vec3(float(room.info.x), 0.0f, float(room.info.z));
|
||||
|
||||
Shader *sh = setRoomShader(room, 1.0f);
|
||||
|
||||
@ -251,9 +253,11 @@ struct Level {
|
||||
TR::StaticMesh *sMesh = level.getMeshByID(rMesh.meshID);
|
||||
ASSERT(sMesh != NULL);
|
||||
|
||||
if (!mesh->meshMap[sMesh->mesh]) continue;
|
||||
|
||||
// check visibility
|
||||
Box box;
|
||||
vec3 offset = vec3(rMesh.x, rMesh.y, rMesh.z);
|
||||
vec3 offset = vec3((float)rMesh.x, (float)rMesh.y, (float)rMesh.z);
|
||||
sMesh->getBox(false, rMesh.rotation, box);
|
||||
if (!camera->frustum->isVisible(offset + box.min, offset + box.max))
|
||||
continue;
|
||||
@ -343,7 +347,7 @@ struct Level {
|
||||
int j = room;
|
||||
for (int i = 0; i < level.rooms[j].lightsCount; i++) {
|
||||
TR::Room::Light &light = level.rooms[j].lights[i];
|
||||
float d = (pos - vec3(light.x, light.y, light.z)).length2();
|
||||
float d = (pos - vec3(float(light.x), float(light.y), float(light.z))).length2();
|
||||
if (idx == -1 || d < dist) {
|
||||
idx = i;
|
||||
dist = d;
|
||||
@ -360,7 +364,7 @@ struct Level {
|
||||
if (idx > -1) {
|
||||
TR::Room::Light &light = level.rooms[room].lights[idx];
|
||||
float c = level.rooms[room].lights[idx].intensity / 8191.0f;
|
||||
Core::lightPos[0] = vec3(light.x, light.y, light.z);
|
||||
Core::lightPos[0] = vec3(float(light.x), float(light.y), float(light.z));
|
||||
Core::lightColor[0] = vec4(c, c, c, (float)light.attenuation * (float)light.attenuation);
|
||||
} else {
|
||||
Core::lightPos[0] = vec3(0);
|
||||
@ -389,7 +393,7 @@ struct Level {
|
||||
setRoomShader(room, c)->bind();
|
||||
Core::active.shader->setParam(uColor, Core::color);
|
||||
// get light parameters for entity
|
||||
getLight(vec3(entity.x, entity.y, entity.z), entity.room);
|
||||
getLight(((Controller*)entity.controller)->pos, entity.room);
|
||||
}
|
||||
|
||||
if (entity.modelIndex < 0) { // sprite
|
||||
@ -482,7 +486,7 @@ struct Level {
|
||||
// Debug::Level::portals(level);
|
||||
// Debug::Level::meshes(level);
|
||||
// Debug::Level::entities(level);
|
||||
Debug::Level::info(level, lara->getEntity(), (int)lara->state, lara->animIndex, int(lara->animTime * 30.0f));
|
||||
Debug::Level::info(level, lara->getEntity(), lara->animation);
|
||||
Debug::end();
|
||||
#endif
|
||||
}
|
||||
|
@ -2504,16 +2504,16 @@ int mp3_decode_init() {
|
||||
for(i=0; i<512*16; i++){
|
||||
int exponent= (i>>4);
|
||||
double f= libc_pow(i&15, 4.0 / 3.0) * libc_pow(2, (exponent-400)*0.25 + FRAC_BITS + 5);
|
||||
expval_table[exponent][i&15]= f;
|
||||
expval_table[exponent][i&15]= uint32_t(f);
|
||||
if((i&15)==1)
|
||||
exp_table[exponent]= f;
|
||||
exp_table[exponent]= uint32_t(f);
|
||||
}
|
||||
|
||||
for(i=0;i<7;i++) {
|
||||
float f;
|
||||
int v;
|
||||
if (i != 6) {
|
||||
f = tan((double)i * M_PI / 12.0);
|
||||
f = float(tan((double)i * M_PI / 12.0));
|
||||
v = FIXR(f / (1.0 + f));
|
||||
} else {
|
||||
v = FIXR(1.0);
|
||||
@ -2522,7 +2522,7 @@ int mp3_decode_init() {
|
||||
is_table[1][6 - i] = v;
|
||||
}
|
||||
for(i=7;i<16;i++)
|
||||
is_table[0][i] = is_table[1][i] = 0.0;
|
||||
is_table[0][i] = is_table[1][i] = 0;
|
||||
|
||||
for(i=0;i<16;i++) {
|
||||
double f;
|
||||
@ -2540,7 +2540,7 @@ int mp3_decode_init() {
|
||||
for(i=0;i<8;i++) {
|
||||
float ci, cs, ca;
|
||||
ci = ci_table[i];
|
||||
cs = 1.0 / sqrt(1.0 + ci * ci);
|
||||
cs = float(1.0 / sqrt(1.0 + ci * ci));
|
||||
ca = cs * ci;
|
||||
csa_table[i][0] = FIXHR(cs/4);
|
||||
csa_table[i][1] = FIXHR(ca/4);
|
||||
|
@ -3,8 +3,8 @@
|
||||
<head><title>OpenLara</title></head>
|
||||
<body>
|
||||
<span id="status">Starting...</span>
|
||||
<canvas id="canvas" width="160" height="120" oncontextmenu="event.preventDefault()"></canvas><br>
|
||||
<span id="info"><a target="_blank" href="https://github.com/XProger/OpenLara">OpenLara on github</a><br>controls:<br>keyboad: move - WASD / arrows, jump - Space, action - E/Ctrl, draw weapon - Q, change weapon - 1-4, walk - Shift, side steps - ZX/walk+direction, camera - MouseR)<br>gamepad: PSX controls on XBox controller</span>
|
||||
<canvas id="canvas" width="16" height="16" oncontextmenu="event.preventDefault()"></canvas><br>
|
||||
<span id="info"><a target="_blank" href="https://github.com/XProger/OpenLara">OpenLara on github</a><br>controls:<br>keyboad: move - WASD / arrows, jump - Space, action - E/Ctrl, draw weapon - Q, change weapon - 1-4, walk - Shift, side steps - ZX/walk+direction, camera - MouseR)<br>gamepad: PSX controls on XBox controller<br>FullScreen: Alt + Enter</span>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var statusElement = document.getElementById('status');
|
||||
|
@ -151,7 +151,9 @@
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\animation.h" />
|
||||
<ClInclude Include="..\..\camera.h" />
|
||||
<ClInclude Include="..\..\character.h" />
|
||||
<ClInclude Include="..\..\controller.h" />
|
||||
<ClInclude Include="..\..\core.h" />
|
||||
<ClInclude Include="..\..\debug.h" />
|
||||
@ -166,6 +168,7 @@
|
||||
<ClInclude Include="..\..\mesh.h" />
|
||||
<ClInclude Include="..\..\shader.h" />
|
||||
<ClInclude Include="..\..\sound.h" />
|
||||
<ClInclude Include="..\..\sprite.h" />
|
||||
<ClInclude Include="..\..\texture.h" />
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\trigger.h" />
|
||||
|
@ -311,14 +311,12 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
|
||||
if (time <= lastTime)
|
||||
continue;
|
||||
|
||||
float slow = Input::down[ikR] ? 8.0f : 1.0f;
|
||||
|
||||
float delta = min(1.0f, (time - lastTime) * 0.001f);
|
||||
EnterCriticalSection(&sndCS);
|
||||
while (delta > EPS) {
|
||||
Core::deltaTime = min(delta, 1.0f / 30.0f) / slow;
|
||||
Core::deltaTime = min(delta, 1.0f / 30.0f);
|
||||
Game::update();
|
||||
delta -= Core::deltaTime * slow;
|
||||
delta -= Core::deltaTime;
|
||||
}
|
||||
LeaveCriticalSection(&sndCS);
|
||||
lastTime = time;
|
||||
|
70
src/sprite.h
Normal file
70
src/sprite.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef H_SPRITE
|
||||
#define H_SPRITE
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
struct Sprite : Controller {
|
||||
|
||||
enum {
|
||||
FRAME_ANIMATED = -1,
|
||||
FRAME_RANDOM = -2,
|
||||
};
|
||||
|
||||
bool instant;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void add(TR::Level *level, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
TR::SpriteSequence& getSequence() {
|
||||
return level->spriteSequences[-(getEntity().modelIndex + 1)];
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (flag >= 0) return;
|
||||
|
||||
bool remove = false;
|
||||
time += Core::deltaTime;
|
||||
|
||||
if (flag == FRAME_ANIMATED) {
|
||||
frame = int(time * SPRITE_FPS);
|
||||
TR::SpriteSequence &seq = getSequence();
|
||||
if (instant && frame >= seq.sCount)
|
||||
remove = true;
|
||||
else
|
||||
frame %= seq.sCount;
|
||||
} else
|
||||
if (instant && time >= (1.0f / SPRITE_FPS))
|
||||
remove = true;
|
||||
|
||||
if (remove) {
|
||||
level->entityRemove(entity);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh) {
|
||||
mat4 m(Core::mModel);
|
||||
m.translate(pos);
|
||||
Core::active.shader->setParam(uModel, m);
|
||||
mesh->renderSprite(-(getEntity().modelIndex + 1), frame);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -2,6 +2,7 @@
|
||||
#define H_TRIGGER
|
||||
|
||||
#include "controller.h"
|
||||
#include "sprite.h"
|
||||
|
||||
struct Trigger : Controller {
|
||||
|
||||
@ -50,7 +51,7 @@ struct Trigger : Controller {
|
||||
}
|
||||
|
||||
if (!inState())
|
||||
setState(state != baseState ? baseState : (entity.type == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
|
||||
animation.setState(state != baseState ? baseState : (entity.type == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
|
||||
|
||||
updateAnimation(true);
|
||||
updateEntity();
|
||||
@ -58,7 +59,7 @@ struct Trigger : Controller {
|
||||
};
|
||||
|
||||
struct Dart : Controller {
|
||||
|
||||
vec3 velocity;
|
||||
vec3 dir;
|
||||
bool inWall; // dart starts from wall
|
||||
|
||||
@ -67,7 +68,8 @@ struct Dart : Controller {
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
velocity = dir * level->anims[animIndex].speed;
|
||||
TR::Animation *anim = animation;
|
||||
velocity = dir * animation.getSpeed();
|
||||
pos = pos + velocity * (Core::deltaTime * 30.0f);
|
||||
updateEntity();
|
||||
TR::Level::FloorInfo info;
|
||||
@ -77,7 +79,7 @@ struct Dart : Controller {
|
||||
TR::Entity &e = getEntity();
|
||||
|
||||
vec3 p = pos - dir * 64.0f; // wall offset = 64
|
||||
addSprite(level, TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, SpriteController::FRAME_RANDOM);
|
||||
Sprite::add(level, TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM);
|
||||
|
||||
level->entityRemove(entity);
|
||||
delete this;
|
||||
@ -99,14 +101,13 @@ struct Dartgun : Trigger {
|
||||
// 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);
|
||||
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
|
||||
|
||||
int dartIndex = level->entityAdd(TR::Entity::TRAP_DART, entity.room, (int)pos.x, (int)pos.y, (int)pos.z, entity.rotation, entity.intensity);
|
||||
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);
|
||||
|
||||
addSprite(level, TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z);
|
||||
Sprite::add(level, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
|
||||
|
||||
playSound(TR::SND_DART, pos, Sound::Flags::PAN);
|
||||
|
||||
@ -159,7 +160,7 @@ struct Block : Controller {
|
||||
level->getFloorInfo(e.room, e.x + (int)dir.x, e.z + (int)dir.z, info, true);
|
||||
if ((info.slantX | info.slantZ) || info.floor != e.y)
|
||||
return false;
|
||||
if (!setState(push ? STATE_PUSH : STATE_PULL))
|
||||
if (!animation.setState(push ? STATE_PUSH : STATE_PULL))
|
||||
return false;
|
||||
updateFloor(false);
|
||||
return true;
|
||||
|
13
src/utils.h
13
src/utils.h
@ -22,7 +22,7 @@
|
||||
#ifdef PROFILE
|
||||
#define LOG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG(...)
|
||||
#define LOG(...) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -115,6 +115,9 @@ struct vec3 {
|
||||
|
||||
float& operator [] (int index) const { return ((float*)this)[index]; }
|
||||
|
||||
bool operator == (float s) const { return x == s && y == s && z == s; }
|
||||
bool operator != (float s) const { return !(*this == s); }
|
||||
|
||||
vec3& operator += (const vec3 &v) { x += v.x; y += v.y; z += v.z; return *this; }
|
||||
vec3& operator -= (const vec3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
|
||||
vec3& operator *= (const vec3 &v) { x *= v.x; y *= v.y; z *= v.z; return *this; }
|
||||
@ -534,6 +537,14 @@ struct Box {
|
||||
Box() {}
|
||||
Box(const vec3 &min, const vec3 &max) : min(min), max(max) {}
|
||||
|
||||
vec3 center() const {
|
||||
return (min + max) * 0.5f;
|
||||
}
|
||||
|
||||
vec3 size() const {
|
||||
return max - min;
|
||||
}
|
||||
|
||||
void rotate90(int n) {
|
||||
switch (n) {
|
||||
case 0 : break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user