A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ResAnmChr.cc
1#include "ResAnmChr.hh"
2
3// Credit: kiwi515/ogws
4
5namespace Kinoko::Abstract::g3d {
6
8ChrAnmResult ResAnmChr::getAnmResult(f32 frame, size_t idx) const {
9 s32 offset = parse<s32>(m_rawData->toChrDataDic);
10 ResDic dic = ResDic(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(m_rawData) + offset));
11 const NodeData *data = reinterpret_cast<const NodeData *>(dic[idx]);
12
13 u32 flags = parse<u32>(data->flags);
14
15 ChrAnmResult result;
16 result.flags = flags &
17 (ChrAnmResult::Flag::FLAG_ANM_EXISTS | ChrAnmResult::Flag::FLAG_MTX_IDENT |
18 ChrAnmResult::Flag::FLAG_ROT_TRANS_ZERO | ChrAnmResult::Flag::FLAG_SCALE_ONE |
19 ChrAnmResult::Flag::FLAG_SCALE_UNIFORM | ChrAnmResult::Flag::FLAG_ROT_ZERO |
20 ChrAnmResult::Flag::FLAG_TRANS_ZERO | ChrAnmResult::Flag::FLAG_PATCH_SCALE |
21 ChrAnmResult::Flag::FLAG_PATCH_ROT | ChrAnmResult::Flag::FLAG_PATCH_TRANS |
22 ChrAnmResult::Flag::FLAG_SSC_APPLY | ChrAnmResult::Flag::FLAG_SSC_PARENT |
23 ChrAnmResult::Flag::FLAG_XSI_SCALING);
24
25 u32 index = (flags & NodeData::Flag::FLAG_HAS_SRT_MASK) >> 22;
26
27 ASSERT(index < s_getAnmResultTable.size());
28
29 (*s_getAnmResultTable[index])(frame, result, m_infoData, data);
30
31 return result;
32}
33
35template <typename TData, typename TDerived>
37public:
38 CResAnmChrFrmBase(const TData *pPtr) : mPtr(pPtr) {}
39
40 void operator++(int) {
41 mPtr++;
42 }
43 void operator--(int) {
44 mPtr--;
45 }
46
47 [[nodiscard]] TDerived operator+(int n) {
48 return TDerived(mPtr + n);
49 }
50
51protected:
52 const TData *mPtr;
53};
54
55template <typename T>
56class CResAnmChrFrm : public CResAnmChrFrmBase<T, CResAnmChrFrm<T>> {};
57
58template <>
59class CResAnmChrFrm<ResAnmChr::Frm48Data>
60 : public CResAnmChrFrmBase<ResAnmChr::Frm48Data, CResAnmChrFrm<ResAnmChr::Frm48Data>> {
61public:
63
64 [[nodiscard]] s16 GetFrame() const {
65 return parse<s16>(mPtr->frame);
66 }
67
68 [[nodiscard]] f32 GetFrameF32() const {
69 return static_cast<f32>(parse<s16>(mPtr->frame)) * (1.0f / 32.0f);
70 }
71
72 [[nodiscard]] f32 GetValue(const ResAnmChr::FVSData *pFVSData) const {
73 f32 x = parse<f32>(pFVSData->fvs48.scale);
74 f32 y = static_cast<f32>(parse<u16>(mPtr->value));
75 f32 z = parse<f32>(pFVSData->fvs48.offset);
76
77 return EGG::Mathf::fma(x, y, z);
78 }
79
80 [[nodiscard]] f32 GetSlope() const {
81 return static_cast<f32>(parse<s16>(mPtr->slope)) * (1.0f / 256.0f);
82 }
83};
84
85template <>
86class CResAnmChrFrm<ResAnmChr::Frm96Data>
87 : public CResAnmChrFrmBase<ResAnmChr::Frm96Data, CResAnmChrFrm<ResAnmChr::Frm96Data>> {
88public:
90
91 [[nodiscard]] f32 GetFrame() const {
92 return parse<f32>(mPtr->frame);
93 }
94
95 [[nodiscard]] f32 GetFrameF32() const {
96 return parse<f32>(mPtr->frame);
97 }
98
99 [[nodiscard]] f32 GetValue(const ResAnmChr::FVSData * /*pFVSData*/) const {
100 return parse<f32>(mPtr->value);
101 }
102
103 [[nodiscard]] f32 GetSlope() const {
104 return parse<f32>(mPtr->slope);
105 }
106};
107
109template <typename T>
111
112template <>
113class CAnmFmtTraits<ResAnmChr::FVS48Data> {
114public:
116 typedef s16 TFrame;
117
118 [[nodiscard]] static CResAnmChrFrm<TFrmData> GetKeyFrame(const ResAnmChr::FVSData *pFVSData,
119 int index) {
120 return CResAnmChrFrm<TFrmData>(&pFVSData->fvs48.frameValues[index]);
121 }
122
123 [[nodiscard]] static TFrame QuantizeFrame(f32 frame) {
124 return static_cast<TFrame>(frame * 32.0f);
125 }
126};
127
128template <>
129class CAnmFmtTraits<ResAnmChr::FVS96Data> {
130public:
132 typedef f32 TFrame;
133
134 [[nodiscard]] static CResAnmChrFrm<TFrmData> GetKeyFrame(const ResAnmChr::FVSData *pFVSData,
135 int index) {
136 return CResAnmChrFrm<TFrmData>(&pFVSData->fvs96.frameValues[index]);
137 }
138
139 [[nodiscard]] static TFrame QuantizeFrame(f32 frame) {
140 return frame;
141 }
142};
143
145template <typename T>
146[[nodiscard]] f32 CalcResultFVS(f32 frame, const ResAnmChr::NodeData *nodeData,
147 const ResAnmChr::NodeData::AnmData *anmData, bool constant) {
148 if (constant) {
149 return parse<f32>(anmData->constValue);
150 }
151
152 const ResAnmChr::AnmData *pFVSAnmData = reinterpret_cast<const ResAnmChr::AnmData *>(
153 reinterpret_cast<uintptr_t>(nodeData) + parse<s32>(anmData->toResAnmChrAnmData));
154
155 return CalcAnimationFVS<CAnmFmtTraits<T>>(frame, &pFVSAnmData->fvs);
156}
157
158[[nodiscard]] inline f32 CalcResult48(f32 frame, const ResAnmChr::NodeData *nodeData,
159 const ResAnmChr::NodeData::AnmData *anmData, bool constant) {
160 return CalcResultFVS<ResAnmChr::FVS48Data>(frame, nodeData, anmData, constant);
161}
162
163[[nodiscard]] inline f32 CalcResult96(f32 frame, const ResAnmChr::NodeData *nodeData,
164 const ResAnmChr::NodeData::AnmData *anmData, bool constant) {
165 return CalcResultFVS<ResAnmChr::FVS96Data>(frame, nodeData, anmData, constant);
166}
167
169template <typename TTraits>
170[[nodiscard]] f32 CalcAnimationFVS(f32 frame, const ResAnmChr::FVSData *pFVSData) {
171 auto first = TTraits::GetKeyFrame(pFVSData, 0);
172 auto last = TTraits::GetKeyFrame(pFVSData, parse<u16>(pFVSData->numFrameValues) - 1);
173
174 if (frame <= first.GetFrameF32()) {
175 return first.GetValue(pFVSData);
176 }
177
178 if (last.GetFrameF32() <= frame) {
179 return last.GetValue(pFVSData);
180 }
181
182 f32 frameOffset = frame - first.GetFrameF32();
183 f32 numKeyFrame = static_cast<f32>(parse<u16>(pFVSData->numFrameValues));
184
185 f32 f_estimatePos = parse<f32>(pFVSData->invKeyFrameRange) * (frameOffset * numKeyFrame);
186 u16 i_estimatePos = static_cast<u16>(f_estimatePos);
187
188 auto left = TTraits::GetKeyFrame(pFVSData, i_estimatePos);
189 auto quantized = TTraits::QuantizeFrame(frame);
190
191 if (quantized < left.GetFrame()) {
192 do {
193 left--;
194 } while (quantized < left.GetFrame());
195 } else {
196 do {
197 left++;
198 } while (left.GetFrame() <= quantized);
199
200 left--;
201 }
202
203 if (frame == left.GetFrameF32()) {
204 return left.GetValue(pFVSData);
205 }
206
207 auto right = left + 1;
208
209 f32 v0 = left.GetValue(pFVSData);
210 f32 t0 = left.GetSlope();
211 f32 v1 = right.GetValue(pFVSData);
212 f32 t1 = right.GetSlope();
213
214 f32 f0 = left.GetFrameF32();
215 f32 f1 = right.GetFrameF32();
216
217 f32 frameDelta = frame - f0;
218 f32 keyFrameDelta = f1 - f0;
219 f32 keyFrameDeltaInv = EGG::Mathf::finv(keyFrameDelta);
220 f32 t = frameDelta * keyFrameDeltaInv;
221 f32 tMinus1 = EGG::Mathf::fms(frameDelta, keyFrameDeltaInv, 1.0f);
222 f32 tanInterp = EGG::Mathf::fma(tMinus1, t0, t * t1);
223 f32 scaledCurve = t * (EGG::Mathf::fms(2.0f, t, 3.0f) * (v0 - v1));
224
225 return EGG::Mathf::fma(frameDelta * tMinus1, tanInterp, EGG::Mathf::fma(t, scaledCurve, v0));
226}
227
228const ResAnmChr::NodeData::AnmData *GetAnmScale(f32 frame, EGG::Vector3f &result,
229 const ResAnmChr::NodeData *nodeData, const ResAnmChr::NodeData::AnmData *anmData) {
230 u32 flags = parse<u32>(nodeData->flags);
231
232 switch (flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_FMT_MASK) {
233 case 0:
234 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS32_FMT:
235 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS48_FMT: {
236 result.x = CalcResult48(frame, nodeData, anmData++,
237 flags & ResAnmChr::NodeData::FLAG_SCALE_X_CONST);
238
239 if (flags & ResAnmChr::NodeData::FLAG_SCALE_UNIFORM) {
240 result.y = result.x;
241 result.z = result.x;
242 } else {
243 result.y = CalcResult48(frame, nodeData, anmData++,
244 flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_Y_CONST);
245 result.z = CalcResult48(frame, nodeData, anmData++,
246 flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_Z_CONST);
247 }
248 } break;
249 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS96_FMT: {
250 result.x = CalcResult96(frame, nodeData, anmData++,
251 flags & ResAnmChr::NodeData::FLAG_SCALE_X_CONST);
252
253 if (flags & ResAnmChr::NodeData::FLAG_SCALE_UNIFORM) {
254 result.y = result.x;
255 result.z = result.x;
256 } else {
257 result.y = CalcResult96(frame, nodeData, anmData++,
258 flags & ResAnmChr::NodeData::FLAG_SCALE_Y_CONST);
259 result.z = CalcResult96(frame, nodeData, anmData++,
260 flags & ResAnmChr::NodeData::FLAG_SCALE_Z_CONST);
261 }
262 } break;
263 default: {
264 result.setZero();
265 }
266 }
267
268 return anmData;
269}
270
271void GetAnmResult_Stub(f32 /*frame*/, ChrAnmResult & /*result*/,
272 const ResAnmChr::InfoData & /*infoData*/, const ResAnmChr::NodeData * /*nodeData*/) {}
273
274void GetAnmResult_S(f32 frame, ChrAnmResult &result, const ResAnmChr::InfoData & /*infoData*/,
275 const ResAnmChr::NodeData *nodeData) {
276 GetAnmScale(frame, result.s, nodeData, nodeData->anms);
277 result.rt = EGG::Matrix34f::ident;
278}
279
280const std::array<ResAnmChr::GetAnmResultFunc, ResAnmChr::NUM_RESULT_FUNCS>
281 ResAnmChr::s_getAnmResultTable = {{
282 &GetAnmResult_Stub,
283 &GetAnmResult_S,
284 &GetAnmResult_Stub,
285 &GetAnmResult_Stub,
286 &GetAnmResult_Stub,
287 &GetAnmResult_Stub,
288 &GetAnmResult_Stub,
289 &GetAnmResult_Stub,
290 }};
291
292} // namespace Kinoko::Abstract::g3d
Represents the CHR0 file format, which pertains to model movement animations.
Definition ResAnmChr.hh:46
static constexpr f32 fms(f32 x, f32 y, f32 z)
Fused multiply-subtract operation.
Definition Math.hh:40
static constexpr f32 finv(f32 x)
Fused Newton-Raphson operation.
Definition Math.hh:775
static constexpr f32 fma(f32 x, f32 y, f32 z)
Fused multiply-add operation.
Definition Math.hh:33
Frame values (FVS) animation data.
Definition ResAnmChr.hh:49