1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Raw description of a skeleton animation
22 #include "precompiled.h"
24 #include "SkeletonAnimDef.h"
25 #include "maths/MathUtil.h"
26 #include "maths/Matrix3D.h"
28 #include "ps/CLogger.h"
29 #include "ps/FileIo.h"
33 // Start IDs at 1 to leave 0 as a special value.
34 u32 g_NextSkeletonDefUID
= 1;
37 ///////////////////////////////////////////////////////////////////////////////////////////
38 // CSkeletonAnimDef constructor
39 CSkeletonAnimDef::CSkeletonAnimDef() : m_FrameTime(0), m_NumKeys(0), m_NumFrames(0)
41 m_UID
= g_NextSkeletonDefUID
++;
42 // Log a warning if we ever overflow. Should that not result from a bug, bumping to u64 ought to suffice.
43 if (g_NextSkeletonDefUID
== 0)
46 g_NextSkeletonDefUID
++;
47 LOGWARNING("CSkeletonAnimDef unique ID overflowed to 0 - model-animation bounds may be incorrect.");
51 ///////////////////////////////////////////////////////////////////////////////////////////
52 // CSkeletonAnimDef destructor
53 CSkeletonAnimDef::~CSkeletonAnimDef()
57 ///////////////////////////////////////////////////////////////////////////////////////////
58 // BuildBoneMatrices: build matrices for all bones at the given time (in MS) in this
60 void CSkeletonAnimDef::BuildBoneMatrices(float time
, CMatrix3D
* matrices
, bool loop
) const
62 float fstartframe
= time
/m_FrameTime
;
63 size_t startframe
= (size_t)(int)(time
/m_FrameTime
);
64 float deltatime
= fstartframe
-startframe
;
66 startframe
%= m_NumFrames
;
68 size_t endframe
= startframe
+ 1;
69 endframe
%= m_NumFrames
;
71 if (!loop
&& endframe
== 0)
73 // This might be something like a death animation, and interpolating
74 // between the final frame and the initial frame is wrong, because they're
75 // totally different. So if we've looped around to endframe==0, just display
76 // the animation's final frame with no interpolation.
77 for (size_t i
= 0; i
< m_NumKeys
; i
++)
79 const Key
& key
= GetKey(startframe
, i
);
80 matrices
[i
].SetIdentity();
81 matrices
[i
].Rotate(key
.m_Rotation
);
82 matrices
[i
].Translate(key
.m_Translation
);
87 for (size_t i
= 0; i
< m_NumKeys
; i
++)
89 const Key
& startkey
= GetKey(startframe
, i
);
90 const Key
& endkey
= GetKey(endframe
, i
);
92 CVector3D trans
= Interpolate(startkey
.m_Translation
, endkey
.m_Translation
, deltatime
);
93 // TODO: is slerp the best thing to use here?
95 rot
.Slerp(startkey
.m_Rotation
, endkey
.m_Rotation
, deltatime
);
97 rot
.ToMatrix(matrices
[i
]);
98 matrices
[i
].Translate(trans
);
103 ///////////////////////////////////////////////////////////////////////////////////////////
104 // Load: try to load the anim from given file; return a new anim if successful
105 std::unique_ptr
<CSkeletonAnimDef
> CSkeletonAnimDef::Load(const VfsPath
& filename
)
107 CFileUnpacker unpacker
;
108 unpacker
.Read(filename
,"PSSA");
111 if (unpacker
.GetVersion()<FILE_READ_VERSION
) {
112 throw PSERROR_File_InvalidVersion();
116 auto anim
= std::make_unique
<CSkeletonAnimDef
>();
118 CStr name
; // unused - just here to maintain compatibility with the animation files
119 unpacker
.UnpackString(name
);
120 unpacker
.UnpackRaw(&anim
->m_FrameTime
,sizeof(anim
->m_FrameTime
));
121 anim
->m_NumKeys
= unpacker
.UnpackSize();
122 anim
->m_NumFrames
= unpacker
.UnpackSize();
123 anim
->m_Keys
.resize(anim
->m_NumKeys
*anim
->m_NumFrames
);
124 unpacker
.UnpackRaw(anim
->m_Keys
.data(), anim
->m_Keys
.size() * sizeof(decltype(anim
->m_Keys
)::value_type
));
125 } catch (PSERROR_File
&) {
133 ///////////////////////////////////////////////////////////////////////////////////////////
134 // Save: try to save anim to file
135 void CSkeletonAnimDef::Save(const VfsPath
& pathname
, const CSkeletonAnimDef
& anim
)
137 CFilePacker
packer(FILE_VERSION
, "PSSA");
139 // pack up all the data
140 packer
.PackString("");
141 packer
.PackRaw(&anim
.m_FrameTime
,sizeof(anim
.m_FrameTime
));
142 const size_t numKeys
= anim
.m_NumKeys
;
143 packer
.PackSize(numKeys
);
144 const size_t numFrames
= anim
.m_NumFrames
;
145 packer
.PackSize(numFrames
);
146 packer
.PackRaw(anim
.m_Keys
.data(), anim
.m_Keys
.size() * sizeof(decltype(anim
.m_Keys
)::value_type
));
149 packer
.Write(pathname
);