[Gameplay] Reduce loom cost
[0ad.git] / source / graphics / SkeletonAnimDef.cpp
blob79efd71bfb531005934867e7679b9703fb5b9af2
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"
27 #include "ps/CStr.h"
28 #include "ps/CLogger.h"
29 #include "ps/FileIo.h"
31 namespace
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)
45 // Reset to 1.
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
59 // animation
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);
85 else
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?
94 CQuaternion rot;
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");
110 // check version
111 if (unpacker.GetVersion()<FILE_READ_VERSION) {
112 throw PSERROR_File_InvalidVersion();
115 // unpack the data
116 auto anim = std::make_unique<CSkeletonAnimDef>();
117 try {
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&) {
126 anim.reset();
127 throw;
130 return anim;
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));
148 // now write it
149 packer.Write(pathname);