diff --git a/src/xrGame/WeaponMagazinedWGrenade.cpp b/src/xrGame/WeaponMagazinedWGrenade.cpp index 9e85a6467..0360f94df 100644 --- a/src/xrGame/WeaponMagazinedWGrenade.cpp +++ b/src/xrGame/WeaponMagazinedWGrenade.cpp @@ -854,7 +854,7 @@ void CWeaponMagazinedWGrenade::PlayAnimIdle() if (m_bGrenadeMode) { - if (act_state == 0) + if (act_state == 0 || psDeviceFlags2.test(rsBlendMoveAnims)) iAmmoElapsed == 0 && HudAnimationExist("anm_idle_empty_g") ? PlayHUDMotion("anm_idle_empty_g", TRUE, NULL, GetState()) : PlayHUDMotion("anm_idle_g", TRUE, NULL, GetState()); @@ -879,7 +879,7 @@ void CWeaponMagazinedWGrenade::PlayAnimIdle() } else { - if (act_state == 0) + if (act_state == 0 || psDeviceFlags2.test(rsBlendMoveAnims)) iAmmoElapsed == 0 && HudAnimationExist("anm_idle_empty_w_gl") ? PlayHUDMotion("anm_idle_empty_w_gl", TRUE, NULL, GetState()) : PlayHUDMotion("anm_idle_w_gl", TRUE, NULL, GetState()); diff --git a/src/xrGame/level_script.cpp b/src/xrGame/level_script.cpp index 62601c7ad..ae024c9fb 100644 --- a/src/xrGame/level_script.cpp +++ b/src/xrGame/level_script.cpp @@ -1546,9 +1546,9 @@ bool AllowHudMotion() return g_player_hud->allow_script_anim(); } -void PlayBlendAnm(LPCSTR name, u8 part, float speed, float power, bool bLooped, bool no_restart) +void PlayBlendAnm(LPCSTR name, u8 part, float speed, float power, bool bLooped, bool no_restart, LPCSTR pivot_bone) { - g_player_hud->PlayBlendAnm(name, part, speed, power, bLooped, no_restart); + g_player_hud->PlayBlendAnm(name, part, speed, power, bLooped, no_restart, pivot_bone); } void StopBlendAnm(LPCSTR name, bool bForce) diff --git a/src/xrGame/player_hud.cpp b/src/xrGame/player_hud.cpp index 0a0f368fe..a4a32119f 100644 --- a/src/xrGame/player_hud.cpp +++ b/src/xrGame/player_hud.cpp @@ -1224,10 +1224,36 @@ void player_hud::update(const Fmatrix& cam_trans) Fmatrix blend = anm->XFORM(); if (anm->m_part == 0 || anm->m_part == 2) - m_transform.mulB_43(blend); + { + IKinematics* K = m_model->dcast_PKinematics(); + u16 bone_id = K ? K->LL_BoneID(anm->m_pivot_bone) : u16(-1); + if (bone_id != u16(-1)) + { + Fmatrix B = K->LL_GetTransform(bone_id); + Fmatrix invB; invB.invert(B); + Fmatrix tmp; tmp.mul_43(B, blend); + tmp.mulB_43(invB); + m_transform.mulB_43(tmp); + } + else + m_transform.mulB_43(blend); + } if (anm->m_part == 1 || anm->m_part == 2) - m_transform_2.mulB_43(blend); + { + IKinematics* K = m_model_2->dcast_PKinematics(); + u16 bone_id = K ? K->LL_BoneID(anm->m_pivot_bone) : u16(-1); + if (bone_id != u16(-1)) + { + Fmatrix B = K->LL_GetTransform(bone_id); + Fmatrix invB; invB.invert(B); + Fmatrix tmp; tmp.mul_43(B, blend); + tmp.mulB_43(invB); + m_transform_2.mulB_43(tmp); + } + else + m_transform_2.mulB_43(blend); + } } bool need_blend[2]; @@ -1371,7 +1397,7 @@ void player_hud::updateMovementLayerState() } } -void player_hud::PlayBlendAnm(LPCSTR name, u8 part, float speed, float power, bool bLooped, bool no_restart) +void player_hud::PlayBlendAnm(LPCSTR name, u8 part, float speed, float power, bool bLooped, bool no_restart, LPCSTR pivot_bone) { for (script_layer* anm : m_script_layers) { @@ -1392,11 +1418,12 @@ void player_hud::PlayBlendAnm(LPCSTR name, u8 part, float speed, float power, bo anm->anm->Speed() = speed; anm->m_power = power; anm->active = true; + anm->m_pivot_bone = pivot_bone; return; } } - script_layer* anm = xr_new(name, part, speed, power, bLooped); + script_layer* anm = xr_new(name, part, speed, power, bLooped, pivot_bone); m_script_layers.push_back(anm); } diff --git a/src/xrGame/player_hud.h b/src/xrGame/player_hud.h index 7c396ed2f..1591ba7eb 100644 --- a/src/xrGame/player_hud.h +++ b/src/xrGame/player_hud.h @@ -112,12 +112,31 @@ struct movement_layer const Fmatrix& XFORM(u8 part) { - blend.set(anm->XFORM()); - blend.mul(blend_amount[part] * m_power); - blend.m[0][0] = 1.f; - blend.m[1][1] = 1.f; - blend.m[2][2] = 1.f; + auto min_jerk_interp = [](float t) { + return 10*t*t*t - 15*t*t*t*t + 6*t*t*t*t*t; + }; + float eased = min_jerk_interp(blend_amount[part]); + + // Rotation interpolation + Fquaternion qA; qA.identity(); + Fquaternion qB; qB.set(anm->XFORM()); + { + // Take m_power into account + Fvector axis; + float angle; + if (qB.get_axis_angle(axis, angle)) { + angle *= m_power; + qB.rotation(axis, angle); + } + } + Fquaternion q; q.slerp(qA, qB, eased); + + // Translation interpolation + Fvector t = anm->XFORM().c; + t.mul(m_power * eased); + + blend.mk_xform(q, t); return blend; } }; @@ -131,12 +150,14 @@ struct script_layer bool active; Fmatrix blend; u8 m_part; + shared_str m_pivot_bone; - script_layer(LPCSTR name, u8 part, float speed = 1.f, float power = 1.f, bool looped = true) + script_layer(LPCSTR name, u8 part, float speed = 1.f, float power = 1.f, bool looped = true, LPCSTR pivot_bone = nullptr) { m_name = name; m_part = part; m_power = power; + m_pivot_bone = pivot_bone; blend.identity(); anm = xr_new(); anm->Load(name); @@ -165,12 +186,31 @@ struct script_layer const Fmatrix& XFORM() { - blend.set(anm->XFORM()); - blend.mul(blend_amount * m_power); - blend.m[0][0] = 1.f; - blend.m[1][1] = 1.f; - blend.m[2][2] = 1.f; + auto min_jerk_interp = [](float t) { + return 10*t*t*t - 15*t*t*t*t + 6*t*t*t*t*t; + }; + + float eased = min_jerk_interp(blend_amount); + + // Rotation interpolation + Fquaternion qA; qA.identity(); + Fquaternion qB; qB.set(anm->XFORM()); + { + // Take m_power into account + Fvector axis; + float angle; + if (qB.get_axis_angle(axis, angle)) { + angle *= m_power; + qB.rotation(axis, angle); + } + } + Fquaternion q; q.slerp(qA, qB, eased); + + // Translation interpolation + Fvector t = anm->XFORM().c; + t.mul(m_power * eased); + blend.mk_xform(q, t); return blend; } }; @@ -321,7 +361,7 @@ class player_hud void update(const Fmatrix& trans); void updateMovementLayerState(); void StopScriptAnim(); - void PlayBlendAnm(LPCSTR name, u8 part = 0, float speed = 1.f, float power = 1.f, bool bLooped = true, bool no_restart = false); + void PlayBlendAnm(LPCSTR name, u8 part = 0, float speed = 1.f, float power = 1.f, bool bLooped = true, bool no_restart = false, LPCSTR pivot_bone = nullptr); void StopBlendAnm(LPCSTR name, bool bForce = false); void StopAllBlendAnms(bool bForce); float SetBlendAnmTime(LPCSTR name, float time);