mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-12 08:04:09 +02:00
Merge remote-tracking branch 'refs/remotes/XProger/master'
This commit is contained in:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
13
src/camera.h
13
src/camera.h
@@ -19,10 +19,9 @@ 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 = 75.0f;
|
fov = 80.0f;
|
||||||
znear = 128;
|
znear = 128;
|
||||||
zfar = 100.0f * 1024.0f;
|
zfar = 100.0f * 1024.0f;
|
||||||
angleAdv = vec3(0.0f);
|
angleAdv = vec3(0.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() {
|
||||||
@@ -57,6 +56,8 @@ struct Camera : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void update() {
|
virtual void update() {
|
||||||
|
actTargetEntity = owner->target;
|
||||||
|
|
||||||
if (timer > 0.0f) {
|
if (timer > 0.0f) {
|
||||||
timer -= Core::deltaTime;
|
timer -= Core::deltaTime;
|
||||||
if (timer <= 0.0f) {
|
if (timer <= 0.0f) {
|
||||||
@@ -90,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];
|
||||||
|
196
src/controller.h
196
src/controller.h
@@ -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);
|
||||||
@@ -96,6 +92,89 @@ struct Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void updateEntity() {
|
void updateEntity() {
|
||||||
TR::Entity &e = getEntity();
|
TR::Entity &e = getEntity();
|
||||||
e.x = int(pos.x);
|
e.x = int(pos.x);
|
||||||
@@ -265,20 +344,11 @@ struct Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual Box getBoundingBox() {
|
virtual Box getBoundingBox() {
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
float t;
|
||||||
TR::Model &model = getModel();
|
TR::AnimFrame *frameA, *frameB;
|
||||||
|
getFrames(&frameA, &frameB, t, animIndex, animTime, true);
|
||||||
|
|
||||||
float k = animTime * 30.0f / anim->frameRate;
|
Box box(frameA->box.min().lerp(frameB->box.min(), t), frameA->box.max().lerp(frameB->box.max(), t));
|
||||||
int fIndex = (int)k;
|
|
||||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
|
||||||
|
|
||||||
int fSize = sizeof(TR::AnimFrame) + model.mCount * sizeof(uint16) * 2;
|
|
||||||
k = k - fIndex;
|
|
||||||
|
|
||||||
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
|
|
||||||
TR::AnimFrame *fA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1];
|
|
||||||
TR::AnimFrame *fB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
|
|
||||||
Box box(fA->box.min().lerp(fB->box.min(), k), fA->box.max().lerp(fB->box.max(), k));
|
|
||||||
box.rotate90(getEntity().rotation.value / 0x4000);
|
box.rotate90(getEntity().rotation.value / 0x4000);
|
||||||
box.min += pos;
|
box.min += pos;
|
||||||
box.max += pos;
|
box.max += pos;
|
||||||
@@ -454,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];
|
||||||
@@ -599,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) {
|
||||||
@@ -617,60 +688,36 @@ struct Controller {
|
|||||||
|
|
||||||
Core::active.shader->setParam(uModel, m);
|
Core::active.shader->setParam(uModel, m);
|
||||||
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
Core::active.shader->setParam(uColor, vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||||
|
Core::active.shader->setParam(uAmbient, vec3(0.0f));
|
||||||
mesh->renderShadowSpot();
|
mesh->renderShadowSpot();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
TR::Animation *anim = &level->anims[animIndex];
|
||||||
float fTime;
|
|
||||||
vec3 angle;
|
|
||||||
|
|
||||||
Controller *controller = (Controller*)entity.controller;
|
mat4 matrix(Core::mModel);
|
||||||
|
matrix.translate(pos);
|
||||||
anim = &level->anims[controller->animIndex];
|
if (angle.y != 0.0f) matrix.rotateY(angle.y);
|
||||||
angle = controller->angle;
|
if (angle.x != 0.0f) matrix.rotateX(angle.x);
|
||||||
fTime = controller->animTime;
|
if (angle.z != 0.0f) matrix.rotateZ(angle.z);
|
||||||
|
|
||||||
if (angle.y != 0.0f) Core::mModel.rotateY(angle.y);
|
|
||||||
if (angle.x != 0.0f) Core::mModel.rotateX(angle.x);
|
|
||||||
if (angle.z != 0.0f) Core::mModel.rotateZ(angle.z);
|
|
||||||
|
|
||||||
float k = fTime * 30.0f / anim->frameRate;
|
|
||||||
int fIndex = (int)k;
|
|
||||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
|
||||||
|
|
||||||
int fSize = sizeof(TR::AnimFrame) + model.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::Animation *nextAnim = NULL;
|
|
||||||
|
|
||||||
|
float t;
|
||||||
vec3 move(0.0f);
|
vec3 move(0.0f);
|
||||||
if (fIndexB == 0) {
|
TR::AnimFrame *frameA, *frameB;
|
||||||
move = getAnimMove();
|
getFrames(&frameA, &frameB, t, animIndex, animTime, true, &move);
|
||||||
nextAnim = &level->anims[anim->nextAnimation];
|
|
||||||
fIndexB = (anim->nextFrame - nextAnim->frameStart) / nextAnim->frameRate;
|
|
||||||
} else
|
|
||||||
nextAnim = anim;
|
|
||||||
|
|
||||||
TR::AnimFrame *frameB = (TR::AnimFrame*)&level->frameData[(nextAnim->frameOffset + fIndexB * fSize) >> 1];
|
vec3 bmin = frameA->box.min().lerp(frameB->box.min(), t);
|
||||||
|
vec3 bmax = frameA->box.max().lerp(frameB->box.max(), t);
|
||||||
vec3 bmin = frameA->box.min().lerp(frameB->box.min(), k);
|
if (frustum && !frustum->isVisible(matrix, bmin, bmax))
|
||||||
vec3 bmax = frameA->box.max().lerp(frameB->box.max(), k);
|
|
||||||
if (frustum && !frustum->isVisible(Core::mModel, 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, k));
|
|
||||||
|
|
||||||
int sIndex = 0;
|
int sIndex = 0;
|
||||||
mat4 stack[20];
|
mat4 stack[20];
|
||||||
@@ -680,32 +727,28 @@ 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;
|
||||||
if (animOverrideMask & (1 << i))
|
if (animOverrideMask & (1 << i))
|
||||||
q = animOverrides[i];
|
q = animOverrides[i];
|
||||||
else
|
else
|
||||||
q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), k);
|
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)) {
|
||||||
@@ -765,8 +808,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -83,6 +83,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_LIGHTS 3
|
||||||
|
|
||||||
struct Shader;
|
struct Shader;
|
||||||
struct Texture;
|
struct Texture;
|
||||||
|
|
||||||
@@ -113,8 +115,8 @@ namespace Core {
|
|||||||
float deltaTime;
|
float deltaTime;
|
||||||
mat4 mView, mProj, mViewProj, mViewInv, mModel;
|
mat4 mView, mProj, mViewProj, mViewInv, mModel;
|
||||||
vec3 viewPos;
|
vec3 viewPos;
|
||||||
vec3 lightPos;
|
vec3 lightPos[MAX_LIGHTS];
|
||||||
vec4 lightColor;
|
vec4 lightColor[MAX_LIGHTS];
|
||||||
vec3 ambient;
|
vec3 ambient;
|
||||||
vec4 color;
|
vec4 color;
|
||||||
|
|
||||||
@@ -185,6 +187,9 @@ namespace Core {
|
|||||||
support.VAO = (void*)glBindVertexArray != NULL;
|
support.VAO = (void*)glBindVertexArray != NULL;
|
||||||
|
|
||||||
Sound::init();
|
Sound::init();
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||||
|
lightColor[i] = vec4(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void free() {
|
void free() {
|
||||||
|
@@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -415,6 +415,10 @@ namespace TR {
|
|||||||
uint16 align;
|
uint16 align;
|
||||||
int16 modelIndex; // index of representation in models (index + 1) or spriteSequences (-(index + 1)) arrays
|
int16 modelIndex; // index of representation in models (index + 1) or spriteSequences (-(index + 1)) arrays
|
||||||
void *controller; // Controller implementation or NULL
|
void *controller; // Controller implementation or NULL
|
||||||
|
|
||||||
|
bool isEnemy() {
|
||||||
|
return type >= ENEMY_TWIN && type <= ENEMY_LARSON;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Animation {
|
struct Animation {
|
||||||
|
655
src/lara.h
655
src/lara.h
@@ -25,6 +25,9 @@
|
|||||||
#define MAX_TRIGGER_ACTIONS 64
|
#define MAX_TRIGGER_ACTIONS 64
|
||||||
|
|
||||||
#define DESCENT_SPEED 2048.0f
|
#define DESCENT_SPEED 2048.0f
|
||||||
|
#define MUZZLE_FLASH_TIME 0.1f
|
||||||
|
#define FLASH_LIGHT_COLOR vec4(0.8f, 0.7f, 0.3f, 2048 * 2048)
|
||||||
|
#define TARGET_MAX_DIST (8.0f * 1024.0f)
|
||||||
|
|
||||||
struct Lara : Controller {
|
struct Lara : Controller {
|
||||||
|
|
||||||
@@ -141,19 +144,19 @@ struct Lara : Controller {
|
|||||||
|
|
||||||
enum : int {
|
enum : int {
|
||||||
BODY_HIP = 0x0001,
|
BODY_HIP = 0x0001,
|
||||||
BODY_LEG_R1 = 0x0002,
|
BODY_LEG_L1 = 0x0002,
|
||||||
BODY_LEG_R2 = 0x0004,
|
BODY_LEG_L2 = 0x0004,
|
||||||
BODY_LEG_R3 = 0x0008,
|
BODY_LEG_L3 = 0x0008,
|
||||||
BODY_LEG_L1 = 0x0010,
|
BODY_LEG_R1 = 0x0010,
|
||||||
BODY_LEG_L2 = 0x0020,
|
BODY_LEG_R2 = 0x0020,
|
||||||
BODY_LEG_L3 = 0x0040,
|
BODY_LEG_R3 = 0x0040,
|
||||||
BODY_CHEST = 0x0080,
|
BODY_CHEST = 0x0080,
|
||||||
BODY_ARM_L1 = 0x0100,
|
BODY_ARM_R1 = 0x0100,
|
||||||
BODY_ARM_L2 = 0x0200,
|
BODY_ARM_R2 = 0x0200,
|
||||||
BODY_ARM_L3 = 0x0400,
|
BODY_ARM_R3 = 0x0400,
|
||||||
BODY_ARM_R1 = 0x0800,
|
BODY_ARM_L1 = 0x0800,
|
||||||
BODY_ARM_R2 = 0x1000,
|
BODY_ARM_L2 = 0x1000,
|
||||||
BODY_ARM_R3 = 0x2000,
|
BODY_ARM_L3 = 0x2000,
|
||||||
BODY_HEAD = 0x4000,
|
BODY_HEAD = 0x4000,
|
||||||
BODY_ARM_L = BODY_ARM_L1 | BODY_ARM_L2 | BODY_ARM_L3,
|
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_ARM_R = BODY_ARM_R1 | BODY_ARM_R2 | BODY_ARM_R3,
|
||||||
@@ -172,24 +175,46 @@ struct Lara : Controller {
|
|||||||
} weapons[Weapon::MAX];
|
} weapons[Weapon::MAX];
|
||||||
|
|
||||||
Weapon::Type wpnCurrent;
|
Weapon::Type wpnCurrent;
|
||||||
Weapon::State wpnState;
|
|
||||||
Weapon::Anim wpnAnim;
|
|
||||||
float wpnAnimTime;
|
|
||||||
float wpnAnimDir;
|
|
||||||
int wpnLastFrame;
|
|
||||||
Weapon::Type wpnNext;
|
Weapon::Type wpnNext;
|
||||||
float wpnShotTime[2];
|
Weapon::State wpnState;
|
||||||
|
vec3 chestOffset;
|
||||||
|
|
||||||
Lara(TR::Level *level, int entity) : Controller(level, entity), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY) {
|
struct Arm {
|
||||||
|
int target;
|
||||||
|
int lastFrame;
|
||||||
|
float shotTimer;
|
||||||
|
float weight;
|
||||||
|
float animDir;
|
||||||
|
float animTime;
|
||||||
|
float animMaxTime;
|
||||||
|
int animIndex;
|
||||||
|
quat rot, rotAbs;
|
||||||
|
Weapon::Anim anim;
|
||||||
|
} arms[2];
|
||||||
|
|
||||||
|
int target;
|
||||||
|
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), health(100), turnTime(0.0f) {
|
||||||
initMeshOverrides();
|
initMeshOverrides();
|
||||||
wpnShotTime[0] = wpnShotTime[1] = 0.0f;
|
for (int i = 0; i < 2; i++) {
|
||||||
|
arms[i].shotTimer = MUZZLE_FLASH_TIME + 1.0f;
|
||||||
|
arms[i].animTime = 0.0f;
|
||||||
|
arms[i].rot = quat(0, 0, 0, 1);
|
||||||
|
arms[i].rotAbs = quat(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rotHead = rotChest = quat(0, 0, 0, 1);
|
||||||
memset(weapons, -1, sizeof(weapons));
|
memset(weapons, -1, sizeof(weapons));
|
||||||
weapons[Weapon::PISTOLS].ammo = 0;
|
weapons[Weapon::PISTOLS].ammo = 0;
|
||||||
weapons[Weapon::SHOTGUN].ammo = 9000;
|
weapons[Weapon::SHOTGUN].ammo = 9000;
|
||||||
weapons[Weapon::MAGNUMS].ammo = 9000;
|
weapons[Weapon::MAGNUMS].ammo = 9000;
|
||||||
weapons[Weapon::UZIS ].ammo = 9000;
|
weapons[Weapon::UZIS ].ammo = 9000;
|
||||||
|
|
||||||
setWeapon(Weapon::PISTOLS, Weapon::IS_HIDDEN);
|
wpnSet(Weapon::PISTOLS);
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
/*
|
/*
|
||||||
// gym
|
// gym
|
||||||
@@ -241,21 +266,48 @@ struct Lara : Controller {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWeapon(Weapon::Type wType, Weapon::State wState, Weapon::Anim wAnim = Weapon::Anim::NONE, float wAnimDir = 0.0f) {
|
void wpnSet(Weapon::Type wType) {
|
||||||
wpnAnimDir = wAnimDir;
|
wpnCurrent = wType;
|
||||||
|
wpnState = Weapon::IS_FIRING;
|
||||||
if (wAnim != wpnAnim) {
|
wpnSetAnim(arms[0], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f);
|
||||||
wpnAnim = wAnim;
|
wpnSetAnim(arms[1], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f);
|
||||||
TR::Animation *anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
|
|
||||||
wpnAnimTime = wpnAnimDir >= 0.0f ? 0.0f : ((anim->frameEnd - anim->frameStart) / 30.0f);
|
|
||||||
wpnLastFrame = 0xFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wpnCurrent == wType && wpnState == wState)
|
void wpnSetAnim(Arm &arm, Weapon::State wState, Weapon::Anim wAnim, float wAnimTime, float wAnimDir, bool playing = true) {
|
||||||
return;
|
if (arm.anim != wAnim)
|
||||||
|
arm.lastFrame = 0xFFFF;
|
||||||
|
|
||||||
|
arm.anim = wAnim;
|
||||||
|
arm.animIndex = wpnGetAnimIndex(wAnim);
|
||||||
|
TR::Animation &anim = level->anims[arm.animIndex];
|
||||||
|
|
||||||
|
arm.animDir = playing ? wAnimDir : 0.0f;
|
||||||
|
arm.animMaxTime = (anim.frameEnd - anim.frameStart) / 30.0f;
|
||||||
|
|
||||||
|
if (wAnimDir > 0.0f)
|
||||||
|
arm.animTime = wAnimTime;
|
||||||
|
else
|
||||||
|
if (wAnimDir < 0.0f)
|
||||||
|
arm.animTime = arm.animMaxTime + wAnimTime;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (wpnState == wState) return;
|
||||||
|
|
||||||
int mask = 0;
|
int mask = 0;
|
||||||
switch (wType) {
|
switch (wpnCurrent) {
|
||||||
case Weapon::EMPTY : break;
|
case Weapon::EMPTY : break;
|
||||||
case Weapon::PISTOLS :
|
case Weapon::PISTOLS :
|
||||||
case Weapon::MAGNUMS :
|
case Weapon::MAGNUMS :
|
||||||
@@ -280,30 +332,30 @@ struct Lara : Controller {
|
|||||||
if (wpnState == Weapon::IS_ARMED && wState == Weapon::IS_HIDDEN) playSound(TR::SND_HOLSTER, pos, Sound::Flags::PAN);
|
if (wpnState == Weapon::IS_ARMED && wState == Weapon::IS_HIDDEN) playSound(TR::SND_HOLSTER, pos, Sound::Flags::PAN);
|
||||||
|
|
||||||
int resetMask = BODY_HEAD | BODY_UPPER | BODY_LOWER;
|
int resetMask = BODY_HEAD | BODY_UPPER | BODY_LOWER;
|
||||||
if (wType == Weapon::SHOTGUN)
|
if (wpnCurrent == Weapon::SHOTGUN)
|
||||||
resetMask &= ~(BODY_LEG_L1 | BODY_LEG_R1);
|
resetMask &= ~(BODY_LEG_L1 | BODY_LEG_R1);
|
||||||
|
|
||||||
// restore original meshes first
|
// restore original meshes first
|
||||||
meshSwap(level->models[Weapon::EMPTY], resetMask);
|
meshSwap(level->models[Weapon::EMPTY], resetMask);
|
||||||
// replace some parts
|
// replace some parts
|
||||||
meshSwap(level->models[wType], mask);
|
meshSwap(level->models[wpnCurrent], mask);
|
||||||
// have a shotgun in inventory place it on the back if another weapon is in use
|
// 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)
|
if (wpnCurrent != Weapon::SHOTGUN && weapons[Weapon::SHOTGUN].ammo != -1)
|
||||||
meshSwap(level->models[Weapon::SHOTGUN], BODY_CHEST);
|
meshSwap(level->models[Weapon::SHOTGUN], BODY_CHEST);
|
||||||
// mesh swap to angry Lara's head while firing (from uzis model)
|
// mesh swap to angry Lara's head while firing (from uzis model)
|
||||||
if (wState == Weapon::IS_FIRING)
|
if (wState == Weapon::IS_FIRING)
|
||||||
meshSwap(level->models[Weapon::UZIS], BODY_HEAD);
|
meshSwap(level->models[Weapon::UZIS], BODY_HEAD);
|
||||||
|
|
||||||
wpnCurrent = wType;
|
|
||||||
wpnState = wState;
|
wpnState = wState;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emptyHands() {
|
bool emptyHands() {
|
||||||
return wpnState == Weapon::IS_HIDDEN;
|
return arms[0].anim == Weapon::Anim::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canDrawWeapon() {
|
bool canDrawWeapon() {
|
||||||
return wpnCurrent != Weapon::EMPTY
|
return wpnCurrent != Weapon::EMPTY
|
||||||
|
&& emptyHands()
|
||||||
&& state != STATE_DEATH
|
&& state != STATE_DEATH
|
||||||
&& state != STATE_HANG
|
&& state != STATE_HANG
|
||||||
&& state != STATE_REACH
|
&& state != STATE_REACH
|
||||||
@@ -336,33 +388,39 @@ struct Lara : Controller {
|
|||||||
&& state != STATE_WATER_OUT;
|
&& state != STATE_WATER_OUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawWeapon() {
|
bool wpnReady() {
|
||||||
|
return arms[0].anim != Weapon::Anim::PREPARE && arms[0].anim != Weapon::Anim::UNHOLSTER && arms[0].anim != Weapon::Anim::HOLSTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wpnDraw() {
|
||||||
if (!canDrawWeapon()) return;
|
if (!canDrawWeapon()) return;
|
||||||
|
|
||||||
if (wpnAnim != Weapon::Anim::PREPARE && wpnAnim != Weapon::Anim::UNHOLSTER && wpnAnim != Weapon::Anim::HOLSTER && emptyHands()) {
|
if (wpnReady() && emptyHands()) {
|
||||||
bool isRifle = wpnCurrent == Weapon::SHOTGUN;
|
if (wpnCurrent != Weapon::SHOTGUN) {
|
||||||
setWeapon(wpnCurrent, wpnState, isRifle ? Weapon::Anim::UNHOLSTER : Weapon::Anim::PREPARE, 1.0f);
|
wpnSetAnim(arms[0], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f);
|
||||||
|
wpnSetAnim(arms[1], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f);
|
||||||
|
} else
|
||||||
|
wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hideWeapon() {
|
void wpnHide() {
|
||||||
if (wpnAnim != Weapon::Anim::PREPARE && wpnAnim != Weapon::Anim::UNHOLSTER && wpnAnim != Weapon::Anim::HOLSTER && !emptyHands()) {
|
if (wpnReady() && !emptyHands()) {
|
||||||
bool isRifle = wpnCurrent == Weapon::SHOTGUN;
|
if (wpnCurrent != Weapon::SHOTGUN) {
|
||||||
|
wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f);
|
||||||
if (isRifle)
|
wpnSetAnim(arms[1], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, -1.0f);
|
||||||
setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLSTER, 1.0f);
|
} else
|
||||||
else
|
wpnSetAnim(arms[0], wpnState, Weapon::Anim::HOLSTER, 0.0f, 1.0f);
|
||||||
setWeapon(wpnCurrent, wpnState, Weapon::Anim::UNHOLSTER, -1.0f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeWeapon(Weapon::Type wType) {
|
void wpnChange(Weapon::Type wType) {
|
||||||
if (wpnCurrent == wType) return;
|
if (wpnCurrent == wType) return;
|
||||||
wpnNext = wType;
|
wpnNext = wType;
|
||||||
hideWeapon();
|
wpnHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
int getWeaponAnimIndex(Weapon::Anim wAnim) {
|
int wpnGetAnimIndex(Weapon::Anim wAnim) {
|
||||||
int baseAnim = level->models[wpnCurrent == Weapon::SHOTGUN ? Weapon::SHOTGUN : Weapon::PISTOLS].animation;
|
int baseAnim = level->models[wpnCurrent == Weapon::SHOTGUN ? Weapon::SHOTGUN : Weapon::PISTOLS].animation;
|
||||||
|
|
||||||
if (wpnCurrent == Weapon::SHOTGUN) {
|
if (wpnCurrent == Weapon::SHOTGUN) {
|
||||||
@@ -388,7 +446,7 @@ struct Lara : Controller {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getWeaponSound() {
|
int wpnGetSound() {
|
||||||
switch (wpnCurrent) {
|
switch (wpnCurrent) {
|
||||||
case Weapon::PISTOLS : return TR::SND_PISTOLS_SHOT;
|
case Weapon::PISTOLS : return TR::SND_PISTOLS_SHOT;
|
||||||
case Weapon::SHOTGUN : return TR::SND_SHOTGUN_SHOT;
|
case Weapon::SHOTGUN : return TR::SND_SHOTGUN_SHOT;
|
||||||
@@ -398,26 +456,68 @@ struct Lara : Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void doShot() {
|
void wpnFire() {
|
||||||
playSound(getWeaponSound(), pos, Sound::Flags::PAN);
|
bool armShot[2] = { false, false };
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
int frameIndex = getFrameIndex(arms[i].animIndex, arms[i].animTime);
|
||||||
|
if (arms[i].anim == Weapon::Anim::FIRE) {
|
||||||
|
if (frameIndex < arms[i].lastFrame) {
|
||||||
|
if ((mask & ACTION) && (target == -1 || (target > -1 && arms[i].target > -1))) {
|
||||||
|
armShot[i] = true;
|
||||||
|
} else
|
||||||
|
wpnSetAnim(arms[i], Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, target == -1);
|
||||||
|
}
|
||||||
|
// shotgun reload sound
|
||||||
|
if (wpnCurrent == Weapon::SHOTGUN) {
|
||||||
|
if (frameIndex >= 10 && arms[i].lastFrame < 10)
|
||||||
|
playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::Flags::PAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arms[i].lastFrame = frameIndex;
|
||||||
|
|
||||||
|
if (wpnCurrent == Weapon::SHOTGUN) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (armShot[0] || armShot[1])
|
||||||
|
doShot(armShot[0], armShot[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doShot(bool rightHand, bool leftHand) {
|
||||||
int count = wpnCurrent == Weapon::SHOTGUN ? 6 : 2;
|
int count = wpnCurrent == Weapon::SHOTGUN ? 6 : 2;
|
||||||
|
|
||||||
float nearDist = 32.0f * 1024.0f;
|
float nearDist = 32.0f * 1024.0f;
|
||||||
vec3 nearPos;
|
vec3 nearPos;
|
||||||
|
bool hasShot = false;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
vec3 p = pos - vec3(0.0f, LARA_HANG_OFFSET, 0.0f);
|
Arm *arm;
|
||||||
vec3 d = getDir();
|
int armIndex;
|
||||||
vec3 r = d.cross(vec3(0, -1, 0)); // right dir
|
if (wpnCurrent == Weapon::SHOTGUN) {
|
||||||
|
if (!rightHand) continue;
|
||||||
|
arm = &arms[0];
|
||||||
|
armIndex = 0;
|
||||||
|
} else {
|
||||||
|
if (!(i ? leftHand : rightHand)) continue;
|
||||||
|
arm = &arms[i];
|
||||||
|
armIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
if (wpnCurrent != Weapon::SHOTGUN)
|
arm->shotTimer = 0.0f;
|
||||||
p += r.normal() * ((i * 2 - 1) * 48);
|
hasShot = true;
|
||||||
|
|
||||||
vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - 1.0f) * 1024.0f;
|
int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8);
|
||||||
|
|
||||||
|
vec3 p = getJoint(joint, false).getPos();
|
||||||
|
vec3 d = arm->rotAbs * vec3(0, 0, 1);
|
||||||
|
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);
|
||||||
|
if (target > -1 && checkHit(target, p, hit, hit)) {
|
||||||
|
((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;
|
hit -= d * 64.0f;
|
||||||
addSprite(level, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, SpriteController::FRAME_RANDOM);
|
addSprite(level, TR::Entity::SPARK, room, (int)hit.x, (int)hit.y, (int)hit.z, SpriteController::FRAME_RANDOM);
|
||||||
|
|
||||||
@@ -428,155 +528,307 @@ struct Lara : Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playSound(TR::SND_RICOCHET, nearPos, Sound::Flags::PAN);
|
Core::lightPos[1 + armIndex] = getJoint(armIndex == 0 ? 10 : 13, false).getPos();
|
||||||
|
Core::lightColor[1 + armIndex] = FLASH_LIGHT_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
wpnShotTime[0] = wpnShotTime[1] = 0.0f;
|
if (hasShot) {
|
||||||
|
playSound(wpnGetSound(), pos, Sound::Flags::PAN);
|
||||||
|
playSound(TR::SND_RICOCHET, nearPos, Sound::Flags::PAN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWeapon() {
|
void updateWeapon() {
|
||||||
wpnShotTime[0] += Core::deltaTime;
|
updateTargets();
|
||||||
wpnShotTime[1] += Core::deltaTime;
|
updateOverrides();
|
||||||
|
|
||||||
TR::Animation *anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
|
if (Input::down[ik1]) wpnChange(Weapon::PISTOLS);
|
||||||
|
if (Input::down[ik2]) wpnChange(Weapon::SHOTGUN);
|
||||||
|
if (Input::down[ik3]) wpnChange(Weapon::MAGNUMS);
|
||||||
|
if (Input::down[ik4]) wpnChange(Weapon::UZIS);
|
||||||
|
|
||||||
if (Input::down[ik1]) changeWeapon(Weapon::PISTOLS);
|
if (wpnNext != Weapon::EMPTY && emptyHands()) {
|
||||||
if (Input::down[ik2]) changeWeapon(Weapon::SHOTGUN);
|
wpnSet(wpnNext);
|
||||||
if (Input::down[ik3]) changeWeapon(Weapon::MAGNUMS);
|
wpnDraw();
|
||||||
if (Input::down[ik4]) changeWeapon(Weapon::UZIS);
|
|
||||||
|
|
||||||
if (wpnNext != Weapon::EMPTY && wpnState == Weapon::IS_HIDDEN) {
|
|
||||||
setWeapon(wpnNext, Weapon::IS_HIDDEN);
|
|
||||||
drawWeapon();
|
|
||||||
wpnNext = Weapon::EMPTY;
|
wpnNext = Weapon::EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply weapon state changes
|
// 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) {
|
if (mask & WEAPON) {
|
||||||
if (emptyHands())
|
if (emptyHands())
|
||||||
drawWeapon();
|
wpnDraw();
|
||||||
else
|
else
|
||||||
hideWeapon();
|
wpnHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emptyHands()) {
|
if (!emptyHands()) {
|
||||||
if (mask & ACTION) {
|
bool isRifle = wpnCurrent == Weapon::SHOTGUN;
|
||||||
if (wpnAnim == Weapon::Anim::HOLD)
|
|
||||||
setWeapon(wpnCurrent, wpnState, Weapon::Anim::AIM, 1.0f);
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (arms[i].target > -1 || ((mask & ACTION) && target == -1)) {
|
||||||
|
if (arms[i].anim == Weapon::Anim::HOLD)
|
||||||
|
wpnSetAnim(arms[i], wpnState, Weapon::Anim::AIM, 0.0f, 1.0f);
|
||||||
} else
|
} else
|
||||||
if (wpnAnim == Weapon::Anim::AIM)
|
if (arms[i].anim == Weapon::Anim::AIM)
|
||||||
wpnAnimDir = -1.0f;
|
arms[i].animDir = -1.0f;
|
||||||
|
|
||||||
|
if (isRifle) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
|
for (int i = 0; i < 2; i++){
|
||||||
float maxTime = (anim->frameEnd - anim->frameStart) / 30.0f;
|
arms[i].animTime += Core::deltaTime * arms[i].animDir;
|
||||||
|
arms[i].shotTimer += Core::deltaTime;
|
||||||
|
|
||||||
if (wpnAnim == Weapon::Anim::NONE) {
|
float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, 0.0f, 1.0f);
|
||||||
animOverrideMask &= ~(BODY_ARM_L | BODY_ARM_R);
|
Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, sqrtf(intensity));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
animOverrideMask |= BODY_ARM_L | BODY_ARM_R;
|
|
||||||
|
|
||||||
Weapon::Anim prevAnim = wpnAnim; // cache before changes
|
if (isRifle)
|
||||||
|
animateShotgun();
|
||||||
|
else
|
||||||
|
animatePistols();
|
||||||
|
|
||||||
wpnAnimTime += Core::deltaTime * wpnAnimDir;
|
wpnFire(); // make a shot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isRifle) {
|
void animatePistols() {
|
||||||
if (wpnAnimDir > 0.0f)
|
for (int i = 0; i < 2; i++) {
|
||||||
switch (wpnAnim) {
|
Arm &arm = arms[i];
|
||||||
|
|
||||||
|
if (arm.animDir >= 0.0f && arm.animTime >= arm.animMaxTime)
|
||||||
|
switch (arm.anim) {
|
||||||
|
case Weapon::Anim::PREPARE : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::UNHOLSTER, arm.animTime - arm.animMaxTime, 1.0f); break;
|
||||||
|
case Weapon::Anim::UNHOLSTER : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); break;
|
||||||
|
case Weapon::Anim::AIM :
|
||||||
|
if (mask & ACTION)
|
||||||
|
wpnSetAnim(arm, Weapon::IS_FIRING, Weapon::Anim::FIRE, arm.animTime - arm.animMaxTime, wpnCurrent == Weapon::UZIS ? 2.0f : 1.0f);
|
||||||
|
else
|
||||||
|
wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::AIM, 0.0f, -1.0f, false);
|
||||||
|
break;
|
||||||
|
default : ;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (arm.animDir < 0.0f && arm.animTime <= 0.0f)
|
||||||
|
switch (arm.anim) {
|
||||||
|
case Weapon::Anim::PREPARE : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 1.0f, false); break;
|
||||||
|
case Weapon::Anim::UNHOLSTER : wpnSetAnim(arm, Weapon::IS_HIDDEN, Weapon::Anim::PREPARE, arm.animTime, -1.0f); break;
|
||||||
|
case Weapon::Anim::AIM : wpnSetAnim(arm, Weapon::IS_ARMED, Weapon::Anim::HOLD, 0.0f, 1.0f, false); break;
|
||||||
|
default : ;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void animateShotgun() {
|
||||||
|
Arm &arm = arms[0];
|
||||||
|
if (arm.animDir >= 0.0f)
|
||||||
|
switch (arm.anim) {
|
||||||
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);
|
||||||
};
|
|
||||||
} 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, wpnCurrent == Weapon::UZIS ? 2.0f : 1.0f); break;
|
|
||||||
default : ;
|
|
||||||
};
|
|
||||||
|
|
||||||
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_HIDDEN, Weapon::Anim::PREPARE, wpnAnimDir); break;
|
|
||||||
case Weapon::Anim::AIM : setWeapon(wpnCurrent, wpnState, Weapon::Anim::HOLD, 0.0f); break;
|
|
||||||
default : ;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevAnim != wpnAnim) // check by cache
|
void updateOverrides() {
|
||||||
anim = &level->anims[getWeaponAnimIndex(wpnAnim)];
|
// update animation overrides
|
||||||
|
TR::AnimFrame *frameA, *frameB;
|
||||||
|
float t;
|
||||||
|
|
||||||
// make a shot
|
// head & chest
|
||||||
int frameIndex = int(wpnAnimTime * 30.0f / anim->frameRate) % ((anim->frameEnd - anim->frameStart) / anim->frameRate + 1);
|
animOverrideMask |= BODY_CHEST | BODY_HEAD;
|
||||||
if (wpnAnim == Weapon::Anim::FIRE) {
|
|
||||||
if (frameIndex < wpnLastFrame) {
|
getFrames(&frameA, &frameB, t, animIndex, animTime, true);
|
||||||
if (mask & ACTION) {
|
animOverrides[ 7] = lerpFrames(frameA, frameB, t, 7);
|
||||||
doShot();
|
animOverrides[14] = lerpFrames(frameA, frameB, t, 14);
|
||||||
|
|
||||||
|
|
||||||
|
if (!emptyHands()) {
|
||||||
|
// right arm
|
||||||
|
Arm *arm = &arms[0];
|
||||||
|
getFrames(&frameA, &frameB, t, arm->animIndex, arm->animTime);
|
||||||
|
animOverrides[ 8] = lerpFrames(frameA, frameB, t, 8);
|
||||||
|
animOverrides[ 9] = lerpFrames(frameA, frameB, t, 9);
|
||||||
|
animOverrides[10] = lerpFrames(frameA, frameB, t, 10);
|
||||||
|
// left arm
|
||||||
|
if (wpnCurrent != Weapon::SHOTGUN) arm = &arms[1];
|
||||||
|
getFrames(&frameA, &frameB, t, arm->animIndex, arm->animTime);
|
||||||
|
animOverrides[11] = lerpFrames(frameA, frameB, t, 11);
|
||||||
|
animOverrides[12] = lerpFrames(frameA, frameB, t, 12);
|
||||||
|
animOverrides[13] = lerpFrames(frameA, frameB, t, 13);
|
||||||
|
|
||||||
|
animOverrideMask |= BODY_ARM_R | BODY_ARM_L;
|
||||||
} else
|
} else
|
||||||
setWeapon(wpnCurrent, Weapon::IS_ARMED, Weapon::Anim::AIM, -1.0f);
|
animOverrideMask &= ~(BODY_ARM_R | BODY_ARM_L);
|
||||||
}
|
|
||||||
// shotgun reload sound
|
|
||||||
if (isRifle && frameIndex >= 10 && wpnLastFrame < 10)
|
|
||||||
playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::Flags::PAN);
|
|
||||||
}
|
|
||||||
wpnLastFrame = frameIndex;
|
|
||||||
|
|
||||||
|
lookAt(target);
|
||||||
|
|
||||||
if (wpnAnim == Weapon::Anim::NONE) {
|
if (wpnCurrent == Weapon::SHOTGUN)
|
||||||
animOverrideMask &= ~(BODY_ARM_L | BODY_ARM_R);
|
aimShotgun();
|
||||||
|
else
|
||||||
|
aimPistols();
|
||||||
|
}
|
||||||
|
|
||||||
|
void lookAt(int target) {
|
||||||
|
float speed = 8.0f * Core::deltaTime;
|
||||||
|
quat rot;
|
||||||
|
|
||||||
|
// chest
|
||||||
|
if ((stand == STAND_GROUND || stand == STAND_SLIDE) && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot))
|
||||||
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||||
|
else
|
||||||
|
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
||||||
|
animOverrides[7] = rotChest * animOverrides[7];
|
||||||
|
|
||||||
|
// head
|
||||||
|
if (aim(target, 14, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
|
||||||
|
rotHead = rotHead.slerp(rot, speed);
|
||||||
|
else
|
||||||
|
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
|
||||||
|
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() {
|
||||||
|
float speed = 8.0f * Core::deltaTime;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
Arm &arm = arms[i];
|
||||||
|
quat rot;
|
||||||
|
if (!aim(target, joint, range, rot, &arm.rotAbs)) {
|
||||||
|
arm.target = -1;
|
||||||
|
rot = quat(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float t;
|
||||||
|
if (arm.anim == Weapon::Anim::FIRE)
|
||||||
|
t = 1.0f;
|
||||||
|
else if (arm.anim == Weapon::Anim::AIM)
|
||||||
|
t = arm.animTime / arm.animMaxTime;
|
||||||
|
else
|
||||||
|
t = 0.0f;
|
||||||
|
|
||||||
|
arm.rot = arm.rot.slerp(rot, speed);
|
||||||
|
animOverrides[joint] = animOverrides[joint].slerp(arm.rot * animOverrides[joint], t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aim(int target, int joint, const vec4 &angleRange, quat &rot, quat *rotAbs = NULL) {
|
||||||
|
if (target > -1) {
|
||||||
|
TR::Entity &e = level->entities[target];
|
||||||
|
vec3 t(e.x, e.y, e.z);
|
||||||
|
|
||||||
|
mat4 m = getJoint(joint);
|
||||||
|
vec3 delta = (m.inverse() * t).normal();
|
||||||
|
|
||||||
|
float angleY = clampAngle(atan2(delta.x, delta.z));
|
||||||
|
float angleX = clampAngle(asinf(delta.y));
|
||||||
|
|
||||||
|
if (angleX > angleRange.x && angleX <= angleRange.y &&
|
||||||
|
angleY > angleRange.z && angleY <= angleRange.w) {
|
||||||
|
|
||||||
|
quat ax(vec3(1, 0, 0), -angleX);
|
||||||
|
quat ay(vec3(0, 1, 0), angleY);
|
||||||
|
|
||||||
|
rot = ay * ax;
|
||||||
|
if (rotAbs)
|
||||||
|
*rotAbs = m.getRot() * rot;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotAbs)
|
||||||
|
*rotAbs = rotYXZ(angle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTargets() {
|
||||||
|
if (emptyHands() || !wpnReady()) {
|
||||||
|
target = arms[0].target = arms[1].target = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update animation overrides
|
if (!(mask & ACTION)) {
|
||||||
float k = wpnAnimTime * 30.0f / anim->frameRate;
|
target = getTarget();
|
||||||
int fIndex = (int)k;
|
arms[0].target = arms[1].target = target;
|
||||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
} 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 fSize = sizeof(TR::AnimFrame) + getModel().mCount * sizeof(uint16) * 2;
|
int getTarget() {
|
||||||
k = k - fIndex;
|
vec3 dir = getDir().normal();
|
||||||
|
int dist = TARGET_MAX_DIST;// * TARGET_MAX_DIST;
|
||||||
|
|
||||||
int fIndexA = fIndex % fCount, fIndexB = (fIndex + 1) % fCount;
|
int index = -1;
|
||||||
TR::AnimFrame *frameA = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexA * fSize) >> 1];
|
for (int i = 0; i < level->entitiesCount; i++) {
|
||||||
TR::AnimFrame *frameB = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + fIndexB * fSize) >> 1];
|
TR::Entity &e = level->entities[i];
|
||||||
|
if (!e.flags.rendered || !e.isEnemy()) continue;
|
||||||
|
|
||||||
// left arm
|
vec3 p = vec3(e.x, e.y, e.z);
|
||||||
animOverrides[ 8] = lerpFrames(frameA, frameB, k, 8);
|
vec3 v = p - pos;
|
||||||
animOverrides[ 9] = lerpFrames(frameA, frameB, k, 9);
|
if (dir.dot(v.normal()) <= 0.5f) continue; // target is out of sigth -60..+60 degrees
|
||||||
animOverrides[10] = lerpFrames(frameA, frameB, k, 10);
|
|
||||||
// right arm
|
int d = v.length();
|
||||||
animOverrides[11] = lerpFrames(frameA, frameB, k, 11);
|
if (d < dist && checkOcclusion(pos - vec3(0, 512, 0), p, d) ) {
|
||||||
animOverrides[12] = lerpFrames(frameA, frameB, k, 12);
|
index = i;
|
||||||
animOverrides[13] = lerpFrames(frameA, frameB, k, 13);
|
dist = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -747,50 +999,14 @@ struct Lara : Controller {
|
|||||||
activateNext();
|
activateNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getViewOffset() {
|
vec3 getViewPoint() {
|
||||||
TR::Animation *anim = &level->anims[animIndex];
|
vec3 offset = chestOffset;
|
||||||
TR::Model &model = getModel();
|
if (stand != STAND_UNDERWATER)
|
||||||
|
offset.y -= 256.0f;
|
||||||
float k = animTime * 30.0f / anim->frameRate;
|
|
||||||
int fIndex = (int)k;
|
|
||||||
int fCount = (anim->frameEnd - anim->frameStart) / anim->frameRate + 1;
|
|
||||||
|
|
||||||
int fSize = sizeof(TR::AnimFrame) + model.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::Animation *nextAnim = NULL;
|
|
||||||
|
|
||||||
vec3 move(0.0f);
|
|
||||||
if (fIndexB == 0) {
|
|
||||||
move = getAnimMove();
|
|
||||||
nextAnim = &level->anims[anim->nextAnimation];
|
|
||||||
fIndexB = (anim->nextFrame - nextAnim->frameStart) / nextAnim->frameRate;
|
|
||||||
} else
|
|
||||||
nextAnim = anim;
|
|
||||||
|
|
||||||
TR::AnimFrame *frameB = (TR::AnimFrame*)&level->frameData[(nextAnim->frameOffset + fIndexB * fSize) >> 1];
|
|
||||||
|
|
||||||
float h = ((vec3)frameA->pos).lerp(move + frameB->pos, k).y;
|
|
||||||
|
|
||||||
switch (stand) {
|
|
||||||
case Controller::STAND_AIR :
|
|
||||||
case Controller::STAND_GROUND :
|
|
||||||
case Controller::STAND_SLIDE :
|
|
||||||
case Controller::STAND_HANG :
|
|
||||||
h -= 256.0f;
|
|
||||||
if (wpnState != Weapon::IS_HIDDEN)
|
if (wpnState != Weapon::IS_HIDDEN)
|
||||||
h -= 256.0f;
|
offset.y -= 256.0f;
|
||||||
break;
|
|
||||||
case Controller::STAND_UNDERWATER :
|
|
||||||
case Controller::STAND_ONWATER :
|
|
||||||
h -= 128.0f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec3(0.0f, h, 0.0f);
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Stand getStand() {
|
virtual Stand getStand() {
|
||||||
@@ -811,7 +1027,7 @@ struct Lara : Controller {
|
|||||||
return stand;
|
return stand;
|
||||||
|
|
||||||
if (getRoom().flags.water) {
|
if (getRoom().flags.water) {
|
||||||
hideWeapon();
|
wpnHide();
|
||||||
return STAND_UNDERWATER;
|
return STAND_UNDERWATER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1173,8 +1389,8 @@ struct Lara : Controller {
|
|||||||
if (!lState) {
|
if (!lState) {
|
||||||
lState = true;
|
lState = true;
|
||||||
|
|
||||||
static int snd_id = 0;
|
static int snd_id = 81;
|
||||||
//playSound(snd_id, pos, 0);
|
playSound(snd_id, pos, 0);
|
||||||
|
|
||||||
LOG("sound: %d\n", snd_id++);
|
LOG("sound: %d\n", snd_id++);
|
||||||
/*
|
/*
|
||||||
@@ -1536,25 +1752,24 @@ struct Lara : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void renderMuzzleFlash(MeshBuilder *mesh, const mat4 &matrix, const vec3 &offset, float time) {
|
void renderMuzzleFlash(MeshBuilder *mesh, const mat4 &matrix, const vec3 &offset, float time) {
|
||||||
if (time > 0.1f) return;
|
if (time > MUZZLE_FLASH_TIME) return;
|
||||||
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) {
|
||||||
Controller::render(frustum, mesh);
|
Controller::render(frustum, mesh);
|
||||||
|
chestOffset = joints[7].getPos();
|
||||||
|
|
||||||
if (wpnCurrent != Weapon::SHOTGUN) {
|
if (wpnCurrent != Weapon::SHOTGUN) {
|
||||||
renderMuzzleFlash(mesh, joints[10], vec3(-10, -50, 150), wpnShotTime[0]);
|
renderMuzzleFlash(mesh, joints[10], vec3(-10, -50, 150), arms[0].shotTimer);
|
||||||
renderMuzzleFlash(mesh, joints[13], vec3( 10, -50, 150), wpnShotTime[1]);
|
renderMuzzleFlash(mesh, joints[13], vec3( 10, -50, 150), arms[1].shotTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
57
src/level.h
57
src/level.h
@@ -172,11 +172,11 @@ struct Level {
|
|||||||
|
|
||||||
void initShaders() {
|
void initShaders() {
|
||||||
char def[255], ext[255];
|
char def[255], ext[255];
|
||||||
sprintf(def, "#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n", mesh->animTexRangesCount, mesh->animTexOffsetsCount);
|
sprintf(def, "#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n", MAX_LIGHTS, mesh->animTexRangesCount, mesh->animTexOffsetsCount);
|
||||||
shaders[shStatic] = new Shader(SHADER, def);
|
shaders[shStatic] = new Shader(SHADER, def);
|
||||||
sprintf(ext, "%s#define CAUSTICS\n", def);
|
sprintf(ext, "#define MAX_LIGHTS %d\n%s#define CAUSTICS\n", MAX_LIGHTS, def);
|
||||||
shaders[shCaustics] = new Shader(SHADER, ext);
|
shaders[shCaustics] = new Shader(SHADER, ext);
|
||||||
sprintf(ext, "%s#define SPRITE\n", def);
|
sprintf(ext, "#define MAX_LIGHTS %d\n%s#define SPRITE\n", MAX_LIGHTS, def);
|
||||||
shaders[shSprite] = new Shader(SHADER, ext);
|
shaders[shSprite] = new Shader(SHADER, ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,11 +238,12 @@ struct Level {
|
|||||||
|
|
||||||
sh->bind();
|
sh->bind();
|
||||||
sh->setParam(uColor, Core::color);
|
sh->setParam(uColor, Core::color);
|
||||||
|
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
|
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
|
sh->setParam(uAmbient, vec3(0.0f));//Core::ambient);
|
||||||
|
|
||||||
// room static meshes
|
// room static meshes
|
||||||
{
|
{
|
||||||
PROFILE_MARKER("R_MESH");
|
|
||||||
|
|
||||||
for (int i = 0; i < room.meshesCount; i++) {
|
for (int i = 0; i < room.meshesCount; i++) {
|
||||||
TR::Room::Mesh &rMesh = room.meshes[i];
|
TR::Room::Mesh &rMesh = room.meshes[i];
|
||||||
if (rMesh.flags.rendered) continue; // skip if already rendered
|
if (rMesh.flags.rendered) continue; // skip if already rendered
|
||||||
@@ -261,11 +262,17 @@ struct Level {
|
|||||||
// set light parameters
|
// set light parameters
|
||||||
getLight(offset, roomIndex);
|
getLight(offset, roomIndex);
|
||||||
|
|
||||||
|
if (rMesh.intensity >= 0) {
|
||||||
|
Core::ambient = vec3(intensity(rMesh.intensity) / 255.0f);
|
||||||
|
Core::ambient = vec3(0.0);
|
||||||
|
sh->setParam(uAmbient, Core::ambient);
|
||||||
|
}
|
||||||
|
|
||||||
// render static mesh
|
// render static mesh
|
||||||
mat4 mTemp = Core::mModel;
|
mat4 mTemp = Core::mModel;
|
||||||
Core::mModel.translate(offset);
|
Core::mModel.translate(offset);
|
||||||
Core::mModel.rotateY(rMesh.rotation);
|
Core::mModel.rotateY(rMesh.rotation);
|
||||||
Core::active.shader->setParam(uModel, Core::mModel);
|
sh->setParam(uModel, Core::mModel);
|
||||||
mesh->renderMesh(mesh->meshMap[sMesh->mesh]);
|
mesh->renderMesh(mesh->meshMap[sMesh->mesh]);
|
||||||
Core::mModel = mTemp;
|
Core::mModel = mTemp;
|
||||||
}
|
}
|
||||||
@@ -275,14 +282,13 @@ struct Level {
|
|||||||
if (!room.flags.rendered) { // skip if already rendered
|
if (!room.flags.rendered) { // skip if already rendered
|
||||||
|
|
||||||
mat4 mTemp = Core::mModel;
|
mat4 mTemp = Core::mModel;
|
||||||
{
|
|
||||||
PROFILE_MARKER("R_GEOM");
|
|
||||||
|
|
||||||
room.flags.rendered = true;
|
room.flags.rendered = true;
|
||||||
|
|
||||||
Core::lightColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
Core::lightColor[0] = vec4(0, 0, 0, 1);
|
||||||
Core::ambient = vec3(1.0f);
|
Core::ambient = vec3(0.0);
|
||||||
sh->setParam(uLightColor, Core::lightColor);
|
|
||||||
|
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
|
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
sh->setParam(uAmbient, Core::ambient);
|
sh->setParam(uAmbient, Core::ambient);
|
||||||
|
|
||||||
Core::mModel.translate(offset);
|
Core::mModel.translate(offset);
|
||||||
@@ -290,15 +296,16 @@ struct Level {
|
|||||||
// render room geometry
|
// render room geometry
|
||||||
sh->setParam(uModel, Core::mModel);
|
sh->setParam(uModel, Core::mModel);
|
||||||
mesh->renderRoomGeometry(roomIndex);
|
mesh->renderRoomGeometry(roomIndex);
|
||||||
}
|
|
||||||
|
|
||||||
// render room sprites
|
// render room sprites
|
||||||
if (mesh->hasRoomSprites(roomIndex)) {
|
if (mesh->hasRoomSprites(roomIndex)) {
|
||||||
PROFILE_MARKER("R_SPR");
|
|
||||||
sh = shaders[shSprite];
|
sh = shaders[shSprite];
|
||||||
sh->bind();
|
sh->bind();
|
||||||
sh->setParam(uModel, Core::mModel);
|
sh->setParam(uModel, Core::mModel);
|
||||||
sh->setParam(uColor, Core::color);
|
sh->setParam(uColor, Core::color);
|
||||||
|
sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
|
sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
|
sh->setParam(uAmbient, vec3(0.0f));//Core::ambient);
|
||||||
mesh->renderRoomSprites(roomIndex);
|
mesh->renderRoomSprites(roomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,17 +360,17 @@ 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 = vec3(light.x, light.y, light.z);
|
Core::lightPos[0] = vec3(light.x, light.y, light.z);
|
||||||
Core::lightColor = 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 = vec3(0);
|
Core::lightPos[0] = vec3(0);
|
||||||
Core::lightColor = vec4(0, 0, 0, 1);
|
Core::lightColor[0] = vec4(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::ambient = vec3(1.0f - level.rooms[roomIndex].ambient / 8191.0f);
|
Core::ambient = vec3(1.0f - level.rooms[roomIndex].ambient / 8191.0f);
|
||||||
Core::active.shader->setParam(uAmbient, Core::ambient);
|
Core::active.shader->setParam(uAmbient, Core::ambient);
|
||||||
Core::active.shader->setParam(uLightPos, Core::lightPos);
|
Core::active.shader->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
Core::active.shader->setParam(uLightColor, Core::lightColor);
|
Core::active.shader->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderEntity(const TR::Entity &entity) {
|
void renderEntity(const TR::Entity &entity) {
|
||||||
@@ -371,12 +378,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 +399,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 +448,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() {
|
||||||
|
108
src/mesh.h
108
src/mesh.h
@@ -106,6 +106,20 @@ struct Mesh {
|
|||||||
n.z = (int)o.z;\
|
n.z = (int)o.z;\
|
||||||
}\
|
}\
|
||||||
|
|
||||||
|
#define CHECK_ROOM_NORMAL(n) \
|
||||||
|
vec3 o(d.vertices[f.vertices[0]].vertex);\
|
||||||
|
vec3 a = o - d.vertices[f.vertices[1]].vertex;\
|
||||||
|
vec3 b = o - d.vertices[f.vertices[2]].vertex;\
|
||||||
|
o = b.cross(a).normal() * 16300.0f;\
|
||||||
|
n.x = (int)o.x;\
|
||||||
|
n.y = (int)o.y;\
|
||||||
|
n.z = (int)o.z;
|
||||||
|
|
||||||
|
|
||||||
|
uint8 intensity(int lighting) {
|
||||||
|
float a = 1.0f - (lighting >> 5) / 255.0f;
|
||||||
|
return int(255 * a * a);
|
||||||
|
}
|
||||||
|
|
||||||
struct MeshBuilder {
|
struct MeshBuilder {
|
||||||
// rooms
|
// rooms
|
||||||
@@ -252,13 +266,14 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
addQuad(indices, iCount, vCount, vStart, vertices, &t);
|
addQuad(indices, iCount, vCount, vStart, vertices, &t);
|
||||||
|
|
||||||
|
TR::Vertex n;
|
||||||
|
CHECK_ROOM_NORMAL(n);
|
||||||
|
|
||||||
for (int k = 0; k < 4; k++) {
|
for (int k = 0; k < 4; k++) {
|
||||||
TR::Room::Data::Vertex &v = d.vertices[f.vertices[k]];
|
TR::Room::Data::Vertex &v = d.vertices[f.vertices[k]];
|
||||||
uint8 a = 255 - (v.lighting >> 5);
|
|
||||||
|
|
||||||
vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 };
|
vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
vertices[vCount].color = { 255, 255, 255, intensity(v.lighting) };
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,13 +284,14 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
addTriangle(indices, iCount, vCount, vStart, vertices, &t);
|
addTriangle(indices, iCount, vCount, vStart, vertices, &t);
|
||||||
|
|
||||||
|
TR::Vertex n;
|
||||||
|
CHECK_ROOM_NORMAL(n);
|
||||||
|
|
||||||
for (int k = 0; k < 3; k++) {
|
for (int k = 0; k < 3; k++) {
|
||||||
auto &v = d.vertices[f.vertices[k]];
|
auto &v = d.vertices[f.vertices[k]];
|
||||||
uint8 a = 255 - (v.lighting >> 5);
|
|
||||||
|
|
||||||
vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 };
|
vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
vertices[vCount].color = { 255, 255, 255, intensity(v.lighting) };
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,8 +304,7 @@ struct MeshBuilder {
|
|||||||
TR::Room::Data::Vertex &v = d.vertices[f.vertex];
|
TR::Room::Data::Vertex &v = d.vertices[f.vertex];
|
||||||
TR::SpriteTexture &sprite = level.spriteTextures[f.texture];
|
TR::SpriteTexture &sprite = level.spriteTextures[f.texture];
|
||||||
|
|
||||||
uint8 intensity = 255 - (v.lighting >> 5);
|
addSprite(indices, vertices, iCount, vCount, vStart, v.vertex.x, v.vertex.y, v.vertex.z, sprite, intensity(v.lighting));
|
||||||
addSprite(indices, vertices, iCount, vCount, vStart, v.vertex.x, v.vertex.y, v.vertex.z, sprite, intensity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,21 +350,26 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
addQuad(indices, iCount, vCount, vStart, vertices, &t);
|
addQuad(indices, iCount, vCount, vStart, vertices, &t);
|
||||||
|
|
||||||
|
short4 normal;
|
||||||
|
if (!normals) {
|
||||||
|
TR::Vertex n = { 0, 0, 0 };
|
||||||
|
CHECK_NORMAL(n);
|
||||||
|
normal = { n.x, n.y, n.z, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
for (int k = 0; k < 4; k++) {
|
for (int k = 0; k < 4; k++) {
|
||||||
uint16 idx = f.vertices[k];
|
TR::Vertex &v = mVertices[f.vertices[k]];
|
||||||
TR::Vertex &v = mVertices[idx];
|
|
||||||
|
|
||||||
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
||||||
|
|
||||||
if (normals) {
|
if (normals) {
|
||||||
TR::Vertex &n = normals[idx];
|
TR::Vertex &n = normals[f.vertices[k]];
|
||||||
CHECK_NORMAL(n);
|
CHECK_NORMAL(n);
|
||||||
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vertices[vCount].color = { 255, 255, 255, 255 };
|
vertices[vCount].color = { 255, 255, 255, 0 };
|
||||||
} else {
|
} else {
|
||||||
uint8 a = 255 - (lights[idx] >> 5);
|
vertices[vCount].normal = normal;
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].color = { 255, 255, 255, intensity(lights[f.vertices[k]]) };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
|
||||||
}
|
}
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
@@ -363,19 +383,25 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
addTriangle(indices, iCount, vCount, vStart, vertices, &t);
|
addTriangle(indices, iCount, vCount, vStart, vertices, &t);
|
||||||
|
|
||||||
|
short4 normal;
|
||||||
|
if (!normals) {
|
||||||
|
TR::Vertex n = { 0, 0, 0 };
|
||||||
|
CHECK_NORMAL(n);
|
||||||
|
normal = { n.x, n.y, n.z, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
for (int k = 0; k < 3; k++) {
|
for (int k = 0; k < 3; k++) {
|
||||||
auto &v = mVertices[f.vertices[k]];
|
auto &v = mVertices[f.vertices[k]];
|
||||||
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
||||||
|
|
||||||
if (nCount > 0) {
|
if (normals) {
|
||||||
TR::Vertex &n = normals[f.vertices[k]];
|
TR::Vertex &n = normals[f.vertices[k]];
|
||||||
CHECK_NORMAL(n);
|
CHECK_NORMAL(n);
|
||||||
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vertices[vCount].color = { 255, 255, 255, 255 };
|
vertices[vCount].color = { 255, 255, 255, 0 };
|
||||||
} else {
|
} else {
|
||||||
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
|
vertices[vCount].normal = normal;
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].color = { 255, 255, 255, intensity(lights[f.vertices[k]]) };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
|
||||||
}
|
}
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
@@ -389,20 +415,26 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
addQuad(indices, iCount, vCount, vStart, vertices, &whiteTileQuad);
|
addQuad(indices, iCount, vCount, vStart, vertices, &whiteTileQuad);
|
||||||
|
|
||||||
|
short4 normal;
|
||||||
|
if (!normals) {
|
||||||
|
TR::Vertex n = { 0, 0, 0 };
|
||||||
|
CHECK_NORMAL(n);
|
||||||
|
normal = { n.x, n.y, n.z, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
for (int k = 0; k < 4; k++) {
|
for (int k = 0; k < 4; k++) {
|
||||||
auto &v = mVertices[f.vertices[k]];
|
auto &v = mVertices[f.vertices[k]];
|
||||||
|
|
||||||
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
||||||
|
|
||||||
if (nCount > 0) {
|
if (normals) {
|
||||||
TR::Vertex &n = normals[f.vertices[k]];
|
TR::Vertex &n = normals[f.vertices[k]];
|
||||||
CHECK_NORMAL(n);
|
CHECK_NORMAL(n);
|
||||||
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vertices[vCount].color = { c.r, c.g, c.b, 255 };
|
vertices[vCount].color = { c.r, c.g, c.b, 0 };
|
||||||
} else {
|
} else {
|
||||||
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
|
vertices[vCount].normal = normal;
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].color = { c.r, c.g, c.b, intensity(lights[f.vertices[k]]) };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
|
||||||
}
|
}
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
@@ -421,15 +453,21 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
vertices[vCount].coord = { v.x, v.y, v.z, 0 };
|
||||||
|
|
||||||
if (nCount > 0) {
|
short4 normal;
|
||||||
|
if (!normals) {
|
||||||
|
TR::Vertex n = { 0, 0, 0 };
|
||||||
|
CHECK_NORMAL(n);
|
||||||
|
normal = { n.x, n.y, n.z, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normals) {
|
||||||
TR::Vertex &n = normals[f.vertices[k]];
|
TR::Vertex &n = normals[f.vertices[k]];
|
||||||
CHECK_NORMAL(n);
|
CHECK_NORMAL(n);
|
||||||
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
vertices[vCount].normal = { n.x, n.y, n.z, 0 };
|
||||||
vertices[vCount].color = { c.r, c.g, c.b, 255 };
|
vertices[vCount].color = { c.r, c.g, c.b, 0 };
|
||||||
} else {
|
} else {
|
||||||
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
|
vertices[vCount].normal = normal;
|
||||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
vertices[vCount].color = { c.r, c.g, c.b, intensity(lights[f.vertices[k]]) };
|
||||||
vertices[vCount].color = { a, a, a, 255 };
|
|
||||||
}
|
}
|
||||||
vCount++;
|
vCount++;
|
||||||
}
|
}
|
||||||
@@ -450,8 +488,8 @@ struct MeshBuilder {
|
|||||||
// build shadow spot
|
// build shadow spot
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
Vertex &v = vertices[vCount + i];
|
Vertex &v = vertices[vCount + i];
|
||||||
v.normal = { 0, 0, 0, 1 };
|
v.normal = { 0, -1, 0, 0 };
|
||||||
v.color = { 255, 255, 255, 255 };
|
v.color = { 255, 255, 255, 0 };
|
||||||
v.texCoord = { 32688, 32688, 0, 0 };
|
v.texCoord = { 32688, 32688, 0, 0 };
|
||||||
|
|
||||||
float a = i * (PI / 4.0f) + (PI / 8.0f);
|
float a = i * (PI / 4.0f) + (PI / 8.0f);
|
||||||
@@ -618,7 +656,7 @@ struct MeshBuilder {
|
|||||||
|
|
||||||
quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z, 0 };
|
quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z, 0 };
|
||||||
quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 };
|
quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 };
|
||||||
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { intensity, intensity, intensity, 255 };
|
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { 255, 255, 255, intensity };
|
||||||
|
|
||||||
int tx = (sprite.tile % 4) * 256;
|
int tx = (sprite.tile % 4) * 256;
|
||||||
int ty = (sprite.tile / 4) * 256;
|
int ty = (sprite.tile / 4) * 256;
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
<head><title>OpenLara</title></head>
|
<head><title>OpenLara</title></head>
|
||||||
<body>
|
<body>
|
||||||
<span id="status">Starting...</span>
|
<span id="status">Starting...</span>
|
||||||
<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>
|
||||||
|
|
||||||
@@ -95,6 +94,11 @@
|
|||||||
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>
|
||||||
|
@@ -97,9 +97,6 @@ void main_loop() {
|
|||||||
}
|
}
|
||||||
lastTime = time;
|
lastTime = time;
|
||||||
|
|
||||||
int f;
|
|
||||||
emscripten_get_canvas_size(&Core::width, &Core::height, &f);
|
|
||||||
|
|
||||||
Core::stats.dips = 0;
|
Core::stats.dips = 0;
|
||||||
Core::stats.tris = 0;
|
Core::stats.tris = 0;
|
||||||
Game::render();
|
Game::render();
|
||||||
@@ -146,6 +143,39 @@ void freeGL() {
|
|||||||
eglTerminate(display);
|
eglTerminate(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EM_BOOL resize() {
|
||||||
|
int f;
|
||||||
|
emscripten_get_canvas_size(&Core::width, &Core::height, &f);
|
||||||
|
LOG("resize %d x %d\n", Core::width, Core::height);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EM_BOOL resizeCallback(int eventType, const EmscriptenUiEvent *e, void *userData) {
|
||||||
|
return resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
EM_BOOL fullscreenCallback(int eventType, const void *reserved, void *userData) {
|
||||||
|
return resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFullScreen() {
|
||||||
|
EmscriptenFullscreenChangeEvent status;
|
||||||
|
emscripten_get_fullscreen_status(&status);
|
||||||
|
return status.isFullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeWindowMode() {
|
||||||
|
if (!isFullScreen()) {
|
||||||
|
EmscriptenFullscreenStrategy s;
|
||||||
|
s.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
|
||||||
|
s.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
|
||||||
|
s.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
|
||||||
|
s.canvasResizedCallback = fullscreenCallback;
|
||||||
|
emscripten_request_fullscreen_strategy(NULL, 1, &s);
|
||||||
|
} else
|
||||||
|
emscripten_exit_fullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
static const int codes[] = {
|
static const int codes[] = {
|
||||||
0x25, 0x27, 0x26, 0x28, 0x20, 0x0D, 0x1B, 0x10, 0x11, 0x12,
|
0x25, 0x27, 0x26, 0x28, 0x20, 0x0D, 0x1B, 0x10, 0x11, 0x12,
|
||||||
@@ -164,21 +194,16 @@ EM_BOOL keyCallback(int eventType, const EmscriptenKeyboardEvent *e, void *userD
|
|||||||
switch(eventType) {
|
switch(eventType) {
|
||||||
case EMSCRIPTEN_EVENT_KEYDOWN:
|
case EMSCRIPTEN_EVENT_KEYDOWN:
|
||||||
case EMSCRIPTEN_EVENT_KEYUP:
|
case EMSCRIPTEN_EVENT_KEYUP:
|
||||||
|
if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && e->altKey && e->keyCode == 0x0D) { // Alt + Enter
|
||||||
|
changeWindowMode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
Input::setDown(keyToInputKey(e->keyCode), eventType == EMSCRIPTEN_EVENT_KEYDOWN);
|
Input::setDown(keyToInputKey(e->keyCode), eventType == EMSCRIPTEN_EVENT_KEYDOWN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EM_BOOL resizeCallback(int eventType, const EmscriptenUiEvent *e, void *userData) {
|
|
||||||
// Core::width = e->documentBodyClientWidth;
|
|
||||||
// Core::height = e->documentBodyClientHeight;
|
|
||||||
int f;
|
|
||||||
emscripten_get_canvas_size(&Core::width, &Core::height, &f);
|
|
||||||
LOG("resize %d x %d\n", Core::width, Core::height);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
EM_BOOL touchCallback(int eventType, const EmscriptenTouchEvent *e, void *userData) {
|
EM_BOOL touchCallback(int eventType, const EmscriptenTouchEvent *e, void *userData) {
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
/*
|
/*
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
R"====(
|
R"====(
|
||||||
#ifndef SPRITE
|
|
||||||
varying vec4 vNormal;
|
varying vec4 vNormal;
|
||||||
varying vec3 vLightVec;
|
varying vec3 vLightVec[MAX_LIGHTS];
|
||||||
varying vec3 vViewVec;
|
varying vec3 vViewVec;
|
||||||
#endif
|
|
||||||
varying vec2 vTexCoord;
|
varying vec2 vTexCoord;
|
||||||
varying vec4 vColor;
|
varying vec4 vColor;
|
||||||
|
|
||||||
@@ -11,11 +9,10 @@ varying vec4 vColor;
|
|||||||
uniform mat4 uViewProj;
|
uniform mat4 uViewProj;
|
||||||
uniform mat4 uModel;
|
uniform mat4 uModel;
|
||||||
uniform mat4 uViewInv;
|
uniform mat4 uViewInv;
|
||||||
uniform vec4 uColor;
|
uniform vec3 uLightPos[MAX_LIGHTS];
|
||||||
|
uniform vec3 uViewPos;
|
||||||
|
|
||||||
#ifndef SPRITE
|
#ifndef SPRITE
|
||||||
uniform vec3 uViewPos;
|
|
||||||
uniform vec3 uLightPos;
|
|
||||||
uniform vec2 uAnimTexRanges[MAX_RANGES];
|
uniform vec2 uAnimTexRanges[MAX_RANGES];
|
||||||
uniform vec2 uAnimTexOffsets[MAX_OFFSETS];
|
uniform vec2 uAnimTexOffsets[MAX_OFFSETS];
|
||||||
#endif
|
#endif
|
||||||
@@ -31,7 +28,7 @@ varying vec4 vColor;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 coord = uModel * vec4(aCoord.xyz, 1.0);
|
vec4 coord = uModel * vec4(aCoord.xyz, 1.0);
|
||||||
vColor = aColor * uColor;
|
vColor = aColor;
|
||||||
|
|
||||||
#ifdef CAUSTICS
|
#ifdef CAUSTICS
|
||||||
float sum = coord.x + coord.y + coord.z;
|
float sum = coord.x + coord.y + coord.z;
|
||||||
@@ -46,49 +43,59 @@ varying vec4 vColor;
|
|||||||
vec2 offset = uAnimTexOffsets[int(range.x + f)]; // texCoord offset from first frame
|
vec2 offset = uAnimTexOffsets[int(range.x + f)]; // texCoord offset from first frame
|
||||||
|
|
||||||
vTexCoord = (aTexCoord.xy + offset) * TEXCOORD_SCALE; // first frame + offset * isAnimated
|
vTexCoord = (aTexCoord.xy + offset) * TEXCOORD_SCALE; // first frame + offset * isAnimated
|
||||||
|
|
||||||
vViewVec = uViewPos - coord.xyz;
|
|
||||||
vLightVec = uLightPos - coord.xyz;
|
|
||||||
vNormal = uModel * aNormal;
|
vNormal = uModel * aNormal;
|
||||||
#else
|
#else
|
||||||
vTexCoord = aTexCoord.xy * TEXCOORD_SCALE;
|
vTexCoord = aTexCoord.xy * TEXCOORD_SCALE;
|
||||||
coord.xyz -= uViewInv[0].xyz * aTexCoord.z + uViewInv[1].xyz * aTexCoord.w;
|
coord.xyz -= uViewInv[0].xyz * aTexCoord.z + uViewInv[1].xyz * aTexCoord.w;
|
||||||
|
vNormal = vec4(uViewPos.xyz - coord.xyz, 0.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
vViewVec = uViewPos - coord.xyz;
|
||||||
|
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||||
|
vLightVec[i] = uLightPos[i] - coord.xyz;
|
||||||
|
|
||||||
gl_Position = uViewProj * coord;
|
gl_Position = uViewProj * coord;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
uniform sampler2D sDiffuse;
|
uniform sampler2D sDiffuse;
|
||||||
|
uniform vec4 uColor;
|
||||||
#ifndef SPRITE
|
|
||||||
uniform vec3 uAmbient;
|
uniform vec3 uAmbient;
|
||||||
uniform vec4 uLightColor;
|
uniform vec4 uLightColor[MAX_LIGHTS];
|
||||||
#endif
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture2D(sDiffuse, vTexCoord);
|
vec4 color = texture2D(sDiffuse, vTexCoord);
|
||||||
if (color.w < 0.6)
|
if (color.w < 0.6)
|
||||||
discard;
|
discard;
|
||||||
|
|
||||||
color *= vColor;
|
color *= uColor;
|
||||||
#ifndef SPRITE
|
color.xyz *= vColor.xyz;
|
||||||
color.xyz = pow(abs(color.xyz), vec3(2.2));
|
|
||||||
|
color.xyz = pow(abs(color.xyz), vec3(2.2)); // to linear space
|
||||||
|
|
||||||
|
// calc point lights
|
||||||
vec3 normal = normalize(vNormal.xyz);
|
vec3 normal = normalize(vNormal.xyz);
|
||||||
vec3 lightVec = normalize(vLightVec);
|
|
||||||
vec3 viewVec = normalize(vViewVec);
|
vec3 viewVec = normalize(vViewVec);
|
||||||
float lum = dot(normal, lightVec);
|
vec3 light = uAmbient;
|
||||||
float att = max(0.0, 1.0 - dot(vLightVec, vLightVec) / uLightColor.w);
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
vec3 light = uLightColor.xyz * max(vNormal.w, lum * att) + uAmbient;
|
vec3 lv = vLightVec[i];
|
||||||
// apply backlight
|
vec4 lc = uLightColor[i];
|
||||||
light *= max(vNormal.w, dot(normal, viewVec) * 0.5 + 0.5);
|
float lum = max(0.0, dot(normal, normalize(lv)));
|
||||||
color.xyz *= light;
|
float att = max(0.0, 1.0 - dot(lv, lv) / lc.w);
|
||||||
color.xyz = pow(abs(color.xyz), vec3(1.0/2.2));
|
light += lc.xyz * (lum * att);
|
||||||
#endif
|
}
|
||||||
|
// calc backlight
|
||||||
|
light *= dot(normal, viewVec) * 0.5 + 0.5;
|
||||||
|
|
||||||
// fog
|
// apply lighting
|
||||||
|
color.xyz *= vColor.w + light;
|
||||||
|
|
||||||
|
color.xyz = pow(abs(color.xyz), vec3(1.0/2.2)); // back to gamma space
|
||||||
|
|
||||||
|
// apply fog
|
||||||
float fog = clamp(1.0 / exp(gl_FragCoord.z / gl_FragCoord.w * 0.000025), 0.0, 1.0);
|
float fog = clamp(1.0 / exp(gl_FragCoord.z / gl_FragCoord.w * 0.000025), 0.0, 1.0);
|
||||||
|
color = mix(vec4(0.0, 0.0, 0.0, 1.0), color, fog);
|
||||||
|
|
||||||
gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), color, fog);
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
)===="
|
)===="
|
58
src/utils.h
58
src/utils.h
@@ -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;
|
||||||
@@ -141,6 +141,10 @@ struct vec3 {
|
|||||||
float s = sinf(angle), c = cosf(angle);
|
float s = sinf(angle), c = cosf(angle);
|
||||||
return vec3(x*c - z*s, y, x*s + z*c);
|
return vec3(x*c - z*s, y, x*s + z*c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float angle(const vec3 &v) {
|
||||||
|
return dot(v) / (length() * v.length());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vec4 {
|
struct vec4 {
|
||||||
@@ -154,11 +158,15 @@ struct vec4 {
|
|||||||
vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
|
vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
|
||||||
vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
|
vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
|
||||||
|
|
||||||
|
vec4 operator * (const vec4 &v) const { return vec4(x*v.x, y*v.y, z*v.z, w*v.w); }
|
||||||
vec4& operator *= (const vec4 &v) { x*=v.x; y*=v.y; z*=v.z; w*=v.w; return *this; }
|
vec4& operator *= (const vec4 &v) { x*=v.x; y*=v.y; z*=v.z; w*=v.w; return *this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct quat {
|
struct quat {
|
||||||
float x, y, z, w;
|
union {
|
||||||
|
struct { float x, y, z, w; };
|
||||||
|
struct { vec3 xyz; };
|
||||||
|
};
|
||||||
|
|
||||||
quat() {}
|
quat() {}
|
||||||
quat(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
|
quat(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
|
||||||
@@ -195,6 +203,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;
|
||||||
}
|
}
|
||||||
@@ -492,20 +505,17 @@ struct mat4 {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
quat rotYXZ(const vec3 &a) {
|
||||||
|
mat4 m;
|
||||||
|
m.identity();
|
||||||
|
m.rotateY(a.y);
|
||||||
|
m.rotateX(a.x);
|
||||||
|
m.rotateZ(a.z);
|
||||||
|
return m.getRot();
|
||||||
|
}
|
||||||
|
|
||||||
quat lerpAngle(const vec3 &a, const vec3 &b, float t) { // TODO: optimization
|
quat lerpAngle(const vec3 &a, const vec3 &b, float t) { // TODO: optimization
|
||||||
mat4 ma, mb;
|
return rotYXZ(a).slerp(rotYXZ(b), t).normal();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 boxNormal(int x, int z) {
|
vec3 boxNormal(int x, int z) {
|
||||||
@@ -537,6 +547,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 {
|
||||||
|
Reference in New Issue
Block a user