Skip to content

Commit

Permalink
tr2/lara/hair: add interpolation control
Browse files Browse the repository at this point in the history
This improves the overall animation of Lara's hair to respond to
interpolated frames in her main animation.

Resolves #2094.
  • Loading branch information
lahm86 committed Dec 28, 2024
1 parent 336d775 commit 2d92771
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 73 deletions.
1 change: 1 addition & 0 deletions docs/tr2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- fixed the camera getting stuck at the start of Home Sweet Home (#2129, regression from 0.7)
- fixed bubbles spawning from flares if Lara is in shallow water (#1590)
- fixed flare sound effects not always playing when Lara is in shallow water (#1590)
- improved the animation of Lara's braid (#2094)

## [0.7.1](https://github.com/LostArtefacts/TRX/compare/tr2-0.7...tr2-0.7.1) - 2024-12-17
- fixed a crash when selecting the sound option (#2057, regression from 0.6)
Expand Down
1 change: 1 addition & 0 deletions docs/tr2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ game with new enhancements and features.
- **Ice Palace**: fixed door 143's position to resolve the invisible wall in front of it, and added an extra pickup trigger beside the Gong Hammer in room 29
- **Temple of Xian**: fixed missing death tiles in room 91
- **Floating Islands**: fixed door 72's position to resolve the invisible wall in front of it
- improved the animation of Lara's braid

#### Cheats
- added a fly cheat
Expand Down
252 changes: 179 additions & 73 deletions src/tr2/game/lara/hair.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,15 @@ void Lara_Hair_Initialise(void)

void Lara_Hair_Control(const bool in_cutscene)
{
const FRAME_INFO *frame;
const FRAME_INFO *frame1;
const FRAME_INFO *frame2;
int32_t frac;
int32_t rate;
if (g_Lara.hit_direction < 0) {
frame = Item_GetBestFrame(g_LaraItem);
FRAME_INFO *frmptr[2];
frac = Item_GetFrames(g_LaraItem, frmptr, &rate);
frame1 = frmptr[0];
frame2 = frmptr[1];
} else {
LARA_ANIMATION lara_anim;
switch (g_Lara.hit_direction) {
Expand All @@ -67,92 +73,189 @@ void Lara_Hair_Control(const bool in_cutscene)

const int16_t *const frame_ptr = g_Anims[lara_anim].frame_ptr;
const int32_t interpolation = g_Anims[lara_anim].interpolation;
frame =
frame1 =
(FRAME_INFO *)&frame_ptr[g_Lara.hit_frame * (interpolation >> 8)];
frac = 0;
}

const int32_t *bone;
const int16_t *mesh;
const int16_t *mesh_rots;
SPHERE spheres[5];

Matrix_PushUnit();
g_MatrixPtr->_03 = g_LaraItem->pos.x << W2V_SHIFT;
g_MatrixPtr->_13 = g_LaraItem->pos.y << W2V_SHIFT;
g_MatrixPtr->_23 = g_LaraItem->pos.z << W2V_SHIFT;
Matrix_RotYXZ(g_LaraItem->rot.y, g_LaraItem->rot.x, g_LaraItem->rot.z);
mesh_rots = frame->mesh_rots;
Matrix_TranslateRel(frame->offset.x, frame->offset.y, frame->offset.z);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_HIPS];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[0].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[0].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[0].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[0].r = mesh[3];
Matrix_Pop();

bone = &g_AnimBones[g_Objects[O_LARA].bone_idx];
Matrix_TranslateRel(bone[25], bone[26], bone[27]);
if (g_Lara.weapon_item != NO_ITEM && g_Lara.gun_type == LGT_M16
&& (g_Items[g_Lara.weapon_item].current_anim_state == 0
|| g_Items[g_Lara.weapon_item].current_anim_state == 2
|| g_Items[g_Lara.weapon_item].current_anim_state == 4)) {
mesh_rots =
&g_Lara.right_arm.frame_base
[g_Lara.right_arm.frame_num
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
+ FBBOX_ROT];
Matrix_RotYXZsuperpack(&mesh_rots, 7);
if (frac != 0) {
const int16_t *mesh_rots1 = frame1->mesh_rots;
const int16_t *mesh_rots2 = frame2->mesh_rots;
Matrix_InitInterpolate(frac, rate);
Matrix_TranslateRel_ID(
frame1->offset.x, frame1->offset.y, frame1->offset.z,
frame2->offset.x, frame2->offset.y, frame2->offset.z);
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 0);

Matrix_Push_I();
mesh = g_Lara.mesh_ptrs[LM_HIPS];
Matrix_TranslateRel_I(mesh[0], mesh[1], mesh[2]);
Matrix_Interpolate();
spheres[0].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[0].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[0].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[0].r = mesh[3];
Matrix_Pop_I();

bone = &g_AnimBones[g_Objects[O_LARA].bone_idx];
Matrix_TranslateRel_I(bone[25], bone[26], bone[27]);
if (g_Lara.weapon_item != NO_ITEM && g_Lara.gun_type == LGT_M16
&& (g_Items[g_Lara.weapon_item].current_anim_state == 0
|| g_Items[g_Lara.weapon_item].current_anim_state == 2
|| g_Items[g_Lara.weapon_item].current_anim_state == 4)) {
mesh_rots1 =
&g_Lara.right_arm.frame_base
[g_Lara.right_arm.frame_num
* (g_Anims[g_Lara.right_arm.anim_num].interpolation
>> 8)
+ FBBOX_ROT];
mesh_rots2 = mesh_rots1;
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 7);
} else {
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 6);
}
Matrix_RotYXZ_I(
g_Lara.torso_y_rot, g_Lara.torso_x_rot, g_Lara.torso_z_rot);

Matrix_Push_I();
mesh = g_Lara.mesh_ptrs[LM_TORSO];
Matrix_TranslateRel_I(mesh[0], mesh[1], mesh[2]);
Matrix_Interpolate();
spheres[1].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[1].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[1].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[1].r = mesh[3];
Matrix_Pop_I();

Matrix_Push_I();
Matrix_TranslateRel_I(bone[29], bone[30], bone[31]);
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 0);

mesh = g_Lara.mesh_ptrs[LM_UARM_R];
Matrix_TranslateRel_I(mesh[0], mesh[1], mesh[2]);
Matrix_Interpolate();
spheres[3].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[3].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[3].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[3].r = mesh[3] * 3 / 2;
Matrix_Pop_I();

Matrix_Push_I();
Matrix_TranslateRel_I(bone[41], bone[42], bone[43]);
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 2);
mesh = g_Lara.mesh_ptrs[LM_UARM_L];
Matrix_TranslateRel_I(mesh[0], mesh[1], mesh[2]);
Matrix_Interpolate();
spheres[4].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[4].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[4].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[4].r = mesh[3] * 3 / 2;
Matrix_Pop_I();

Matrix_TranslateRel_I(bone[53], bone[54], bone[55]);
Matrix_RotYXZsuperpack_I(&mesh_rots1, &mesh_rots2, 2);
Matrix_RotYXZ_I(
g_Lara.head_y_rot, g_Lara.head_x_rot, g_Lara.head_z_rot);
Matrix_Push_I();
mesh = g_Lara.mesh_ptrs[LM_HEAD];
Matrix_TranslateRel_I(mesh[0], mesh[1], mesh[2]);
Matrix_Interpolate();
spheres[2].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[2].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[2].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[2].r = mesh[3];
Matrix_Pop_I();

Matrix_TranslateRel_I(0, -23, -55);
Matrix_Interpolate();
} else {
Matrix_RotYXZsuperpack(&mesh_rots, 6);
}
Matrix_RotYXZ(g_Lara.torso_y_rot, g_Lara.torso_x_rot, g_Lara.torso_z_rot);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_TORSO];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[1].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[1].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[1].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[1].r = mesh[3];
Matrix_Pop();
const int16_t *mesh_rots = frame1->mesh_rots;
Matrix_TranslateRel(
frame1->offset.x, frame1->offset.y, frame1->offset.z);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_HIPS];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[0].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[0].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[0].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[0].r = mesh[3];
Matrix_Pop();

Matrix_Push();
Matrix_TranslateRel(bone[29], bone[30], bone[31]);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
mesh = g_Lara.mesh_ptrs[LM_UARM_R];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[3].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[3].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[3].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[3].r = mesh[3] * 3 / 2;
Matrix_Pop();
bone = &g_AnimBones[g_Objects[O_LARA].bone_idx];
Matrix_TranslateRel(bone[25], bone[26], bone[27]);
if (g_Lara.weapon_item != NO_ITEM && g_Lara.gun_type == LGT_M16
&& (g_Items[g_Lara.weapon_item].current_anim_state == 0
|| g_Items[g_Lara.weapon_item].current_anim_state == 2
|| g_Items[g_Lara.weapon_item].current_anim_state == 4)) {
mesh_rots =
&g_Lara.right_arm.frame_base
[g_Lara.right_arm.frame_num
* (g_Anims[g_Lara.right_arm.anim_num].interpolation
>> 8)
+ FBBOX_ROT];
Matrix_RotYXZsuperpack(&mesh_rots, 7);
} else {
Matrix_RotYXZsuperpack(&mesh_rots, 6);
}
Matrix_RotYXZ(
g_Lara.torso_y_rot, g_Lara.torso_x_rot, g_Lara.torso_z_rot);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_TORSO];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[1].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[1].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[1].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[1].r = mesh[3];
Matrix_Pop();

Matrix_Push();
Matrix_TranslateRel(bone[41], bone[42], bone[43]);
Matrix_RotYXZsuperpack(&mesh_rots, 2);
mesh = g_Lara.mesh_ptrs[LM_UARM_R];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[4].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[4].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[4].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[4].r = mesh[3] * 3 / 2;
Matrix_Pop();
Matrix_Push();
Matrix_TranslateRel(bone[29], bone[30], bone[31]);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
mesh = g_Lara.mesh_ptrs[LM_UARM_R];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[3].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[3].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[3].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[3].r = mesh[3] * 3 / 2;
Matrix_Pop();

Matrix_TranslateRel(bone[53], bone[54], bone[55]);
Matrix_RotYXZsuperpack(&mesh_rots, 2);
Matrix_RotYXZ(g_Lara.head_y_rot, g_Lara.head_x_rot, g_Lara.head_z_rot);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_HEAD];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[2].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[2].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[2].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[2].r = mesh[3];
Matrix_Pop();
Matrix_Push();
Matrix_TranslateRel(bone[41], bone[42], bone[43]);
Matrix_RotYXZsuperpack(&mesh_rots, 2);
mesh = g_Lara.mesh_ptrs[LM_UARM_L];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[4].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[4].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[4].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[4].r = mesh[3] * 3 / 2;
Matrix_Pop();

Matrix_TranslateRel(bone[53], bone[54], bone[55]);
Matrix_RotYXZsuperpack(&mesh_rots, 2);
Matrix_RotYXZ(g_Lara.head_y_rot, g_Lara.head_x_rot, g_Lara.head_z_rot);
Matrix_Push();
mesh = g_Lara.mesh_ptrs[LM_HEAD];
Matrix_TranslateRel(mesh[0], mesh[1], mesh[2]);
spheres[2].x = g_MatrixPtr->_03 >> W2V_SHIFT;
spheres[2].y = g_MatrixPtr->_13 >> W2V_SHIFT;
spheres[2].z = g_MatrixPtr->_23 >> W2V_SHIFT;
spheres[2].r = mesh[3];
Matrix_Pop();

Matrix_TranslateRel(0, -23, -55);
}

Matrix_TranslateRel(0, -23, -55);
const XYZ_32 pos = {
.x = g_MatrixPtr->_03 >> W2V_SHIFT,
.y = g_MatrixPtr->_13 >> W2V_SHIFT,
Expand Down Expand Up @@ -196,9 +299,12 @@ void Lara_Hair_Control(const bool in_cutscene)
water_height = NO_HEIGHT;
} else {
water_height = Room_GetWaterHeight(
g_LaraItem->pos.x + (frame->bounds.min_x + frame->bounds.max_x) / 2,
g_LaraItem->pos.y + (frame->bounds.max_y + frame->bounds.min_y) / 2,
g_LaraItem->pos.z + (frame->bounds.max_z + frame->bounds.min_z) / 2,
g_LaraItem->pos.x
+ (frame1->bounds.min_x + frame1->bounds.max_x) / 2,
g_LaraItem->pos.y
+ (frame1->bounds.max_y + frame1->bounds.min_y) / 2,
g_LaraItem->pos.z
+ (frame1->bounds.max_z + frame1->bounds.min_z) / 2,
room_num);
}

Expand Down

0 comments on commit 2d92771

Please sign in to comment.