1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-02-24 07:22:58 +01:00

#3 draw weapons

This commit is contained in:
XProger 2016-11-07 03:14:49 +03:00
parent 4390eb9a96
commit b3ef2052c2
4 changed files with 306 additions and 26 deletions

View File

@ -41,6 +41,9 @@ struct Controller {
float turnTime;
int *meshes;
int mCount;
struct ActionCommand {
TR::Action action;
int value;
@ -58,6 +61,23 @@ struct Controller {
stand = STAND_GROUND;
animIndex = e.modelIndex > 0 ? getModel().animation : 0;
state = level->anims[animIndex].state;
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;
}
virtual ~Controller() {
delete[] meshes;
}
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;
}
}
void updateEntity() {

View File

@ -138,8 +138,54 @@ struct Lara : Controller {
STATE_HANDSTAND,
STATE_WATER_OUT,
STATE_MAX };
enum : int {
BODY_HIP = 0x0001,
BODY_LEG_R1 = 0x0002,
BODY_LEG_R2 = 0x0004,
BODY_LEG_R3 = 0x0008,
BODY_LEG_L1 = 0x0010,
BODY_LEG_L2 = 0x0020,
BODY_LEG_L3 = 0x0040,
BODY_CHEST = 0x0080,
BODY_ARM_L1 = 0x0100,
BODY_ARM_L2 = 0x0200,
BODY_ARM_L3 = 0x0400,
BODY_ARM_R1 = 0x0800,
BODY_ARM_R2 = 0x1000,
BODY_ARM_R3 = 0x2000,
BODY_HEAD = 0x4000,
BODY_ARM_L = BODY_ARM_L1 | BODY_ARM_L2 | BODY_ARM_L3,
BODY_ARM_R = BODY_ARM_R1 | BODY_ARM_R2 | BODY_ARM_R3,
BODY_LEG_L = BODY_LEG_L1 | BODY_LEG_L2 | BODY_LEG_L3,
BODY_LEG_R = BODY_LEG_R1 | BODY_LEG_R2 | BODY_LEG_R3,
BODY_UPPER = BODY_CHEST | BODY_ARM_L | BODY_ARM_R, // without head
BODY_LOWER = BODY_HIP | BODY_LEG_L | BODY_LEG_R,
};
struct Weapon {
enum Type { EMPTY, PISTOLS, SHOTGUN, MAGNUMS, UZIS, MAX };
enum State { IS_CLEAN, IS_ARMED, IS_FIRING };
enum Anim { NONE, PREPARE, UNHOLSTER, HOLSTER, HOLD, AIM, FIRE };
int ammo; // if -1 weapon is not available
} weapons[Weapon::MAX];
Weapon::Type wpnCurrent;
Weapon::State wpnState;
Weapon::Anim wpnAnim;
float wpnAnimTime;
float wpnAnimDir;
quat animOverrides[15]; // left & right arms animation frames
int animOverrideMask;
Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), animOverrideMask(0) {
memset(weapons, -1, sizeof(weapons));
weapons[Weapon::PISTOLS].ammo = 0;
weapons[Weapon::SHOTGUN].ammo = 8;
setWeapon(Weapon::PISTOLS, Weapon::IS_CLEAN, Weapon::Anim::NONE, 1.0f);
Lara(TR::Level *level, int entity) : Controller(level, entity) {
#ifdef _DEBUG
/*
// gym
@ -191,6 +237,78 @@ struct Lara : Controller {
#endif
}
void setWeapon(Weapon::Type wType, Weapon::State wState, Weapon::Anim wAnim, float wAnimDir) {
wpnAnimDir = wAnimDir;
if (wAnim != wpnAnim) {
wpnAnim = wAnim;
TR::Animation *anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
wpnAnimTime = wpnAnimDir >= 0.0f ? 0.0f : ((anim->frameEnd - anim->frameStart) / 30.0f);
}
if (wpnCurrent == wType && wpnState == wState)
return;
int mask = 0;
switch (wType) {
case Weapon::EMPTY : break;
case Weapon::PISTOLS :
case Weapon::MAGNUMS :
case Weapon::UZIS :
switch (wState) {
case Weapon::IS_CLEAN : mask = BODY_LEG_L1 | BODY_LEG_R1; break;
case Weapon::IS_ARMED : mask = BODY_ARM_L3 | BODY_ARM_R3; break;
case Weapon::IS_FIRING : mask = BODY_ARM_L3 | BODY_ARM_R3 | BODY_HEAD; break;
}
break;
case Weapon::SHOTGUN :
switch (wState) {
case Weapon::IS_CLEAN : mask = BODY_CHEST; break;
case Weapon::IS_ARMED : mask = BODY_ARM_L3 | BODY_ARM_R3; break;
case Weapon::IS_FIRING : mask = BODY_ARM_L3 | BODY_ARM_R3 | BODY_HEAD; break;
}
break;
}
// restore original meshes first
meshSwap(level->models[Weapon::EMPTY], BODY_HEAD | BODY_UPPER | BODY_LOWER);
// replace some parts
meshSwap(level->models[wType], mask);
// have a shotgun in inventory place it on the back if another weapon is in use
if (wType != Weapon::SHOTGUN && weapons[Weapon::SHOTGUN].ammo != -1)
meshSwap(level->models[Weapon::SHOTGUN], BODY_CHEST);
// mesh swap to angry Lara's head while firing (from uzis model)
if (wState == Weapon::IS_FIRING)
meshSwap(level->models[Weapon::UZIS], BODY_HEAD);
wpnCurrent = wType;
wpnState = wState;
}
int getWeaponAnimIndex(Weapon::Anim wAnim) {
int baseAnim = level->models[wpnCurrent].animation;
if (wpnCurrent == Weapon::SHOTGUN) {
switch (wAnim) {
case Weapon::Anim::PREPARE : ASSERT(false); break; // rifle has no prepare animation
case Weapon::Anim::UNHOLSTER : return baseAnim + 1;
case Weapon::Anim::HOLSTER : return baseAnim + 3;
case Weapon::Anim::HOLD :
case Weapon::Anim::AIM : return baseAnim;
case Weapon::Anim::FIRE : return baseAnim + 2;
}
} else
switch (wAnim) {
case Weapon::Anim::PREPARE : return baseAnim + 1;
case Weapon::Anim::UNHOLSTER : return baseAnim + 2;
case Weapon::Anim::HOLSTER : ASSERT(false); break; // pistols has no holster animation (it's reversed unholster)
case Weapon::Anim::HOLD :
case Weapon::Anim::AIM : return baseAnim;
case Weapon::Anim::FIRE : return baseAnim + 3;
}
return 0;
}
bool waterOut(int &outState) {
// TODO: playSound 36
vec3 dst = pos + getDir() * 32.0f;
@ -403,6 +521,10 @@ struct Lara : Controller {
return vec3(0.0f, h, 0.0f);
}
bool emptyHands() {
return wpnState == Weapon::IS_CLEAN;
}
virtual Stand getStand() {
if (state == STATE_HANG || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT) {
if (mask & ACTION)
@ -466,7 +588,7 @@ struct Lara : Controller {
if (state == STATE_REACH && getDir().dot(vec3(velocity.x, 0.0f, velocity.z)) < 0)
velocity.x = velocity.z = 0.0f;
if ((state == STATE_REACH || state == STATE_UP_JUMP) && (mask & ACTION)) {
if ((state == STATE_REACH || state == STATE_UP_JUMP) && (mask & ACTION) && emptyHands()) {
if (state == STATE_REACH && velocity.y < 0.0f)
return state;
@ -498,8 +620,10 @@ struct Lara : Controller {
}
if (state == STATE_FORWARD_JUMP) {
if (mask & ACTION) return STATE_REACH;
if ((mask & (FORTH | WALK)) == (FORTH | WALK)) return STATE_SWAN_DIVE;
if (emptyHands()) {
if (mask & ACTION) return STATE_REACH;
if ((mask & (FORTH | WALK)) == (FORTH | WALK)) return STATE_SWAN_DIVE;
}
} else
if (state != STATE_SWAN_DIVE && state != STATE_REACH && state != STATE_FALL && state != STATE_UP_JUMP && state != STATE_BACK_JUMP && state != STATE_LEFT_JUMP && state != STATE_RIGHT_JUMP)
return setAnimation(ANIM_FALL);
@ -539,10 +663,10 @@ struct Lara : Controller {
virtual int getStateGround() {
angle.x = 0.0f;
if ((mask & ACTION) && doPickUp())
if ((mask & ACTION) && doPickUp() && emptyHands())
return STATE_PICK_UP;
if ( (mask & (FORTH | ACTION)) == (FORTH | ACTION) && (animIndex == ANIM_STAND || animIndex == ANIM_STAND_NORMAL) ) {
if ( (mask & (FORTH | ACTION)) == (FORTH | ACTION) && (animIndex == ANIM_STAND || animIndex == ANIM_STAND_NORMAL) && emptyHands()) {
vec3 p = pos + getDir() * 64.0f;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info, true);
@ -612,7 +736,7 @@ struct Lara : Controller {
return STATE_STOP;
}
if (mask & ACTION) {
if ((mask & ACTION) && emptyHands()) {
if (state == STATE_PUSH_PULL_READY && (mask & (FORTH | BACK))) {
int pushState = (mask & FORTH) ? STATE_PUSH_BLOCK : STATE_PULL_BLOCK;
Block *block = getBlock();
@ -878,6 +1002,135 @@ struct Lara : Controller {
}
}
virtual void updateAnimation(bool commands) {
Controller::updateAnimation(commands);
// apply weapon state changes
if (wpnCurrent == Weapon::EMPTY) {
animOverrideMask &= ~(BODY_ARM_L | BODY_ARM_R);
return;
}
Weapon::Anim nextAnim = wpnAnim;
bool isRifle = wpnCurrent == Weapon::SHOTGUN;
if ((mask & WEAPON) && wpnAnim != Weapon::Anim::PREPARE && wpnAnim != Weapon::Anim::UNHOLSTER && wpnAnim != Weapon::Anim::HOLSTER) {
if (wpnState == Weapon::IS_CLEAN)
setWeapon(wpnCurrent, wpnState, isRifle ? Weapon::Anim::UNHOLSTER : Weapon::Anim::PREPARE, 1.0f);
else
if (isRifle)
setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLSTER, 1.0f);
else
setWeapon(wpnCurrent, wpnState, Weapon::Anim::UNHOLSTER, -1.0f);
}
if (wpnState != Weapon::IS_CLEAN) {
if (mask & ACTION) {
if (wpnAnim == Weapon::Anim::HOLD)
setWeapon(wpnCurrent, wpnState, Weapon::Anim::AIM, 1.0f);
} else
if (wpnAnim == Weapon::Anim::AIM)
wpnAnimDir = -1.0f;
}
TR::Animation *anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
float maxTime = (anim->frameEnd - anim->frameStart) / 30.0f;
if (wpnAnim == Weapon::Anim::NONE) {
animOverrideMask &= ~(BODY_ARM_L | BODY_ARM_R);
return;
}
animOverrideMask |= BODY_ARM_L | BODY_ARM_R;
Weapon::Anim prevAnim = wpnAnim; // cache before changes
wpnAnimTime += Core::deltaTime * wpnAnimDir;
/*
case Weapon::Anim::FIRE :
if (!(mask & ACTION))
setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::AIM, -1.0f);
break;
*/
if (wpnAnim == Weapon::Anim::FIRE && wpnAnimTime >= maxTime)
if (!(mask & ACTION))
setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::AIM, -1.0f);
if (isRifle) {
if (wpnAnimDir > 0.0f)
switch (wpnAnim) {
case Weapon::Anim::UNHOLSTER :
if (wpnAnimTime >= maxTime)
setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f);
else if (wpnAnimTime >= maxTime * 0.3f)
setWeapon(wpnCurrent, Weapon::IS_ARMED, wpnAnim, 1.0f);
break;
case Weapon::Anim::HOLSTER :
if (wpnAnimTime >= maxTime)
setWeapon(wpnCurrent, Weapon::IS_CLEAN, Weapon::Anim::NONE, wpnAnimDir);
else if (wpnAnimTime >= maxTime * 0.7f)
setWeapon(wpnCurrent, Weapon::IS_CLEAN, wpnAnim, 1.0f); break;
case Weapon::Anim::AIM : if (wpnAnimTime >= maxTime) setWeapon(wpnCurrent, Weapon::IS_FIRING, Weapon::Anim::FIRE, wpnAnimDir); break;
};
if (wpnAnimDir < 0.0f && wpnAnimTime <= 0.0f)
switch (wpnAnim) {
case Weapon::Anim::AIM : setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLD, 0.0f); break;
};
} else {
if (wpnAnimDir > 0.0f && wpnAnimTime >= maxTime)
switch (wpnAnim) {
case Weapon::Anim::PREPARE : setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::UNHOLSTER, wpnAnimDir); break;
case Weapon::Anim::UNHOLSTER : setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLD, 0.0f); break;
case Weapon::Anim::AIM : setWeapon(wpnCurrent, Weapon::IS_FIRING, Weapon::Anim::FIRE, wpnAnimDir); break;
};
if (wpnAnimDir < 0.0f && wpnAnimTime <= 0.0f)
switch (wpnAnim) {
case Weapon::Anim::PREPARE : setWeapon(wpnCurrent, wpnState, Weapon::Anim::NONE, wpnAnimDir); break;
case Weapon::Anim::UNHOLSTER : setWeapon(wpnCurrent, Weapon::IS_CLEAN, Weapon::Anim::PREPARE, wpnAnimDir); break;
case Weapon::Anim::AIM : setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLD, 0.0f); break;
};
}
if (prevAnim != wpnAnim) // check by cache
anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
if (wpnAnim == Weapon::Anim::NONE) {
animOverrideMask &= ~(BODY_ARM_L | BODY_ARM_R);
return;
}
// update animation overrides
float k = wpnAnimTime * 30.0f / anim->frameRate;
int fIndex = (int)k;
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
int fSize = sizeof(TR::AnimFrame) + getModel().mCount * sizeof(uint16) * 2;
k = k - fIndex;
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
TR::AnimFrame *frameA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1];
TR::AnimFrame *frameB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
LOG("%d %d %f\n", getWeaponAnimIndex(wpnAnim), fIndexA, wpnAnimTime);
// left arm
animOverrides[ 8] = lerpFrames(frameA, frameB, k, 8);
animOverrides[ 9] = lerpFrames(frameA, frameB, k, 9);
animOverrides[10] = lerpFrames(frameA, frameB, k, 10);
// right arm
animOverrides[11] = lerpFrames(frameA, frameB, k, 11);
animOverrides[12] = lerpFrames(frameA, frameB, k, 12);
animOverrides[13] = lerpFrames(frameA, frameB, k, 13);
}
quat lerpFrames(TR::AnimFrame *frameA, TR::AnimFrame *frameB, float t, int index) {
return lerpAngle(frameA->getAngle(index), frameB->getAngle(index), t);
}
virtual void updateVelocity() {
// calculate moving speed
float dt = Core::deltaTime * 30.0f;
@ -994,7 +1247,7 @@ struct Lara : Controller {
if (canPassGap) {
TR::Room &r = level->rooms[e.room];
for (int i = 0; i < r.meshesCount; i++) {
TR::Room::Mesh &m = r.meshes[i];
TR::Room::Mesh &m = r.meshes[i];
TR::StaticMesh *sm = level->getMeshByID(m.meshID);
if (sm->flags != 2) continue; // no have collision box

View File

@ -332,22 +332,6 @@ struct Level {
mesh->renderMesh(m);
}
quat lerpAngle(const vec3 &a, const vec3 &b, float t) { // TODO: optimization
mat4 ma, mb;
ma.identity();
mb.identity();
ma.rotateY(a.y);
ma.rotateX(a.x);
ma.rotateZ(a.z);
mb.rotateY(b.y);
mb.rotateX(b.x);
mb.rotateZ(b.z);
return ma.getRot().slerp(mb.getRot(), t).normal();
}
void renderShadow(const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) {
mat4 m;
m.identity();
@ -432,7 +416,11 @@ struct Level {
m.translate(vec3(t.x, t.y, t.z));
}
quat q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), k);
quat q;
if (entity.type == TR::Entity::LARA && (((Lara*)controller)->animOverrideMask & (1 << i)))
q = ((Lara*)controller)->animOverrides[i];
else
q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), k);
m = m * mat4(q, vec3(0.0f));
@ -443,7 +431,10 @@ struct Level {
mat4 tmp = Core::mModel;
Core::mModel = Core::mModel * m;
renderMesh(model.mStart + i);
if (controller)
renderMesh(controller->meshes[i]);
else
renderMesh(model.mStart + i);
Core::mModel = tmp;
}

View File

@ -491,6 +491,22 @@ struct mat4 {
}
};
quat lerpAngle(const vec3 &a, const vec3 &b, float t) { // TODO: optimization
mat4 ma, mb;
ma.identity();
mb.identity();
ma.rotateY(a.y);
ma.rotateX(a.x);
ma.rotateZ(a.z);
mb.rotateY(b.y);
mb.rotateX(b.x);
mb.rotateZ(b.z);
return ma.getRot().slerp(mb.getRot(), t).normal();
}
struct Box {
vec3 min, max;