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