| 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
| 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
| 3 | |
| 4 | #include "animstate.h" |
| 5 | |
| 6 | #include <base/math.h> |
| 7 | |
| 8 | #include <generated/client_data.h> |
| 9 | |
| 10 | static void AnimSeqEval(const CAnimSequence *pSeq, float Time, CAnimKeyframe *pFrame) |
| 11 | { |
| 12 | if(pSeq->m_NumFrames == 0) |
| 13 | { |
| 14 | pFrame->m_Time = 0; |
| 15 | pFrame->m_X = 0; |
| 16 | pFrame->m_Y = 0; |
| 17 | pFrame->m_Angle = 0; |
| 18 | } |
| 19 | else if(pSeq->m_NumFrames == 1) |
| 20 | { |
| 21 | *pFrame = pSeq->m_aFrames[0]; |
| 22 | } |
| 23 | else |
| 24 | { |
| 25 | CAnimKeyframe *pFrame1 = nullptr; |
| 26 | CAnimKeyframe *pFrame2 = nullptr; |
| 27 | float Blend = 0.0f; |
| 28 | |
| 29 | // TODO: make this smarter.. binary search |
| 30 | for(int i = 1; i < pSeq->m_NumFrames; i++) |
| 31 | { |
| 32 | if(pSeq->m_aFrames[i - 1].m_Time <= Time && pSeq->m_aFrames[i].m_Time >= Time) |
| 33 | { |
| 34 | pFrame1 = &pSeq->m_aFrames[i - 1]; |
| 35 | pFrame2 = &pSeq->m_aFrames[i]; |
| 36 | Blend = (Time - pFrame1->m_Time) / (pFrame2->m_Time - pFrame1->m_Time); |
| 37 | break; |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | if(pFrame1 != nullptr && pFrame2 != nullptr) |
| 42 | { |
| 43 | pFrame->m_Time = Time; |
| 44 | pFrame->m_X = mix(a: pFrame1->m_X, b: pFrame2->m_X, amount: Blend); |
| 45 | pFrame->m_Y = mix(a: pFrame1->m_Y, b: pFrame2->m_Y, amount: Blend); |
| 46 | pFrame->m_Angle = mix(a: pFrame1->m_Angle, b: pFrame2->m_Angle, amount: Blend); |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | static void AnimAddKeyframe(CAnimKeyframe *pSeq, const CAnimKeyframe *pAdded, float Amount) |
| 52 | { |
| 53 | // AnimSeqEval fills m_X for any case, clang-analyzer assumes going into the |
| 54 | // final else branch with pSeq->m_NumFrames < 2, which is impossible. |
| 55 | pSeq->m_X += pAdded->m_X * Amount; // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult) |
| 56 | pSeq->m_Y += pAdded->m_Y * Amount; |
| 57 | pSeq->m_Angle += pAdded->m_Angle * Amount; |
| 58 | } |
| 59 | |
| 60 | void CAnimState::AnimAdd(CAnimState *pState, const CAnimState *pAdded, float Amount) |
| 61 | { |
| 62 | AnimAddKeyframe(pSeq: &pState->m_Body, pAdded: pAdded->GetBody(), Amount); |
| 63 | AnimAddKeyframe(pSeq: &pState->m_BackFoot, pAdded: pAdded->GetBackFoot(), Amount); |
| 64 | AnimAddKeyframe(pSeq: &pState->m_FrontFoot, pAdded: pAdded->GetFrontFoot(), Amount); |
| 65 | AnimAddKeyframe(pSeq: &pState->m_Attach, pAdded: pAdded->GetAttach(), Amount); |
| 66 | } |
| 67 | |
| 68 | void CAnimState::Set(CAnimation *pAnim, float Time) |
| 69 | { |
| 70 | AnimSeqEval(pSeq: &pAnim->m_Body, Time, pFrame: &m_Body); |
| 71 | AnimSeqEval(pSeq: &pAnim->m_BackFoot, Time, pFrame: &m_BackFoot); |
| 72 | AnimSeqEval(pSeq: &pAnim->m_FrontFoot, Time, pFrame: &m_FrontFoot); |
| 73 | AnimSeqEval(pSeq: &pAnim->m_Attach, Time, pFrame: &m_Attach); |
| 74 | } |
| 75 | |
| 76 | void CAnimState::Add(CAnimation *pAnim, float Time, float Amount) |
| 77 | { |
| 78 | CAnimState Add; |
| 79 | Add.Set(pAnim, Time); |
| 80 | AnimAdd(pState: this, pAdded: &Add, Amount); |
| 81 | } |
| 82 | |
| 83 | const CAnimState *CAnimState::GetIdle() |
| 84 | { |
| 85 | static CAnimState s_State; |
| 86 | static bool s_Init = true; |
| 87 | |
| 88 | if(s_Init) |
| 89 | { |
| 90 | s_State.Set(pAnim: &g_pData->m_aAnimations[ANIM_BASE], Time: 0.0f); |
| 91 | s_State.Add(pAnim: &g_pData->m_aAnimations[ANIM_IDLE], Time: 0.0f, Amount: 1.0f); |
| 92 | s_Init = false; |
| 93 | } |
| 94 | |
| 95 | return &s_State; |
| 96 | } |
| 97 | |