1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-07-31 18:30:19 +02:00

#7 auto-aiming, enemies hit; #8 camera look at enemy, view point on Lara's chest moving while moving blocks; #3 head & chest IK; #15 preventing closing page by Ctrl + W (request)

This commit is contained in:
XProger
2016-11-15 03:45:16 +03:00
parent 7f372076df
commit 08dc561617
8 changed files with 230 additions and 187 deletions

Binary file not shown.

View File

@@ -19,7 +19,6 @@ struct Camera : Controller {
float timer; float timer;
int actTargetEntity, actCamera; int actTargetEntity, actCamera;
vec3 viewOffset;
Camera(TR::Level *level, Lara *owner) : Controller(level, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1) { Camera(TR::Level *level, Lara *owner) : Controller(level, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1) {
fov = 80.0f; fov = 80.0f;
@@ -30,8 +29,8 @@ struct Camera : Controller {
if (owner) { if (owner) {
room = owner->getEntity().room; room = owner->getEntity().room;
pos = pos - owner->getDir() * 1024.0f; pos = pos - owner->getDir() * 1024.0f;
target = owner->getViewPoint();
} }
viewOffset = owner->getViewOffset();
} }
virtual ~Camera() { virtual ~Camera() {
@@ -92,11 +91,9 @@ 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) ? 4.0f : 10.0f; float lerpFactor = (actTargetEntity == -1) ? 6.0f : 10.0f;
viewOffset = viewOffset.lerp(owner->getViewOffset(), Core::deltaTime * lerpFactor);
vec3 dir; vec3 dir;
target = vec3(owner->pos.x, owner->pos.y, owner->pos.z) + viewOffset; 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];

View File

@@ -39,10 +39,6 @@ struct Controller {
float angleExt; float angleExt;
int health;
float turnTime;
int *meshes; int *meshes;
int mCount; int mCount;
quat *animOverrides; // left & right arms animation frames quat *animOverrides; // left & right arms animation frames
@@ -59,7 +55,7 @@ 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), health(100), turnTime(0.0f), actionCommand(NULL), mCount(0), meshes(NULL), animOverrides(NULL), animOverrideMask(0), joints(NULL) { 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) {
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);
@@ -528,6 +524,7 @@ struct Controller {
virtual int getStateDeath() { return state; } virtual int getStateDeath() { return state; }
virtual int getStateDefault() { return state; } virtual int getStateDefault() { return state; }
virtual int getInputMask() { return 0; } virtual int getInputMask() { return 0; }
virtual void hit(int damage) { };
virtual int getState(Stand stand) { virtual int getState(Stand stand) {
TR::Animation *anim = &level->anims[animIndex]; TR::Animation *anim = &level->anims[animIndex];
@@ -673,12 +670,12 @@ struct Controller {
updateEnd(); updateEnd();
} }
void renderMesh(MeshBuilder *mesh, uint32 offsetIndex) { void renderMesh(const mat4 &matrix, MeshBuilder *mesh, uint32 offsetIndex) {
MeshBuilder::MeshInfo *m = mesh->meshMap[offsetIndex]; MeshBuilder::MeshInfo *mInfo = mesh->meshMap[offsetIndex];
if (!m) return; // invisible mesh (offsetIndex > 0 && level.meshOffsets[offsetIndex] == 0) camera target entity etc. if (!mInfo) return; // invisible mesh (offsetIndex > 0 && level.meshOffsets[offsetIndex] == 0) camera target entity etc.
Core::active.shader->setParam(uModel, Core::mModel); Core::active.shader->setParam(uModel, matrix);
mesh->renderMesh(m); mesh->renderMesh(mInfo);
} }
void renderShadow(MeshBuilder *mesh, const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) { void renderShadow(MeshBuilder *mesh, const vec3 &pos, const vec3 &offset, const vec3 &size, float angle) {
@@ -695,15 +692,16 @@ struct Controller {
} }
virtual void render(Frustum *frustum, MeshBuilder *mesh) { virtual void render(Frustum *frustum, MeshBuilder *mesh) {
PROFILE_MARKER("MDL");
TR::Entity &entity = getEntity(); TR::Entity &entity = getEntity();
TR::Model &model = getModel(); TR::Model &model = getModel();
TR::Animation *anim = &level->anims[animIndex]; TR::Animation *anim = &level->anims[animIndex];
if (angle.y != 0.0f) Core::mModel.rotateY(angle.y); mat4 matrix(Core::mModel);
if (angle.x != 0.0f) Core::mModel.rotateX(angle.x); matrix.translate(pos);
if (angle.z != 0.0f) Core::mModel.rotateZ(angle.z); 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);
float t; float t;
vec3 move(0.0f); vec3 move(0.0f);
@@ -712,14 +710,13 @@ struct Controller {
vec3 bmin = frameA->box.min().lerp(frameB->box.min(), t); vec3 bmin = frameA->box.min().lerp(frameB->box.min(), t);
vec3 bmax = frameA->box.max().lerp(frameB->box.max(), t); vec3 bmax = frameA->box.max().lerp(frameB->box.max(), t);
if (frustum && !frustum->isVisible(Core::mModel, bmin, bmax)) if (frustum && !frustum->isVisible(matrix, bmin, bmax))
return; return;
entity.flags.rendered = true;
TR::Node *node = (int)model.node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model.node] : NULL; TR::Node *node = (int)model.node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model.node] : NULL;
mat4 m; matrix.translate(((vec3)frameA->pos).lerp(move + frameB->pos, t));
m.identity();
m.translate(((vec3)frameA->pos).lerp(move + frameB->pos, t));
int sIndex = 0; int sIndex = 0;
mat4 stack[20]; mat4 stack[20];
@@ -729,12 +726,12 @@ struct Controller {
if (i > 0 && node) { if (i > 0 && node) {
TR::Node &t = node[i - 1]; TR::Node &t = node[i - 1];
if (t.flags & 0x01) m = stack[--sIndex]; if (t.flags & 0x01) matrix = stack[--sIndex];
if (t.flags & 0x02) stack[sIndex++] = m; if (t.flags & 0x02) stack[sIndex++] = matrix;
ASSERT(sIndex >= 0 && sIndex < 20); ASSERT(sIndex >= 0 && sIndex < 20);
m.translate(vec3(t.x, t.y, t.z)); matrix.translate(vec3(t.x, t.y, t.z));
} }
quat q; quat q;
@@ -742,19 +739,15 @@ struct Controller {
q = animOverrides[i]; q = animOverrides[i];
else else
q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), t); q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), t);
m = m * mat4(q, vec3(0.0f)); matrix = matrix * mat4(q, vec3(0.0f));
mat4 tmp = Core::mModel;
Core::mModel = Core::mModel * m;
if (meshes) if (meshes)
renderMesh(mesh, meshes[i]); renderMesh(matrix, mesh, meshes[i]);
else else
renderMesh(mesh, model.mStart + i); renderMesh(matrix, mesh, model.mStart + i);
if (joints) if (joints)
joints[i] = Core::mModel; joints[i] = matrix;
Core::mModel = tmp;
} }
if (TR::castShadow(entity.type)) { if (TR::castShadow(entity.type)) {
@@ -814,8 +807,9 @@ struct SpriteController : Controller {
} }
virtual void render(Frustum *frustum, MeshBuilder *mesh) { virtual void render(Frustum *frustum, MeshBuilder *mesh) {
PROFILE_MARKER("SPR"); mat4 m(Core::mModel);
Core::active.shader->setParam(uModel, Core::mModel); m.translate(pos);
Core::active.shader->setParam(uModel, m);
mesh->renderSprite(-(getEntity().modelIndex + 1), frame); mesh->renderSprite(-(getEntity().modelIndex + 1), frame);
} }
}; };

View File

@@ -4,11 +4,18 @@
#include "controller.h" #include "controller.h"
struct Enemy : Controller { struct Enemy : Controller {
Enemy(TR::Level *level, int entity) : Controller(level, entity) {} int health;
Enemy(TR::Level *level, int entity) : Controller(level, entity), health(100) {}
virtual Stand getStand() { virtual Stand getStand() {
return STAND_GROUND; return STAND_GROUND;
} }
virtual void hit(int damage) {
health -= damage;
};
}; };

View File

@@ -187,20 +187,23 @@ struct Lara : Controller {
float animTime; float animTime;
float animMaxTime; float animMaxTime;
int animIndex; int animIndex;
quat rot; quat rot, rotAbs;
Weapon::Anim anim; Weapon::Anim anim;
} arms[2]; } arms[2];
int target; int target;
quat rotHead, rotChest; quat rotHead, rotChest;
int health;
float turnTime;
Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), target(-1) { Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), target(-1), health(100), turnTime(0.0f) {
initMeshOverrides(); initMeshOverrides();
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
arms[i].shotTimer = MUZZLE_FLASH_TIME + 1.0f; arms[i].shotTimer = MUZZLE_FLASH_TIME + 1.0f;
arms[i].animTime = 0.0f; arms[i].animTime = 0.0f;
arms[i].rot = quat(0, 0, 0, 1); arms[i].rot = quat(0, 0, 0, 1);
arms[i].rotAbs = quat(0, 0, 0, 1);
} }
rotHead = rotChest = quat(0, 0, 0, 1); rotHead = rotChest = quat(0, 0, 0, 1);
@@ -261,7 +264,7 @@ struct Lara : Controller {
updateEntity(); updateEntity();
#endif #endif
} }
void wpnSet(Weapon::Type wType) { void wpnSet(Weapon::Type wType) {
wpnCurrent = wType; wpnCurrent = wType;
wpnState = Weapon::IS_FIRING; wpnState = Weapon::IS_FIRING;
@@ -289,6 +292,16 @@ struct Lara : Controller {
wpnSetState(wState); wpnSetState(wState);
} }
int wpnGetDamage() {
switch (wpnCurrent) {
case Weapon::PISTOLS : return 10;
case Weapon::SHOTGUN : return 5;
case Weapon::MAGNUMS : return 20;
case Weapon::UZIS : return 5;
default : return 0;
}
}
void wpnSetState(Weapon::State wState) { void wpnSetState(Weapon::State wState) {
if (wpnState == wState) return; if (wpnState == wState) return;
@@ -335,32 +348,6 @@ struct Lara : Controller {
wpnState = wState; wpnState = wState;
} }
/*
void setWeaponAnim(Weapon::Anim wAnim, float wAnimDir, Arm *arm) {
arm->animDir = wAnimDir;
if (wAnim != arm->anim) {
arm->anim = wAnim;
TR::Animation *anim = &level->anims[getWeaponAnimIndex(arm->anim)];
arm->animTime = arm->animDir >= 0.0f ? 0.0f : ((anim->frameEnd - anim->frameStart) / 30.0f);
arm->lastFrame = 0xFFFF;
}
}
void setWeapon(Weapon::Type wType, Weapon::State wState, Weapon::Anim wAnim = Weapon::Anim::NONE, float wAnimDir = 0.0f, Arm *arm = NULL) {
if (wpnCurrent != Weapon::SHOTGUN && wAnim != Weapon::Anim::HOLD && wAnim != Weapon::Anim::AIM && wAnim != Weapon::Anim::FIRE) {
setWeaponAnim(wAnim, wAnimDir, &arms[0]);
setWeaponAnim(wAnim, wAnimDir, &arms[1]);
} else
setWeaponAnim(wAnim, wAnimDir, !arm ? &arms[1] : arm);
if (wpnCurrent == wType && wpnState == wState)
return;
wpnCurrent = wType;
wpnSetState(wState);
}
*/
bool emptyHands() { bool emptyHands() {
return arms[0].anim == Weapon::Anim::NONE; return arms[0].anim == Weapon::Anim::NONE;
} }
@@ -422,7 +409,7 @@ struct Lara : Controller {
wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f); wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f);
wpnSetAnim(arms[1], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f); wpnSetAnim(arms[1], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f);
} else } else
wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, 1.0f); wpnSetAnim(arms[0], wpnState, Weapon::Anim::HOLSTER, 0.0f, 1.0f);
} }
} }
@@ -474,7 +461,7 @@ struct Lara : Controller {
int frameIndex = getFrameIndex(arms[i].animIndex, arms[i].animTime); int frameIndex = getFrameIndex(arms[i].animIndex, arms[i].animTime);
if (arms[i].anim == Weapon::Anim::FIRE) { if (arms[i].anim == Weapon::Anim::FIRE) {
if (frameIndex < arms[i].lastFrame) { if (frameIndex < arms[i].lastFrame) {
if (target == -1 || ((mask & ACTION) && arms[i].target > -1)) { if ((mask & ACTION) && (target == -1 || (target > -1 && arms[i].target > -1))) {
armShot[i] = true; armShot[i] = true;
} else } else
wpnSetAnim(arms[i], Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, target == -1); wpnSetAnim(arms[i], Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, target == -1);
@@ -516,24 +503,25 @@ struct Lara : Controller {
int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8); int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8);
quat tmp = animOverrides[joint]; vec3 p = getJoint(joint, false).getPos();
animOverrides[joint] = arm->rot * quat(vec3(1, 0, 0), PI * 0.5f) ; vec3 d = arm->rotAbs * vec3(0, 0, 1);
mat4 m = getJoint(joint, true);
animOverrides[joint] = tmp;
vec3 p = m.getPos();
vec3 d = (m * vec4(0, 1, 0, 0)).xyz;
vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f; vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f;
int room; int room;
vec3 hit = trace(getRoomIndex(), p, t, room, false); vec3 hit = trace(getRoomIndex(), p, t, room, false);
hit -= d * 64.0f; if (target > -1 && checkHit(target, p, hit, hit)) {
addSprite(level, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, SpriteController::FRAME_RANDOM); ((Controller*)level->entities[target].controller)->hit(wpnGetDamage());
hit -= d * 64.0f;
addSprite(level, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, SpriteController::FRAME_ANIMATED);
} else {
hit -= d * 64.0f;
addSprite(level, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, SpriteController::FRAME_RANDOM);
float dist = (hit - p).length(); float dist = (hit - p).length();
if (dist < nearDist) { if (dist < nearDist) {
nearPos = hit; nearPos = hit;
nearDist = dist; nearDist = dist;
}
} }
} }
@@ -622,39 +610,35 @@ struct Lara : Controller {
} }
void animateShotgun() { void animateShotgun() {
/* Arm &arm = arms[0];
float &wpnAnimDir = arms[0].animDir; if (arm.animDir >= 0.0f)
float &wpnAnimTime = arms[0].animTime; switch (arm.anim) {
Weapon::Anim &wpnAnim = arms[0].anim;
TR::Animation *anim = &level->anims[getWeaponAnimIndex(arms[0].anim)];
float maxTime = (anim->frameEnd - anim->frameStart) / 30.0f;
if (wpnAnimDir > 0.0f)
switch (wpnAnim) {
case Weapon::Anim::UNHOLSTER : case Weapon::Anim::UNHOLSTER :
if (wpnAnimTime >= maxTime) if (arm.animTime >= arm.animMaxTime)
setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f); wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false);
else if (wpnAnimTime >= maxTime * 0.3f) else if (arm.animTime >= arm.animMaxTime * 0.3f)
setWeapon(wpnCurrent, Weapon::IS_ARMED, wpnAnim, 1.0f); wpnSetAnim(arm, Weapon::IS_ARMED, arm.anim, arm.animTime, 1.0f);
break; break;
case Weapon::Anim::HOLSTER : case Weapon::Anim::HOLSTER :
if (wpnAnimTime >= maxTime) if (arm.animTime >= arm.animMaxTime)
setWeapon(wpnCurrent, Weapon::IS_HIDDEN, Weapon::Anim::NONE, wpnAnimDir); wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 1.0f, false);
else if (wpnAnimTime >= maxTime * 0.7f) else if (arm.animTime >= arm.animMaxTime * 0.7f)
setWeapon(wpnCurrent, Weapon::IS_HIDDEN, wpnAnim, 1.0f); wpnSetAnim(arm, Weapon::IS_HIDDEN, arm.anim, arm.animTime, 1.0f);
break; break;
case Weapon::Anim::AIM : case Weapon::Anim::AIM :
if (wpnAnimTime >= maxTime) if (arm.animTime >= arm.animMaxTime) {
setWeapon(wpnCurrent, Weapon::IS_FIRING, Weapon::Anim::FIRE, wpnAnimDir); if (mask & ACTION)
wpnSetAnim(arm, Weapon::IS_FIRING, Weapon::Anim::FIRE, arm.animTime - arm.animMaxTime, 1.0f);
else
wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, false);
}
break; break;
default : ; default : ;
}; };
if (wpnAnimDir < 0.0f && wpnAnimTime <= 0.0f) if (arm.animDir < 0.0f && arm.animTime <= 0.0f)
if (wpnAnim == Weapon::Anim::AIM) { if (arm.anim == Weapon::Anim::AIM)
setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLD, 0.0f); wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false);
};
*/
} }
void updateOverrides() { void updateOverrides() {
@@ -669,29 +653,30 @@ struct Lara : Controller {
animOverrides[ 7] = lerpFrames(frameA, frameB, t, 7); animOverrides[ 7] = lerpFrames(frameA, frameB, t, 7);
animOverrides[14] = lerpFrames(frameA, frameB, t, 14); animOverrides[14] = lerpFrames(frameA, frameB, t, 14);
// right arm
if (arms[0].anim != Weapon::Anim::NONE) { if (!emptyHands()) {
getFrames(&frameA, &frameB, t, arms[0].animIndex, arms[0].animTime); // right arm
Arm *arm = &arms[0];
getFrames(&frameA, &frameB, t, arm->animIndex, arm->animTime);
animOverrides[ 8] = lerpFrames(frameA, frameB, t, 8); animOverrides[ 8] = lerpFrames(frameA, frameB, t, 8);
animOverrides[ 9] = lerpFrames(frameA, frameB, t, 9); animOverrides[ 9] = lerpFrames(frameA, frameB, t, 9);
animOverrides[10] = lerpFrames(frameA, frameB, t, 10); animOverrides[10] = lerpFrames(frameA, frameB, t, 10);
animOverrideMask |= BODY_ARM_R; // left arm
} else if (wpnCurrent != Weapon::SHOTGUN) arm = &arms[1];
animOverrideMask &= ~BODY_ARM_R; getFrames(&frameA, &frameB, t, arm->animIndex, arm->animTime);
// left arm
if (arms[1].anim != Weapon::Anim::NONE) {
getFrames(&frameA, &frameB, t, arms[1].animIndex, arms[1].animTime);
animOverrides[11] = lerpFrames(frameA, frameB, t, 11); animOverrides[11] = lerpFrames(frameA, frameB, t, 11);
animOverrides[12] = lerpFrames(frameA, frameB, t, 12); animOverrides[12] = lerpFrames(frameA, frameB, t, 12);
animOverrides[13] = lerpFrames(frameA, frameB, t, 13); animOverrides[13] = lerpFrames(frameA, frameB, t, 13);
animOverrideMask |= BODY_ARM_L;
animOverrideMask |= BODY_ARM_R | BODY_ARM_L;
} else } else
animOverrideMask &= ~BODY_ARM_L; animOverrideMask &= ~(BODY_ARM_R | BODY_ARM_L);
lookAt(target); lookAt(target);
if (wpnCurrent != Weapon::SHOTGUN) if (wpnCurrent == Weapon::SHOTGUN)
aimShotgun();
else
aimPistols(); aimPistols();
} }
@@ -714,6 +699,12 @@ struct Lara : Controller {
animOverrides[14] = rotHead * animOverrides[14]; animOverrides[14] = rotHead * animOverrides[14];
} }
void aimShotgun() {
quat rot;
if (!aim(target, 14, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.25f, PI * 0.25f), rot, &arms[0].rotAbs))
arms[0].target = -1;
}
void aimPistols() { void aimPistols() {
float speed = 8.0f * Core::deltaTime; float speed = 8.0f * Core::deltaTime;
@@ -721,48 +712,53 @@ struct Lara : Controller {
int joint = i ? 11 : 8; int joint = i ? 11 : 8;
vec4 range = i ? vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.5f, PI * 0.2f) : vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.2f, PI * 0.5f); vec4 range = i ? vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.5f, PI * 0.2f) : vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.2f, PI * 0.5f);
Arm *arm = &arms[i]; Arm &arm = arms[i];
quat rot; quat rot;
if (!aim(target, joint, range, rot)) { if (!aim(target, joint, range, rot, &arm.rotAbs)) {
arm->target = -1; arm.target = -1;
rot = quat(0, 0, 0, 1); rot = quat(0, 0, 0, 1);
} else }
arm->target = target;
float t; float t;
if (arm->anim == Weapon::Anim::FIRE) if (arm.anim == Weapon::Anim::FIRE)
t = 1.0f; t = 1.0f;
else if (arm->anim == Weapon::Anim::AIM) else if (arm.anim == Weapon::Anim::AIM)
t = arm->animTime / arm->animMaxTime; t = arm.animTime / arm.animMaxTime;
else else
t = 0.0f; t = 0.0f;
arm->rot = arm->rot.slerp(rot, speed); arm.rot = arm.rot.slerp(rot, speed);
animOverrides[joint] = animOverrides[joint].slerp(arm->rot * animOverrides[joint], t); animOverrides[joint] = animOverrides[joint].slerp(arm.rot * animOverrides[joint], t);
} }
} }
bool aim(int target, int joint, const vec4 &angleRange, quat &rot) { bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
if (target == -1) return false; if (target > -1) {
TR::Entity &e = level->entities[target];
vec3 t(e.x, e.y, e.z);
TR::Entity &e = level->entities[target]; mat4 m = getJoint(joint);
vec3 t(e.x, e.y, e.z); vec3 delta = (m.inverse() * t).normal();
mat4 m = getJoint(joint); float angleY = clampAngle(atan2(delta.x, delta.z));
vec3 delta = (m.inverse() * t).normal(); float angleX = clampAngle(asinf(delta.y));
float angleY = clampAngle(atan2(delta.x, delta.z)); if (angleX > angleRange.x && angleX <= angleRange.y &&
float angleX = clampAngle(asinf(delta.y)); angleY > angleRange.z && angleY <= angleRange.w) {
if (angleX < angleRange.x || angleX > angleRange.y || quat ax(vec3(1, 0, 0), -angleX);
angleY < angleRange.z || angleY > angleRange.w) quat ay(vec3(0, 1, 0), angleY);
return false;
quat ax(vec3(1, 0, 0), -angleX); rot = ay * ax;
quat ay(vec3(0, 1, 0), angleY); if (rotAbs)
*rotAbs = m.getRot() * rot;
return true;
}
}
rot = ay * ax; if (rotAbs)
return true; *rotAbs = rotYXZ(angle);
return false;
} }
void updateTargets() { void updateTargets() {
@@ -771,24 +767,33 @@ struct Lara : Controller {
return; return;
} }
if (!(mask & ACTION) || target == -1) { if (!(mask & ACTION)) {
target = getTarget(); target = getTarget();
arms[0].target = target; arms[0].target = arms[1].target = target;
arms[1].target = target; } else
} if (target > -1) {
TR::Entity &e = level->entities[target];
vec3 to = vec3(e.x, e.y, e.z);
vec3 from = pos - vec3(0, 512, 0);
arms[0].target = arms[1].target = checkOcclusion(from, to, (to - from).length()) ? target : -1;
}
} }
int getTarget() { int getTarget() {
int dist = TARGET_MAX_DIST * 3.0f; vec3 dir = getDir().normal();
int dist = TARGET_MAX_DIST;// * TARGET_MAX_DIST;
int index = -1; int index = -1;
TR::Entity &entity = getEntity();
for (int i = 0; i < level->entitiesCount; i++) { for (int i = 0; i < level->entitiesCount; i++) {
TR::Entity &e = level->entities[i]; TR::Entity &e = level->entities[i];
if (!e.isEnemy()) continue; if (!e.flags.rendered || !e.isEnemy()) continue;
int d = abs(e.x - entity.x) + abs(e.y - entity.y) + abs(e.z - entity.z); vec3 p = vec3(e.x, e.y, e.z);
if (d < dist) { vec3 v = p - pos;
if (dir.dot(v.normal()) <= 0.5f) continue; // target is out of sigth -60..+60 degrees
int d = v.length();
if (d < dist && checkOcclusion(pos - vec3(0, 512, 0), p, d) ) {
index = i; index = i;
dist = d; dist = d;
} }
@@ -797,6 +802,25 @@ struct Lara : Controller {
return index; return index;
} }
bool checkOcclusion(const vec3 &from, const vec3 &to, float dist) {
int room;
vec3 d = trace(getRoomIndex(), from, to, room, false); // check occlusion
return ((d - from).length() > (dist - 512.0f));
}
bool checkHit(int target, const vec3 &from, const vec3 &to, vec3 &point) {
TR::Entity &e = level->entities[target];
Box box = ((Controller*)e.controller)->getBoundingBox();
float t;
vec3 v = (to - from);
vec3 dir = v.normal();
if (box.intersect(from, dir, t) && v.length() > t) {
point = from + dir * t;
return true;
} else
return false;
}
bool waterOut(int &outState) { bool waterOut(int &outState) {
// TODO: playSound 36 // TODO: playSound 36
vec3 dst = pos + getDir() * 32.0f; vec3 dst = pos + getDir() * 32.0f;
@@ -965,14 +989,14 @@ struct Lara : Controller {
activateNext(); activateNext();
} }
vec3 getViewOffset() { vec3 getViewPoint() {
vec3 offset = vec3(0.0f); vec3 offset = chestOffset;
if (stand != STAND_UNDERWATER) if (stand != STAND_UNDERWATER)
offset.y -= 256.0f; offset.y -= 256.0f;
if (wpnState != Weapon::IS_HIDDEN) if (wpnState != Weapon::IS_HIDDEN)
offset.y -= 256.0f; offset.y -= 256.0f;
return chestOffset - pos + offset; return offset;
} }
virtual Stand getStand() { virtual Stand getStand() {
@@ -1722,14 +1746,11 @@ struct Lara : Controller {
float alpha = min(1.0f, (0.1f - time) * 20.0f); float alpha = min(1.0f, (0.1f - time) * 20.0f);
float lum = 3.0f; float lum = 3.0f;
mat4 tmp = Core::mModel; mat4 m(matrix);
Core::mModel = matrix; m.rotateX(-PI * 0.5f);
Core::mModel.rotateX(-PI * 0.5f); m.translate(offset);
Core::mModel.translate(offset);
Core::active.shader->setParam(uColor, vec4(lum, lum, lum, alpha)); Core::active.shader->setParam(uColor, vec4(lum, lum, lum, alpha));
renderMesh(mesh, level->models[47].mStart); renderMesh(m, mesh, level->models[47].mStart);
Core::active.shader->setParam(uColor, Core::color);
Core::mModel = tmp;
} }
virtual void render(Frustum *frustum, MeshBuilder *mesh) { virtual void render(Frustum *frustum, MeshBuilder *mesh) {

View File

@@ -371,12 +371,9 @@ struct Level {
ASSERT(entity.controller); ASSERT(entity.controller);
TR::Room &room = level.rooms[entity.room]; TR::Room &room = level.rooms[entity.room];
if (!room.flags.rendered || entity.flags.invisible) // check for room visibility if (!room.flags.rendered || entity.flags.invisible || entity.flags.rendered)
return; return;
mat4 m = Core::mModel;
Core::mModel.translate(vec3(entity.x, entity.y, entity.z));
float c = (entity.intensity > -1) ? (1.0f - entity.intensity / (float)0x1FFF) : 1.0f; float c = (entity.intensity > -1) ? (1.0f - entity.intensity / (float)0x1FFF) : 1.0f;
float l = 1.0f; float l = 1.0f;
@@ -395,8 +392,6 @@ struct Level {
Core::active.shader->setParam(uColor, Core::color); Core::active.shader->setParam(uColor, Core::color);
((Controller*)entity.controller)->render(camera->frustum, mesh); ((Controller*)entity.controller)->render(camera->frustum, mesh);
Core::mModel = m;
} }
void update() { void update() {
@@ -446,6 +441,9 @@ struct Level {
for (int j = 0; j < room.meshesCount; j++) for (int j = 0; j < room.meshesCount; j++)
room.meshes[j].flags.rendered = false; // clear visible flag for room static meshes room.meshes[j].flags.rendered = false; // clear visible flag for room static meshes
} }
for (int i = 0; i < level.entitiesCount; i++)
level.entities[i].flags.rendered = false;
} }
void renderRooms() { void renderRooms() {

View File

@@ -6,7 +6,7 @@
<input type="button" value="fullscreen" onclick="Module.requestFullScreen(false, true)"><br><br> <input type="button" value="fullscreen" onclick="Module.requestFullScreen(false, true)"><br><br>
<canvas id="canvas" width="160" height="120" oncontextmenu="event.preventDefault()"></canvas><br> <canvas id="canvas" width="160" height="120" oncontextmenu="event.preventDefault()"></canvas><br>
<span id="info"><a target="_blank" href="https://github.com/XProger/OpenLara">OpenLara on github</a><br>controls:<br>keyboad: move - WASD / arrows, jump - Space, action - E/Ctrl, draw weapon - Q, change weapon - 1-4, walk - Shift, side steps - ZX/walk+direction, camera - MouseR)<br>gamepad: PSX controls on XBox controller</span> <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>
<script type='text/javascript'> <script type='text/javascript'>
var statusElement = document.getElementById('status'); var statusElement = document.getElementById('status');
var canvasElement = document.getElementById('canvas'); var canvasElement = document.getElementById('canvas');
@@ -29,7 +29,7 @@
} }
}, },
canvas: (function() { canvas: (function() {
canvasElement.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); canvasElement.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvasElement; return canvasElement;
})(), })(),
setStatus: function(text) { setStatus: function(text) {
@@ -43,14 +43,14 @@
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}, },
}; };
function snd_init() { function snd_init() {
var AudioContext = window.AudioContext || window.webkitAudioContext; var AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return; if (!AudioContext) return;
var ctx = new (window.AudioContext || window.webkitAudioContext)(); var ctx = new (window.AudioContext || window.webkitAudioContext)();
var count = 2048; var count = 2048;
var rate = 44100 / ctx.sampleRate; var rate = 44100 / ctx.sampleRate;
var framesCount = Math.ceil(count * rate); var framesCount = Math.ceil(count * rate);
var frames = Module._malloc(framesCount * 4); // interleaved short L, R var frames = Module._malloc(framesCount * 4); // interleaved short L, R
var proc = ctx.createScriptProcessor(count, 2, 2); var proc = ctx.createScriptProcessor(count, 2, 2);
proc.onaudioprocess = function(e) { proc.onaudioprocess = function(e) {
@@ -58,16 +58,16 @@
R = e.outputBuffer.getChannelData(1); R = e.outputBuffer.getChannelData(1);
Module.ccall('snd_fill', 'null', ['number', 'number'], [frames, framesCount]); Module.ccall('snd_fill', 'null', ['number', 'number'], [frames, framesCount]);
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
var index = frames + Math.floor(i * rate) * 4; // 4 is sample frame stride in bytes var index = frames + Math.floor(i * rate) * 4; // 4 is sample frame stride in bytes
L[i] = Module.getValue(index , 'i16') / 0x8000; L[i] = Module.getValue(index , 'i16') / 0x8000;
R[i] = Module.getValue(index + 2, 'i16') / 0x8000; R[i] = Module.getValue(index + 2, 'i16') / 0x8000;
} }
} }
proc.connect(ctx.destination); proc.connect(ctx.destination);
} }
var gl = canvasElement.getContext("webgl") || canvasElement.getContext("experimental-webgl"); var gl = canvasElement.getContext("webgl") || canvasElement.getContext("experimental-webgl");
Module.setStatus('Downloading...'); Module.setStatus('Downloading...');
window.onerror = function(event) { window.onerror = function(event) {
Module.setStatus('Exception thrown, see JavaScript console'); Module.setStatus('Exception thrown, see JavaScript console');
@@ -77,7 +77,7 @@
if (text) Module.printErr('[post-exception status] ' + text); if (text) Module.printErr('[post-exception status] ' + text);
}; };
}; };
</script> </script>
<script> <script>
(function() { (function() {
var memoryInitializer = 'OpenLara.js.mem'; var memoryInitializer = 'OpenLara.js.mem';
@@ -95,8 +95,13 @@
var script = document.createElement('script'); var script = document.createElement('script');
script.src = "OpenLara.js"; script.src = "OpenLara.js";
document.body.appendChild(script); document.body.appendChild(script);
window.onbeforeunload = function () { // Ctrl+W
return "Really want to quit the game?";
};
</script> </script>
<audio autoplay loop><source src="05.ogg" type="audio/ogg"></audio> <audio autoplay loop><source src="05.ogg" type="audio/ogg"></audio>
</body> </body>
</html> </html>

View File

@@ -26,12 +26,12 @@
#endif #endif
#endif #endif
#define EPS FLT_EPSILON
#define INF INFINITY
#define PI 3.14159265358979323846f #define PI 3.14159265358979323846f
#define PI2 (PI * 2.0f) #define PI2 (PI * 2.0f)
#define DEG2RAD (PI / 180.0f) #define DEG2RAD (PI / 180.0f)
#define RAD2DEG (180.0f / PI) #define RAD2DEG (180.0f / PI)
#define EPS FLT_EPSILON
#define randf() ((float)rand()/RAND_MAX) #define randf() ((float)rand()/RAND_MAX)
typedef char int8; typedef char int8;
@@ -202,6 +202,11 @@ struct quat {
w * q.w - x * q.x - y * q.y - z * q.z); w * q.w - x * q.x - y * q.y - z * q.z);
} }
vec3 operator * (const vec3 &v) const {
// return v + xyz.cross(xyz.cross(v) + v * w) * 2.0f;
return (*this * quat(v.x, v.y, v.z, 0) * inverse()).xyz;
}
float dot(const quat &q) const { float dot(const quat &q) const {
return x * q.x + y * q.y + z * q.z + w * q.w; return x * q.x + y * q.y + z * q.z + w * q.w;
} }
@@ -541,6 +546,22 @@ struct Box {
bool intersect(const Box &box) const { bool intersect(const Box &box) const {
return !((max.x < box.min.x || min.x > box.max.x) || (max.y < box.min.y || min.y > box.max.y) || (max.z < box.min.z || min.z > box.max.z)); return !((max.x < box.min.x || min.x > box.max.x) || (max.y < box.min.y || min.y > box.max.y) || (max.z < box.min.z || min.z > box.max.z));
} }
bool intersect(const vec3 &rayPos, const vec3 &rayDir, float &t) const {
float t1 = INF, t0 = -t1;
for (int i = 0; i < 3; i++)
if (rayDir[i] != 0) {
float lo = (min[i] - rayPos[i]) / rayDir[i];
float hi = (max[i] - rayPos[i]) / rayDir[i];
t0 = ::max(t0, ::min(lo, hi));
t1 = ::min(t1, ::max(lo, hi));
} else
if (rayPos[i] < min[i] || rayPos[i] > max[i])
return false;
t = t0;
return (t0 <= t1) && (t1 > 0);
}
}; };
struct Stream { struct Stream {