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
87template <typename T>
89
90template <>
91class CAnmFmtTraits<ResAnmChr::FVS48Data> {
92public:
94 typedef s16 TFrame;
95
96 [[nodiscard]] static CResAnmChrFrm<TFrmData> GetKeyFrame(const ResAnmChr::FVSData *pFVSData,
97 int index) {
98 return CResAnmChrFrm<TFrmData>(&pFVSData->fvs48.frameValues[index]);
99 }
100
101 [[nodiscard]] static TFrame QuantizeFrame(f32 frame) {
102 return static_cast<TFrame>(frame * 32.0f);
103 }
104};
105
107template <typename T>
108[[nodiscard]] f32 CalcResultFVS(f32 frame, const ResAnmChr::NodeData *nodeData,
109 const ResAnmChr::NodeData::AnmData *anmData, bool constant) {
110 if (constant) {
111 return parse<f32>(anmData->constValue);
112 }
113
114 const ResAnmChr::AnmData *pFVSAnmData = reinterpret_cast<const ResAnmChr::AnmData *>(
115 reinterpret_cast<uintptr_t>(nodeData) + parse<s32>(anmData->toResAnmChrAnmData));
116
117 return CalcAnimationFVS<CAnmFmtTraits<T>>(frame, &pFVSAnmData->fvs);
118}
119
120[[nodiscard]] inline f32 CalcResult48(f32 frame, const ResAnmChr::NodeData *nodeData,
121 const ResAnmChr::NodeData::AnmData *anmData, bool constant) {
122 return CalcResultFVS<ResAnmChr::FVS48Data>(frame, nodeData, anmData, constant);
123}
124
126template <typename TTraits>
127[[nodiscard]] f32 CalcAnimationFVS(f32 frame, const ResAnmChr::FVSData *pFVSData) {
128 auto first = TTraits::GetKeyFrame(pFVSData, 0);
129 auto last = TTraits::GetKeyFrame(pFVSData, parse<u16>(pFVSData->numFrameValues) - 1);
130
131 if (frame <= first.GetFrameF32()) {
132 return first.GetValue(pFVSData);
133 }
134
135 if (last.GetFrameF32() <= frame) {
136 return last.GetValue(pFVSData);
137 }
138
139 f32 frameOffset = frame - first.GetFrameF32();
140 f32 numKeyFrame = static_cast<f32>(parse<u16>(pFVSData->numFrameValues));
141
142 f32 f_estimatePos = parse<f32>(pFVSData->invKeyFrameRange) * (frameOffset * numKeyFrame);
143 u16 i_estimatePos = static_cast<u16>(f_estimatePos);
144
145 auto left = TTraits::GetKeyFrame(pFVSData, i_estimatePos);
146 auto quantized = TTraits::QuantizeFrame(frame);
147
148 if (quantized < left.GetFrame()) {
149 do {
150 left--;
151 } while (quantized < left.GetFrame());
152 } else {
153 do {
154 left++;
155 } while (left.GetFrame() <= quantized);
156
157 left--;
158 }
159
160 if (frame == left.GetFrameF32()) {
161 return left.GetValue(pFVSData);
162 }
163
164 auto right = left + 1;
165
166 f32 v0 = left.GetValue(pFVSData);
167 f32 t0 = left.GetSlope();
168 f32 v1 = right.GetValue(pFVSData);
169 f32 t1 = right.GetSlope();
170
171 f32 f0 = left.GetFrameF32();
172 f32 f1 = right.GetFrameF32();
173
174 f32 frameDelta = frame - f0;
175 f32 keyFrameDelta = f1 - f0;
176 f32 keyFrameDeltaInv = EGG::Mathf::finv(keyFrameDelta);
177 f32 t = frameDelta * keyFrameDeltaInv;
178 f32 tMinus1 = EGG::Mathf::fms(frameDelta, keyFrameDeltaInv, 1.0f);
179 f32 tanInterp = EGG::Mathf::fma(tMinus1, t0, t * t1);
180 f32 scaledCurve = t * (EGG::Mathf::fms(2.0f, t, 3.0f) * (v0 - v1));
181
182 return EGG::Mathf::fma(frameDelta * tMinus1, tanInterp, EGG::Mathf::fma(t, scaledCurve, v0));
183}
184
185const ResAnmChr::NodeData::AnmData *GetAnmScale(f32 frame, EGG::Vector3f &result,
186 const ResAnmChr::NodeData *nodeData, const ResAnmChr::NodeData::AnmData *anmData) {
187 u32 flags = parse<u32>(nodeData->flags);
188
189 switch (flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_FMT_MASK) {
190 case 0:
191 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS32_FMT:
192 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS96_FMT:
193 PANIC("GetAnmScale flags not handled!");
194 break;
195 case ResAnmChr::NodeData::Flag::FLAG_SCALE_FVS48_FMT: {
196 result.x = CalcResult48(frame, nodeData, anmData++,
197 flags & ResAnmChr::NodeData::FLAG_SCALE_X_CONST);
198
199 if (flags & ResAnmChr::NodeData::FLAG_SCALE_UNIFORM) {
200 result.y = result.x;
201 result.z = result.x;
202 } else {
203 result.y = CalcResult48(frame, nodeData, anmData++,
204 flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_Y_CONST);
205 result.z = CalcResult48(frame, nodeData, anmData++,
206 flags & ResAnmChr::NodeData::Flag::FLAG_SCALE_Z_CONST);
207 }
208 } break;
209 default: {
210 result.setZero();
211 }
212 }
213
214 return anmData;
215}
216
217void GetAnmResult_Stub(f32 /*frame*/, ChrAnmResult & /*result*/,
218 const ResAnmChr::InfoData & /*infoData*/, const ResAnmChr::NodeData * /*nodeData*/) {}
219
220void GetAnmResult_S(f32 frame, ChrAnmResult &result, const ResAnmChr::InfoData & /*infoData*/,
221 const ResAnmChr::NodeData *nodeData) {
222 GetAnmScale(frame, result.s, nodeData, nodeData->anms);
223 result.rt = EGG::Matrix34f::ident;
224}
225
226const std::array<ResAnmChr::GetAnmResultFunc, ResAnmChr::NUM_RESULT_FUNCS>
227 ResAnmChr::s_getAnmResultTable = {{
228 &GetAnmResult_Stub,
229 &GetAnmResult_S,
230 &GetAnmResult_Stub,
231 &GetAnmResult_Stub,
232 &GetAnmResult_Stub,
233 &GetAnmResult_Stub,
234 &GetAnmResult_Stub,
235 &GetAnmResult_Stub,
236 }};
237
238} // namespace g3d
239} // 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:69
static f32 finv(f32 x)
Fused Newton-Raphson operation.
Definition Math.hh:316
static f32 fms(f32 x, f32 y, f32 z)
Fused multiply-subtract operation.
Definition Math.hh:76
Frame values (FVS) animation data.
Definition ResAnmChr.hh:51
A 3D float vector.
Definition Vector.hh:87