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