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() {
|
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) {
|
if (timer > 0.0f) {
|
||||||
timer -= Core::deltaTime;
|
timer -= Core::deltaTime;
|
||||||
@ -65,6 +67,7 @@ struct Camera : Controller {
|
|||||||
if (room != getRoomIndex())
|
if (room != getRoomIndex())
|
||||||
pos = lastDest;
|
pos = lastDest;
|
||||||
actTargetEntity = actCamera = -1;
|
actTargetEntity = actCamera = -1;
|
||||||
|
target = owner->getViewPoint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef FREE_CAMERA
|
#ifdef FREE_CAMERA
|
||||||
@ -91,28 +94,28 @@ struct Camera : Controller {
|
|||||||
angle.z = 0.0f;
|
angle.z = 0.0f;
|
||||||
//angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
|
//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;
|
vec3 dir;
|
||||||
target = target.lerp(owner->getViewPoint(), lerpFactor * Core::deltaTime);
|
target = target.lerp(owner->getViewPoint(), lerpFactor * Core::deltaTime);
|
||||||
|
|
||||||
if (actCamera > -1) {
|
if (actCamera > -1) {
|
||||||
TR::Camera &c = level->cameras[actCamera];
|
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())
|
if (room != getRoomIndex())
|
||||||
pos = destPos;
|
pos = destPos;
|
||||||
if (actTargetEntity > -1) {
|
if (lookAt > -1) {
|
||||||
TR::Entity &e = level->entities[actTargetEntity];
|
TR::Entity &e = level->entities[lookAt];
|
||||||
target = vec3(e.x, e.y, e.z);
|
target = ((Controller*)e.controller)->pos;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (actTargetEntity > -1) {
|
if (lookAt > -1) {
|
||||||
TR::Entity &e = level->entities[actTargetEntity];
|
TR::Entity &e = level->entities[lookAt];
|
||||||
dir = (vec3(e.x, e.y, e.z) - target).normal();
|
dir = (((Controller*)e.controller)->pos - target).normal();
|
||||||
} else
|
} else
|
||||||
dir = getDir();
|
dir = getDir();
|
||||||
|
|
||||||
int destRoom;
|
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;
|
vec3 eye = target - dir * CAMERA_OFFSET;
|
||||||
destPos = trace(owner->getRoomIndex(), target, eye, destRoom, true);
|
destPos = trace(owner->getRoomIndex(), target, eye, destRoom, true);
|
||||||
lastDest = destPos;
|
lastDest = destPos;
|
||||||
@ -138,7 +141,7 @@ struct Camera : Controller {
|
|||||||
room = info.roomAbove;
|
room = info.roomAbove;
|
||||||
else
|
else
|
||||||
if (info.ceiling != 0xffff8100)
|
if (info.ceiling != 0xffff8100)
|
||||||
pos.y = info.ceiling;
|
pos.y = (float)info.ceiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos.y > info.floor) {
|
if (pos.y > info.floor) {
|
||||||
@ -146,7 +149,7 @@ struct Camera : Controller {
|
|||||||
room = info.roomBelow;
|
room = info.roomBelow;
|
||||||
else
|
else
|
||||||
if (info.floor != 0xffff8100)
|
if (info.floor != 0xffff8100)
|
||||||
pos.y = info.floor;
|
pos.y = (float)info.floor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// play underwater sound when camera goes under water
|
// 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
|
535
src/controller.h
535
src/controller.h
@ -4,51 +4,25 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "frustum.h"
|
#include "frustum.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
#include "animation.h"
|
||||||
|
|
||||||
#define GRAVITY 6.0f
|
#define GRAVITY 6.0f
|
||||||
#define NO_OVERLAP 0x7FFFFFFF
|
#define NO_OVERLAP 0x7FFFFFFF
|
||||||
|
|
||||||
#define SPRITE_FPS 10.0f
|
#define SPRITE_FPS 10.0f
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
TR::Level *level;
|
TR::Level *level;
|
||||||
int entity;
|
int entity;
|
||||||
|
|
||||||
enum Stand {
|
Animation animation;
|
||||||
STAND_AIR, STAND_GROUND, STAND_SLIDE, STAND_HANG, STAND_UNDERWATER, STAND_ONWATER
|
int &state;
|
||||||
} stand;
|
|
||||||
int state;
|
|
||||||
int mask;
|
|
||||||
|
|
||||||
enum { LEFT = 1 << 1,
|
vec3 pos;
|
||||||
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 angle;
|
vec3 angle;
|
||||||
|
|
||||||
float angleExt;
|
|
||||||
|
|
||||||
int *meshes;
|
int *meshes;
|
||||||
int mCount;
|
int mCount;
|
||||||
|
|
||||||
// TODO: Character class
|
|
||||||
quat *animOverrides; // left & right arms animation frames
|
|
||||||
int animOverrideMask;
|
|
||||||
mat4 *joints;
|
|
||||||
int health;
|
|
||||||
float tilt;
|
|
||||||
|
|
||||||
struct ActionCommand {
|
struct ActionCommand {
|
||||||
TR::Action action;
|
TR::Action action;
|
||||||
int value;
|
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(TR::Action action, int value, float timer, ActionCommand *next = NULL) : action(action), value(value), timer(timer), next(next) {}
|
||||||
} *actionCommand;
|
} *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();
|
TR::Entity &e = getEntity();
|
||||||
pos = vec3((float)e.x, (float)e.y, (float)e.z);
|
pos = vec3((float)e.x, (float)e.y, (float)e.z);
|
||||||
angle = vec3(0.0f, e.rotation, 0.0f);
|
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() {
|
virtual ~Controller() {
|
||||||
delete[] meshes;
|
delete[] meshes;
|
||||||
delete[] animOverrides;
|
|
||||||
delete[] joints;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initMeshOverrides() {
|
void initMeshOverrides() {
|
||||||
TR::Model &model = getModel();
|
TR::Model *model = getModel();
|
||||||
mCount = model.mCount;
|
mCount = model->mCount;
|
||||||
meshes = mCount ? new int[mCount] : NULL;
|
meshes = mCount ? new int[mCount] : NULL;
|
||||||
for (int i = 0; i < mCount; i++)
|
for (int i = 0; i < mCount; i++)
|
||||||
meshes[i] = model.mStart + i;
|
meshes[i] = model->mStart + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initAnimOverrides() {
|
void meshSwap(TR::Model *model, int mask) {
|
||||||
TR::Model &model = getModel();
|
for (int i = 0; i < model->mCount; i++) {
|
||||||
|
int index = model->mStart + i;
|
||||||
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;
|
|
||||||
if (((1 << i) & mask) && level->meshOffsets[index])
|
if (((1 << i) & mask) && level->meshOffsets[index])
|
||||||
meshes[i] = 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) {
|
bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
|
||||||
if (target > -1) {
|
if (target > -1) {
|
||||||
TR::Entity &e = level->entities[target];
|
TR::Entity &e = level->entities[target];
|
||||||
Box box = ((Controller*)e.controller)->getBoundingBox();
|
Box box = ((Controller*)e.controller)->getBoundingBox();
|
||||||
vec3 t = (box.min + box.max) * 0.5f;
|
vec3 t = (box.min + box.max) * 0.5f;
|
||||||
|
|
||||||
mat4 m = getJoint(joint);
|
mat4 m = animation.getJoints(getMatrix(), joint);
|
||||||
vec3 delta = (m.inverse() * t).normal();
|
vec3 delta = (m.inverse() * t).normal();
|
||||||
|
|
||||||
float angleY = clampAngle(atan2(delta.x, delta.z));
|
float angleY = clampAngle(atan2(delta.x, delta.z));
|
||||||
@ -233,16 +101,17 @@ struct Controller {
|
|||||||
|
|
||||||
bool insideRoom(const vec3 &pos, int room) const {
|
bool insideRoom(const vec3 &pos, int room) const {
|
||||||
TR::Room &r = level->rooms[room];
|
TR::Room &r = level->rooms[room];
|
||||||
vec3 min = vec3(r.info.x, r.info.yTop, r.info.z);
|
vec3 min = vec3((float)r.info.x, (float)r.info.yTop, (float)r.info.z);
|
||||||
vec3 max = min + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024);
|
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 &&
|
return pos.x >= min.x && pos.x <= max.x &&
|
||||||
pos.y >= min.y && pos.y <= max.y &&
|
pos.y >= min.y && pos.y <= max.y &&
|
||||||
pos.z >= min.z && pos.z <= max.z;
|
pos.z >= min.z && pos.z <= max.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
TR::Model& getModel() const {
|
TR::Model* getModel() const {
|
||||||
return level->models[getEntity().modelIndex - 1];
|
int index = getEntity().modelIndex;
|
||||||
|
return index > 0 ? &level->models[index - 1] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TR::Entity& getEntity() const {
|
TR::Entity& getEntity() const {
|
||||||
@ -259,64 +128,6 @@ struct Controller {
|
|||||||
return getEntity().room;
|
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 getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ) const {
|
||||||
int dx, dz;
|
int dx, dz;
|
||||||
TR::Room::Sector &s = level->getSector(getEntity().room, fromX, fromZ, dx, dz);
|
TR::Room::Sector &s = level->getSector(getEntity().room, fromX, fromZ, dx, dz);
|
||||||
@ -391,15 +202,7 @@ struct Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual Box getBoundingBox() {
|
virtual Box getBoundingBox() {
|
||||||
float t;
|
return animation.getBoundingBox(pos, getEntity().rotation.value / 0x4000);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham
|
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 maxX = minX + 1024;
|
||||||
int maxZ = minZ + 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();
|
dir = (pos - from).normal();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -472,7 +275,7 @@ struct Controller {
|
|||||||
if (rand() % 10 <= 6) return;
|
if (rand() % 10 <= 6) return;
|
||||||
playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN);
|
playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
void collide() {
|
void collide() {
|
||||||
TR::Entity &entity = getEntity();
|
TR::Entity &entity = getEntity();
|
||||||
|
|
||||||
@ -510,7 +313,7 @@ struct Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
void activateNext() { // activate next entity (for triggers)
|
void activateNext() { // activate next entity (for triggers)
|
||||||
if (!actionCommand || !actionCommand->next) {
|
if (!actionCommand || !actionCommand->next) {
|
||||||
actionCommand = NULL;
|
actionCommand = NULL;
|
||||||
@ -555,92 +358,26 @@ struct Controller {
|
|||||||
actionCommand = NULL;
|
actionCommand = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool activate(ActionCommand *cmd) { actionCommand = cmd; return true; }
|
virtual bool activate(ActionCommand *cmd) { actionCommand = cmd; return true; }
|
||||||
virtual void doCustomCommand (int curFrame, int prevFrame) {}
|
virtual void doCustomCommand (int curFrame, int prevFrame) {}
|
||||||
virtual void updateVelocity() {}
|
virtual void updateVelocity() {}
|
||||||
virtual void checkRoom() {}
|
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 int getState(Stand stand) {
|
virtual void cmdOffset(const vec3 &offset) {
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
pos = pos + offset.rotateY(-angle.y);
|
||||||
|
|
||||||
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();
|
|
||||||
updateEntity();
|
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) {
|
virtual void updateAnimation(bool commands) {
|
||||||
int frameIndex = int((animTime += Core::deltaTime) * 30.0f);
|
animation.update();
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
|
||||||
bool endFrame = frameIndex > anim->frameEnd - anim->frameStart;
|
TR::Animation *anim = animation;
|
||||||
|
|
||||||
// apply animation commands
|
// apply animation commands
|
||||||
if (commands) {
|
if (commands) {
|
||||||
@ -649,50 +386,24 @@ struct Controller {
|
|||||||
for (int i = 0; i < anim->acCount; i++) {
|
for (int i = 0; i < anim->acCount; i++) {
|
||||||
int cmd = *ptr++;
|
int cmd = *ptr++;
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case TR::ANIM_CMD_MOVE : { // cmd position
|
case TR::ANIM_CMD_OFFSET : ptr += 3; break;
|
||||||
int16 sx = *ptr++;
|
case TR::ANIM_CMD_JUMP : ptr += 2; break;
|
||||||
int16 sy = *ptr++;
|
case TR::ANIM_CMD_EMPTY : cmdEmpty(); break;
|
||||||
int16 sz = *ptr++;
|
case TR::ANIM_CMD_KILL : cmdKill(); break;
|
||||||
if (endFrame) {
|
case TR::ANIM_CMD_SOUND :
|
||||||
pos = pos + vec3(sx, sy, sz).rotateY(-angle.y);
|
case TR::ANIM_CMD_EFFECT : {
|
||||||
updateEntity();
|
int frame = (*ptr++) - anim->frameStart;
|
||||||
checkRoom();
|
int fx = (*ptr++) & 0x3FFF;
|
||||||
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
|
if (animation.isFrameActive(frame)) {
|
||||||
}
|
|
||||||
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) {
|
|
||||||
if (cmd == TR::ANIM_CMD_EFFECT) {
|
if (cmd == TR::ANIM_CMD_EFFECT) {
|
||||||
switch (id) {
|
switch (fx) {
|
||||||
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
|
||||||
case TR::EFFECT_LARA_BUBBLES : doBubbles(); break;
|
case TR::EFFECT_LARA_BUBBLES : doBubbles(); break;
|
||||||
case TR::EFFECT_LARA_HANDSFREE : 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
|
} else
|
||||||
playSound(id, pos, Sound::Flags::PAN);
|
playSound(fx, pos, Sound::Flags::PAN);
|
||||||
}
|
}
|
||||||
break;
|
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
|
if (animation.isEnded) { // if animation is end - switch to next
|
||||||
setAnimation(anim->nextAnimation, anim->nextFrame);
|
if (animation.offset != 0.0f) cmdOffset(animation.offset);
|
||||||
|
if (animation.jump != 0.0f) cmdJump(animation.jump);
|
||||||
|
animation.playNext();
|
||||||
activateNext();
|
activateNext();
|
||||||
} else
|
} else
|
||||||
animPrevFrame = frameIndex;
|
animation.framePrev = animation.frameIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update() {
|
virtual void update() {
|
||||||
updateBegin();
|
|
||||||
updateState();
|
|
||||||
updateAnimation(true);
|
updateAnimation(true);
|
||||||
updateVelocity();
|
|
||||||
updateEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderMesh(const mat4 &matrix, MeshBuilder *mesh, uint32 offsetIndex) {
|
void renderMesh(const mat4 &matrix, MeshBuilder *mesh, uint32 offsetIndex) {
|
||||||
@ -739,139 +449,40 @@ struct Controller {
|
|||||||
mesh->renderShadowSpot();
|
mesh->renderShadowSpot();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void render(Frustum *frustum, MeshBuilder *mesh) {
|
mat4 getMatrix() {
|
||||||
TR::Entity &entity = getEntity();
|
mat4 matrix;
|
||||||
TR::Model &model = getModel();
|
matrix.identity();
|
||||||
|
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
|
||||||
|
|
||||||
mat4 matrix(Core::mModel);
|
|
||||||
matrix.translate(pos);
|
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.x != 0.0f) matrix.rotateX(angle.x);
|
||||||
if (angle.z != 0.0f) matrix.rotateZ(angle.z);
|
if (angle.z != 0.0f) matrix.rotateZ(angle.z);
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
float t;
|
virtual void render(Frustum *frustum, MeshBuilder *mesh) { // TODO: animation.calcJoints
|
||||||
vec3 move(0.0f);
|
mat4 matrix = getMatrix();
|
||||||
TR::AnimFrame *frameA, *frameB;
|
|
||||||
getFrames(&frameA, &frameB, t, animIndex, animTime, true, &move);
|
|
||||||
|
|
||||||
vec3 bmin = frameA->box.min().lerp(frameB->box.min(), t);
|
Box box = animation.getBoundingBox(vec3(0, 0, 0), 0);
|
||||||
vec3 bmax = frameA->box.max().lerp(frameB->box.max(), t);
|
if (frustum && !frustum->isVisible(matrix, box.min, box.max))
|
||||||
if (frustum && !frustum->isVisible(matrix, bmin, bmax))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
TR::Entity &entity = getEntity();
|
||||||
|
TR::Model *model = getModel();
|
||||||
entity.flags.rendered = true;
|
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));
|
animation.getJoints(matrix, -1, true, joints);
|
||||||
|
for (int i = 0; i < model->mCount; i++)
|
||||||
int sIndex = 0;
|
renderMesh(joints[i], mesh, meshes ? meshes[i] : (model->mStart + i));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TR::castShadow(entity.type)) {
|
if (TR::castShadow(entity.type)) {
|
||||||
TR::Level::FloorInfo info;
|
TR::Level::FloorInfo info;
|
||||||
level->getFloorInfo(entity.room, entity.x, entity.z, info, true);
|
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
|
#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;
|
TR::Node *node = m.node < level.nodesDataSize ? (TR::Node*)&level.nodesData[m.node] : NULL;
|
||||||
|
|
||||||
if (!node) continue; // ???
|
if (!node) continue; // ???
|
||||||
|
/*
|
||||||
if (e.type == m.type) {
|
if (e.type == m.type) {
|
||||||
ASSERT(m.animation < 0xFFFF);
|
ASSERT(m.animation < 0xFFFF);
|
||||||
|
|
||||||
int fSize = sizeof(TR::AnimFrame) + m.mCount * sizeof(uint16) * 2;
|
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)];
|
TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[(anim->frameOffset + (controller ? int((controller->animTime * 30.0f / anim->frameRate)) * fSize : 0) >> 1)];
|
||||||
|
|
||||||
//mat4 m;
|
//mat4 m;
|
||||||
@ -495,35 +495,39 @@ namespace Debug {
|
|||||||
int offset = level.meshOffsets[m.mStart + k];
|
int offset = level.meshOffsets[m.mStart + k];
|
||||||
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[offset / 2];
|
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));
|
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) {
|
{ //if (e.id != 0) {
|
||||||
char buf[255];
|
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);
|
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::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));
|
Debug::Draw::box(matrix, frame->box.min(), frame->box.max(), vec4(1.0));
|
||||||
|
|
||||||
break;
|
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];
|
char buf[255];
|
||||||
sprintf(buf, "DIP = %d, TRI = %d, SND = %d", Core::stats.dips, Core::stats.tris, Sound::channelsCount);
|
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);
|
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);
|
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;
|
TR::Level::FloorInfo info;
|
||||||
level.getFloorInfo(entity.room, entity.x, entity.z, 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);
|
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
|
#ifndef H_ENEMY
|
||||||
#define H_ENEMY
|
#define H_ENEMY
|
||||||
|
|
||||||
#include "controller.h"
|
#include "character.h"
|
||||||
|
|
||||||
struct Enemy : Controller {
|
struct Enemy : Character {
|
||||||
int target;
|
|
||||||
quat rotHead, rotChest;
|
|
||||||
int baseAnim;
|
|
||||||
|
|
||||||
Enemy(TR::Level *level, int entity) : Controller(level, entity), target(-1) {
|
Enemy(TR::Level *level, int entity, int health) : Character(level, entity, health) {}
|
||||||
initAnimOverrides();
|
|
||||||
rotHead = rotChest = quat(0, 0, 0, 1);
|
|
||||||
baseAnim = animIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Stand getStand() {
|
|
||||||
return STAND_GROUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void hit(int damage) {
|
|
||||||
health -= damage;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool activate(ActionCommand *cmd) {
|
virtual bool activate(ActionCommand *cmd) {
|
||||||
Controller::activate(cmd);
|
Controller::activate(cmd);
|
||||||
@ -38,66 +23,40 @@ struct Enemy : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void updateVelocity() {
|
virtual void updateVelocity() {
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
TR::Animation *anim = animation;
|
||||||
float speed = anim->speed + anim->accel * (animTime * 30.0f);
|
float speed = anim->speed + anim->accel * (animation.time * 30.0f);
|
||||||
velocity = getDir() * speed;
|
velocity = getDir() * speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void move() {
|
virtual void updatePosition() {
|
||||||
if (!getEntity().flags.active) return;
|
if (!getEntity().flags.active) return;
|
||||||
vec3 p = pos;
|
vec3 p = pos;
|
||||||
pos += velocity * Core::deltaTime * 30.0f;
|
pos += velocity * Core::deltaTime * 30.0f;
|
||||||
TR::Level::FloorInfo info;
|
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) {
|
if (pos.y - info.floor > 1024) {
|
||||||
pos = p;
|
pos = p;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stand == STAND_GROUND)
|
if (stand == STAND_GROUND)
|
||||||
pos.y = info.floor;
|
pos.y = float(info.floor);
|
||||||
updateEntity();
|
updateEntity();
|
||||||
checkRoom();
|
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) {
|
void setOverrides(bool active, int chest, int head) {
|
||||||
int mask = 0;
|
int mask = 0;
|
||||||
if (head > -1) mask |= (1 << head);
|
if (active && head > -1) {
|
||||||
if (chest > -1) mask |= (1 << chest);
|
animation.overrides[head] = animation.getJointRot(head);
|
||||||
|
animation.overrideMask |= (1 << head);
|
||||||
|
} else
|
||||||
|
animation.overrideMask &= ~(1 << head);
|
||||||
|
|
||||||
if (active)
|
if (active && chest > -1) {
|
||||||
animOverrideMask |= mask;
|
animation.overrides[chest] = animation.getJointRot(chest);
|
||||||
else
|
animation.overrideMask |= (1 << chest);
|
||||||
animOverrideMask &= ~mask;
|
} else
|
||||||
|
animation.overrideMask &= ~(1 << chest);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lookAt(int target, int chest, int head) {
|
void lookAt(int target, int chest, int head) {
|
||||||
@ -105,11 +64,11 @@ struct Enemy : Controller {
|
|||||||
quat rot;
|
quat rot;
|
||||||
|
|
||||||
if (chest > -1) {
|
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);
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||||
else
|
else
|
||||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
||||||
animOverrides[chest] = rotChest * animOverrides[chest];
|
animation.overrides[chest] = rotChest * animation.overrides[chest];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (head > -1) {
|
if (head > -1) {
|
||||||
@ -117,14 +76,16 @@ struct Enemy : Controller {
|
|||||||
rotHead = rotHead.slerp(rot, speed);
|
rotHead = rotHead.slerp(rot, speed);
|
||||||
else
|
else
|
||||||
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
|
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) {
|
if (target > -1) {
|
||||||
vec3 v = (((Controller*)level->entities[target].controller)->pos - pos).normal();
|
vec3 a = (((Controller*)level->entities[target].controller)->pos - pos).normal();
|
||||||
float d = atan2(v.x, v.z) - angle.y;
|
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)
|
if (fabsf(d) > 0.01f)
|
||||||
return d < 0 ? LEFT : RIGHT;
|
return d < 0 ? LEFT : RIGHT;
|
||||||
}
|
}
|
||||||
@ -135,6 +96,9 @@ struct Enemy : Controller {
|
|||||||
|
|
||||||
#define WOLF_TURN_FAST PI
|
#define WOLF_TURN_FAST PI
|
||||||
#define WOLF_TURN_SLOW (PI / 3.0f)
|
#define WOLF_TURN_SLOW (PI / 3.0f)
|
||||||
|
#define WOLF_TILT_MAX (PI / 6.0f)
|
||||||
|
#define WOLF_TILT_SPEED WOLF_TILT_MAX
|
||||||
|
|
||||||
|
|
||||||
struct Wolf : Enemy {
|
struct Wolf : Enemy {
|
||||||
|
|
||||||
@ -163,7 +127,7 @@ struct Wolf : Enemy {
|
|||||||
JOINT_HEAD = 3
|
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() {
|
virtual int getStateGround() {
|
||||||
// STATE_SLEEP -> STATE_STOP
|
// STATE_SLEEP -> STATE_STOP
|
||||||
@ -178,9 +142,9 @@ struct Wolf : Enemy {
|
|||||||
|
|
||||||
if (health <= 0) {
|
if (health <= 0) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_RUN : return setAnimation(baseAnim + ANIM_DEATH_RUN);
|
case STATE_RUN : return animation.setAnim(ANIM_DEATH_RUN);
|
||||||
case STATE_JUMP : return setAnimation(baseAnim + ANIM_DEATH_JUMP);
|
case STATE_JUMP : return animation.setAnim(ANIM_DEATH_JUMP);
|
||||||
default : return setAnimation(baseAnim + ANIM_DEATH);
|
default : return animation.setAnim(ANIM_DEATH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +155,7 @@ struct Wolf : Enemy {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_SLEEP : return STATE_STOP;
|
case STATE_SLEEP : return STATE_STOP;
|
||||||
case STATE_STOP : return STATE_HOWL;
|
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;
|
case STATE_STALKING : if (health < 70) return STATE_RUN; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +179,7 @@ struct Wolf : Enemy {
|
|||||||
float w = 0.0f;
|
float w = 0.0f;
|
||||||
if (state == STATE_RUN || state == STATE_STALKING) {
|
if (state == STATE_RUN || state == STATE_STALKING) {
|
||||||
w = state == STATE_RUN ? WOLF_TURN_FAST : WOLF_TURN_SLOW;
|
w = state == STATE_RUN ? WOLF_TURN_FAST : WOLF_TURN_SLOW;
|
||||||
if (mask & LEFT) w = -w;
|
if (input & LEFT) w = -w;
|
||||||
|
|
||||||
if (w != 0.0f) {
|
if (w != 0.0f) {
|
||||||
w *= Core::deltaTime;
|
w *= Core::deltaTime;
|
||||||
@ -226,12 +190,14 @@ struct Wolf : Enemy {
|
|||||||
velocity = vec3(0.0f);
|
velocity = vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void move() {
|
virtual void updatePosition() {
|
||||||
|
updateTilt(state == STATE_RUN, WOLF_TILT_SPEED, WOLF_TILT_MAX);
|
||||||
|
|
||||||
if (state == STATE_DEATH) {
|
if (state == STATE_DEATH) {
|
||||||
animOverrideMask = 0;
|
animation.overrideMask = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Enemy::move();
|
Enemy::updatePosition();
|
||||||
setOverrides(state == STATE_STALKING || state == STATE_RUN, JOINT_CHEST, JOINT_HEAD);
|
setOverrides(state == STATE_STALKING || state == STATE_RUN, JOINT_CHEST, JOINT_HEAD);
|
||||||
lookAt(target, JOINT_CHEST, JOINT_HEAD);
|
lookAt(target, JOINT_CHEST, JOINT_HEAD);
|
||||||
}
|
}
|
||||||
@ -244,7 +210,7 @@ struct Bear : Enemy {
|
|||||||
STATE_STOP = 1,
|
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() {
|
virtual int getStateGround() {
|
||||||
return state;
|
return state;
|
||||||
@ -259,14 +225,14 @@ struct Bat : Enemy {
|
|||||||
STATE_FLY = 2,
|
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() {
|
virtual Stand getStand() {
|
||||||
return STAND_AIR;
|
return STAND_AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int getStateAir() {
|
virtual int getStateAir() {
|
||||||
animTime = 0.0f;
|
animation.time = 0.0f;
|
||||||
return STATE_AWAKE;
|
return STATE_AWAKE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
40
src/format.h
40
src/format.h
@ -5,13 +5,14 @@
|
|||||||
|
|
||||||
#define MAX_RESERVED_ENTITIES 64
|
#define MAX_RESERVED_ENTITIES 64
|
||||||
#define MAX_SECRETS_COUNT 16
|
#define MAX_SECRETS_COUNT 16
|
||||||
|
#define MAX_TRIGGER_COMMANDS 32
|
||||||
|
|
||||||
namespace TR {
|
namespace TR {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ANIM_CMD_NONE ,
|
ANIM_CMD_NONE ,
|
||||||
ANIM_CMD_MOVE ,
|
ANIM_CMD_OFFSET ,
|
||||||
ANIM_CMD_SPEED ,
|
ANIM_CMD_JUMP ,
|
||||||
ANIM_CMD_EMPTY ,
|
ANIM_CMD_EMPTY ,
|
||||||
ANIM_CMD_KILL ,
|
ANIM_CMD_KILL ,
|
||||||
ANIM_CMD_SOUND ,
|
ANIM_CMD_SOUND ,
|
||||||
@ -106,6 +107,15 @@ namespace TR {
|
|||||||
SND_SECRET = 173,
|
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 {
|
enum Action : uint16 {
|
||||||
ACTIVATE , // activate item
|
ACTIVATE , // activate item
|
||||||
CAMERA_SWITCH , // switch to camera
|
CAMERA_SWITCH , // switch to camera
|
||||||
@ -419,6 +429,17 @@ namespace TR {
|
|||||||
bool isEnemy() {
|
bool isEnemy() {
|
||||||
return type >= ENEMY_TWIN && type <= ENEMY_LARSON;
|
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 {
|
struct Animation {
|
||||||
@ -463,11 +484,13 @@ namespace TR {
|
|||||||
vec3 max() const { return vec3((float)maxX, (float)maxY, (float)maxZ); }
|
vec3 max() const { return vec3((float)maxX, (float)maxY, (float)maxZ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 4200 ) // zero-sized array warning
|
||||||
struct AnimFrame {
|
struct AnimFrame {
|
||||||
MinMax box;
|
MinMax box;
|
||||||
Vertex pos; // Starting offset for this model
|
Vertex pos;
|
||||||
int16 aCount;
|
int16 aCount;
|
||||||
uint16 angles[0]; // angle frames in YXZ order
|
uint16 angles[0]; // angle frames in YXZ order
|
||||||
|
|
||||||
vec3 getAngle(int index) {
|
vec3 getAngle(int index) {
|
||||||
#define ANGLE_SCALE (2.0f * PI / 1024.0f)
|
#define ANGLE_SCALE (2.0f * PI / 1024.0f)
|
||||||
@ -475,7 +498,7 @@ namespace TR {
|
|||||||
uint16 b = angles[index * 2 + 0];
|
uint16 b = angles[index * 2 + 0];
|
||||||
uint16 a = angles[index * 2 + 1];
|
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 count; // number of texture offsets - 1 in group
|
||||||
int16 textures[0]; // offsets into objectTextures[]
|
int16 textures[0]; // offsets into objectTextures[]
|
||||||
};
|
};
|
||||||
|
#pragma warning( push )
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
uint32 flags;
|
uint32 flags;
|
||||||
@ -715,7 +739,7 @@ namespace TR {
|
|||||||
int trigCmdCount;
|
int trigCmdCount;
|
||||||
Trigger trigger;
|
Trigger trigger;
|
||||||
FloorData::TriggerInfo trigInfo;
|
FloorData::TriggerInfo trigInfo;
|
||||||
FloorData::TriggerCommand trigCmd[16];
|
FloorData::TriggerCommand trigCmd[MAX_TRIGGER_COMMANDS];
|
||||||
|
|
||||||
vec3 getNormal() {
|
vec3 getNormal() {
|
||||||
return vec3((float)-slantX, -4.0f, (float)-slantZ).normal();
|
return vec3((float)-slantX, -4.0f, (float)-slantZ).normal();
|
||||||
@ -734,7 +758,8 @@ namespace TR {
|
|||||||
bool secrets[MAX_SECRETS_COUNT];
|
bool secrets[MAX_SECRETS_COUNT];
|
||||||
void *cameraController;
|
void *cameraController;
|
||||||
|
|
||||||
Level(Stream &stream, bool demo) {
|
Level(const char *name, bool demo) {
|
||||||
|
Stream stream(name);
|
||||||
// read version
|
// read version
|
||||||
stream.read(version);
|
stream.read(version);
|
||||||
// tiles
|
// tiles
|
||||||
@ -1039,6 +1064,7 @@ namespace TR {
|
|||||||
info.trigInfo = (*fd++).triggerInfo;
|
info.trigInfo = (*fd++).triggerInfo;
|
||||||
FloorData::TriggerCommand trigCmd;
|
FloorData::TriggerCommand trigCmd;
|
||||||
do {
|
do {
|
||||||
|
ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS);
|
||||||
trigCmd = (*fd++).triggerCmd; // trigger action
|
trigCmd = (*fd++).triggerCmd; // trigger action
|
||||||
info.trigCmd[info.trigCmdCount++] = trigCmd;
|
info.trigCmd[info.trigCmdCount++] = trigCmd;
|
||||||
} while (!trigCmd.end);
|
} while (!trigCmd.end);
|
||||||
|
11
src/game.h
11
src/game.h
@ -12,8 +12,9 @@ namespace Game {
|
|||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
Core::init();
|
Core::init();
|
||||||
Stream stream("LEVEL2_DEMO.PHD");
|
level = new Level("LEVEL2_DEMO.PHD", true, false);
|
||||||
level = new Level(stream, true);
|
//level = new Level("GYM.PHD", false, true);
|
||||||
|
//level = new Level("LEVEL4.PHD", false, false);
|
||||||
|
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
||||||
@ -29,7 +30,13 @@ namespace Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
|
float dt = Core::deltaTime;
|
||||||
|
if (Input::down[ikR]) // slow motion (for animation debugging)
|
||||||
|
Core::deltaTime /= 50.0f;
|
||||||
|
|
||||||
level->update();
|
level->update();
|
||||||
|
|
||||||
|
Core::deltaTime = dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
|
669
src/lara.h
669
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;
|
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
|
#ifdef _DEBUG
|
||||||
Debug::init();
|
Debug::init();
|
||||||
#endif
|
#endif
|
||||||
@ -44,8 +44,10 @@ struct Level {
|
|||||||
TR::Entity &entity = level.entities[i];
|
TR::Entity &entity = level.entities[i];
|
||||||
switch (entity.type) {
|
switch (entity.type) {
|
||||||
case TR::Entity::LARA :
|
case TR::Entity::LARA :
|
||||||
|
entity.controller = (lara = new Lara(&level, i, home));
|
||||||
|
break;
|
||||||
case TR::Entity::LARA_CUT :
|
case TR::Entity::LARA_CUT :
|
||||||
entity.controller = (lara = new Lara(&level, i));
|
entity.controller = (lara = new Lara(&level, i, false));
|
||||||
break;
|
break;
|
||||||
case TR::Entity::ENEMY_WOLF :
|
case TR::Entity::ENEMY_WOLF :
|
||||||
entity.controller = new Wolf(&level, i);
|
entity.controller = new Wolf(&level, i);
|
||||||
@ -71,7 +73,7 @@ struct Level {
|
|||||||
case TR::Entity::ENEMY_CENTAUR :
|
case TR::Entity::ENEMY_CENTAUR :
|
||||||
case TR::Entity::ENEMY_MUMMY :
|
case TR::Entity::ENEMY_MUMMY :
|
||||||
case TR::Entity::ENEMY_LARSON :
|
case TR::Entity::ENEMY_LARSON :
|
||||||
entity.controller = new Enemy(&level, i);
|
entity.controller = new Enemy(&level, i, 100);
|
||||||
break;
|
break;
|
||||||
case TR::Entity::DOOR_1 :
|
case TR::Entity::DOOR_1 :
|
||||||
case TR::Entity::DOOR_2 :
|
case TR::Entity::DOOR_2 :
|
||||||
@ -108,7 +110,7 @@ struct Level {
|
|||||||
if (entity.modelIndex > 0)
|
if (entity.modelIndex > 0)
|
||||||
entity.controller = new Controller(&level, i);
|
entity.controller = new Controller(&level, i);
|
||||||
else
|
else
|
||||||
entity.controller = new SpriteController(&level, i, 0);
|
entity.controller = new Sprite(&level, i, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +234,7 @@ struct Level {
|
|||||||
PROFILE_MARKER("ROOM");
|
PROFILE_MARKER("ROOM");
|
||||||
|
|
||||||
TR::Room &room = level.rooms[roomIndex];
|
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);
|
Shader *sh = setRoomShader(room, 1.0f);
|
||||||
|
|
||||||
@ -251,9 +253,11 @@ struct Level {
|
|||||||
TR::StaticMesh *sMesh = level.getMeshByID(rMesh.meshID);
|
TR::StaticMesh *sMesh = level.getMeshByID(rMesh.meshID);
|
||||||
ASSERT(sMesh != NULL);
|
ASSERT(sMesh != NULL);
|
||||||
|
|
||||||
|
if (!mesh->meshMap[sMesh->mesh]) continue;
|
||||||
|
|
||||||
// check visibility
|
// check visibility
|
||||||
Box box;
|
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);
|
sMesh->getBox(false, rMesh.rotation, box);
|
||||||
if (!camera->frustum->isVisible(offset + box.min, offset + box.max))
|
if (!camera->frustum->isVisible(offset + box.min, offset + box.max))
|
||||||
continue;
|
continue;
|
||||||
@ -343,7 +347,7 @@ struct Level {
|
|||||||
int j = room;
|
int j = room;
|
||||||
for (int i = 0; i < level.rooms[j].lightsCount; i++) {
|
for (int i = 0; i < level.rooms[j].lightsCount; i++) {
|
||||||
TR::Room::Light &light = level.rooms[j].lights[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) {
|
if (idx == -1 || d < dist) {
|
||||||
idx = i;
|
idx = i;
|
||||||
dist = d;
|
dist = d;
|
||||||
@ -360,7 +364,7 @@ struct Level {
|
|||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
TR::Room::Light &light = level.rooms[room].lights[idx];
|
TR::Room::Light &light = level.rooms[room].lights[idx];
|
||||||
float c = level.rooms[room].lights[idx].intensity / 8191.0f;
|
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);
|
Core::lightColor[0] = vec4(c, c, c, (float)light.attenuation * (float)light.attenuation);
|
||||||
} else {
|
} else {
|
||||||
Core::lightPos[0] = vec3(0);
|
Core::lightPos[0] = vec3(0);
|
||||||
@ -389,7 +393,7 @@ struct Level {
|
|||||||
setRoomShader(room, c)->bind();
|
setRoomShader(room, c)->bind();
|
||||||
Core::active.shader->setParam(uColor, Core::color);
|
Core::active.shader->setParam(uColor, Core::color);
|
||||||
// get light parameters for entity
|
// 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
|
if (entity.modelIndex < 0) { // sprite
|
||||||
@ -482,7 +486,7 @@ struct Level {
|
|||||||
// Debug::Level::portals(level);
|
// Debug::Level::portals(level);
|
||||||
// Debug::Level::meshes(level);
|
// Debug::Level::meshes(level);
|
||||||
// Debug::Level::entities(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();
|
Debug::end();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -2504,16 +2504,16 @@ int mp3_decode_init() {
|
|||||||
for(i=0; i<512*16; i++){
|
for(i=0; i<512*16; i++){
|
||||||
int exponent= (i>>4);
|
int exponent= (i>>4);
|
||||||
double f= libc_pow(i&15, 4.0 / 3.0) * libc_pow(2, (exponent-400)*0.25 + FRAC_BITS + 5);
|
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)
|
if((i&15)==1)
|
||||||
exp_table[exponent]= f;
|
exp_table[exponent]= uint32_t(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i=0;i<7;i++) {
|
for(i=0;i<7;i++) {
|
||||||
float f;
|
float f;
|
||||||
int v;
|
int v;
|
||||||
if (i != 6) {
|
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));
|
v = FIXR(f / (1.0 + f));
|
||||||
} else {
|
} else {
|
||||||
v = FIXR(1.0);
|
v = FIXR(1.0);
|
||||||
@ -2522,7 +2522,7 @@ int mp3_decode_init() {
|
|||||||
is_table[1][6 - i] = v;
|
is_table[1][6 - i] = v;
|
||||||
}
|
}
|
||||||
for(i=7;i<16;i++)
|
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++) {
|
for(i=0;i<16;i++) {
|
||||||
double f;
|
double f;
|
||||||
@ -2540,7 +2540,7 @@ int mp3_decode_init() {
|
|||||||
for(i=0;i<8;i++) {
|
for(i=0;i<8;i++) {
|
||||||
float ci, cs, ca;
|
float ci, cs, ca;
|
||||||
ci = ci_table[i];
|
ci = ci_table[i];
|
||||||
cs = 1.0 / sqrt(1.0 + ci * ci);
|
cs = float(1.0 / sqrt(1.0 + ci * ci));
|
||||||
ca = cs * ci;
|
ca = cs * ci;
|
||||||
csa_table[i][0] = FIXHR(cs/4);
|
csa_table[i][0] = FIXHR(cs/4);
|
||||||
csa_table[i][1] = FIXHR(ca/4);
|
csa_table[i][1] = FIXHR(ca/4);
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
<head><title>OpenLara</title></head>
|
<head><title>OpenLara</title></head>
|
||||||
<body>
|
<body>
|
||||||
<span id="status">Starting...</span>
|
<span id="status">Starting...</span>
|
||||||
<canvas id="canvas" width="160" height="120" oncontextmenu="event.preventDefault()"></canvas><br>
|
<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</span>
|
<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'>
|
<script type='text/javascript'>
|
||||||
var statusElement = document.getElementById('status');
|
var statusElement = document.getElementById('status');
|
||||||
|
@ -151,7 +151,9 @@
|
|||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\..\animation.h" />
|
||||||
<ClInclude Include="..\..\camera.h" />
|
<ClInclude Include="..\..\camera.h" />
|
||||||
|
<ClInclude Include="..\..\character.h" />
|
||||||
<ClInclude Include="..\..\controller.h" />
|
<ClInclude Include="..\..\controller.h" />
|
||||||
<ClInclude Include="..\..\core.h" />
|
<ClInclude Include="..\..\core.h" />
|
||||||
<ClInclude Include="..\..\debug.h" />
|
<ClInclude Include="..\..\debug.h" />
|
||||||
@ -166,6 +168,7 @@
|
|||||||
<ClInclude Include="..\..\mesh.h" />
|
<ClInclude Include="..\..\mesh.h" />
|
||||||
<ClInclude Include="..\..\shader.h" />
|
<ClInclude Include="..\..\shader.h" />
|
||||||
<ClInclude Include="..\..\sound.h" />
|
<ClInclude Include="..\..\sound.h" />
|
||||||
|
<ClInclude Include="..\..\sprite.h" />
|
||||||
<ClInclude Include="..\..\texture.h" />
|
<ClInclude Include="..\..\texture.h" />
|
||||||
<ClInclude Include="..\..\format.h" />
|
<ClInclude Include="..\..\format.h" />
|
||||||
<ClInclude Include="..\..\trigger.h" />
|
<ClInclude Include="..\..\trigger.h" />
|
||||||
|
@ -311,14 +311,12 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
|
|||||||
if (time <= lastTime)
|
if (time <= lastTime)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float slow = Input::down[ikR] ? 8.0f : 1.0f;
|
|
||||||
|
|
||||||
float delta = min(1.0f, (time - lastTime) * 0.001f);
|
float delta = min(1.0f, (time - lastTime) * 0.001f);
|
||||||
EnterCriticalSection(&sndCS);
|
EnterCriticalSection(&sndCS);
|
||||||
while (delta > EPS) {
|
while (delta > EPS) {
|
||||||
Core::deltaTime = min(delta, 1.0f / 30.0f) / slow;
|
Core::deltaTime = min(delta, 1.0f / 30.0f);
|
||||||
Game::update();
|
Game::update();
|
||||||
delta -= Core::deltaTime * slow;
|
delta -= Core::deltaTime;
|
||||||
}
|
}
|
||||||
LeaveCriticalSection(&sndCS);
|
LeaveCriticalSection(&sndCS);
|
||||||
lastTime = time;
|
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
|
#define H_TRIGGER
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "sprite.h"
|
||||||
|
|
||||||
struct Trigger : Controller {
|
struct Trigger : Controller {
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ struct Trigger : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!inState())
|
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);
|
updateAnimation(true);
|
||||||
updateEntity();
|
updateEntity();
|
||||||
@ -58,7 +59,7 @@ struct Trigger : Controller {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Dart : Controller {
|
struct Dart : Controller {
|
||||||
|
vec3 velocity;
|
||||||
vec3 dir;
|
vec3 dir;
|
||||||
bool inWall; // dart starts from wall
|
bool inWall; // dart starts from wall
|
||||||
|
|
||||||
@ -67,7 +68,8 @@ struct Dart : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void update() {
|
virtual void update() {
|
||||||
velocity = dir * level->anims[animIndex].speed;
|
TR::Animation *anim = animation;
|
||||||
|
velocity = dir * animation.getSpeed();
|
||||||
pos = pos + velocity * (Core::deltaTime * 30.0f);
|
pos = pos + velocity * (Core::deltaTime * 30.0f);
|
||||||
updateEntity();
|
updateEntity();
|
||||||
TR::Level::FloorInfo info;
|
TR::Level::FloorInfo info;
|
||||||
@ -77,7 +79,7 @@ struct Dart : Controller {
|
|||||||
TR::Entity &e = getEntity();
|
TR::Entity &e = getEntity();
|
||||||
|
|
||||||
vec3 p = pos - dir * 64.0f; // wall offset = 64
|
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);
|
level->entityRemove(entity);
|
||||||
delete this;
|
delete this;
|
||||||
@ -99,14 +101,13 @@ struct Dartgun : Trigger {
|
|||||||
// add dart (bullet)
|
// add dart (bullet)
|
||||||
TR::Entity &entity = getEntity();
|
TR::Entity &entity = getEntity();
|
||||||
|
|
||||||
vec3 pos = vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
|
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
|
||||||
pos = pos + vec3(entity.x, entity.y, entity.z);
|
|
||||||
|
|
||||||
int dartIndex = level->entityAdd(TR::Entity::TRAP_DART, entity.room, (int)pos.x, (int)pos.y, (int)pos.z, entity.rotation, entity.intensity);
|
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)
|
if (dartIndex > -1)
|
||||||
level->entities[dartIndex].controller = new Dart(level, dartIndex);
|
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);
|
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);
|
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)
|
if ((info.slantX | info.slantZ) || info.floor != e.y)
|
||||||
return false;
|
return false;
|
||||||
if (!setState(push ? STATE_PUSH : STATE_PULL))
|
if (!animation.setState(push ? STATE_PUSH : STATE_PULL))
|
||||||
return false;
|
return false;
|
||||||
updateFloor(false);
|
updateFloor(false);
|
||||||
return true;
|
return true;
|
||||||
|
13
src/utils.h
13
src/utils.h
@ -22,7 +22,7 @@
|
|||||||
#ifdef PROFILE
|
#ifdef PROFILE
|
||||||
#define LOG(...) printf(__VA_ARGS__)
|
#define LOG(...) printf(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define LOG(...)
|
#define LOG(...) 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -115,6 +115,9 @@ struct vec3 {
|
|||||||
|
|
||||||
float& operator [] (int index) const { return ((float*)this)[index]; }
|
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; }
|
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() {}
|
||||||
Box(const vec3 &min, const vec3 &max) : min(min), max(max) {}
|
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) {
|
void rotate90(int n) {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 0 : break;
|
case 0 : break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user