| 1 | #pragma once |
| 2 | |
| 3 | #include <nn/nn_BitTypes.h> |
| 4 | #include <nn/util/util_BinTypes.h> |
| 5 | #include <nn/util/util_BinaryFormat.h> |
| 6 | #include <nn/util/util_ResDic.h> |
| 7 | #include "nn/gfx/gfx_ResUserData.h" |
| 8 | #include "nn/util/MathTypes.h" |
| 9 | |
| 10 | namespace nn::g3d { |
| 11 | |
| 12 | struct ResBoneData { |
| 13 | nn::util::BinPtrToString pName; |
| 14 | nn::util::BinTPtr<nn::gfx::ResUserData> pUserDataArray; |
| 15 | nn::util::BinTPtr<nn::util::ResDic> pUserDataDic; |
| 16 | #ifndef UKING_NX_V150 |
| 17 | // This array is present in Pokémon Sword and Shield's version of nnSdk but not in BotW 1.5.0. |
| 18 | uint8_t reserved[16]; |
| 19 | #endif |
| 20 | uint16_t index; |
| 21 | uint16_t parentIndex; |
| 22 | int16_t smoothMtxIndex; |
| 23 | int16_t rigidMtxIndex; |
| 24 | uint16_t billboardIndex; |
| 25 | uint16_t userDataCount; |
| 26 | nn::Bit32 flag; |
| 27 | nn::util::Float3 scale; |
| 28 | union { |
| 29 | nn::util::Float4 quat; |
| 30 | nn::util::Float3 euler; |
| 31 | } rotate; |
| 32 | nn::util::Float3 translate; |
| 33 | }; |
| 34 | |
| 35 | class ResBone : public nn::util::AccessorBase<ResBoneData> { |
| 36 | public: |
| 37 | enum Shift { |
| 38 | Shift_Hierarchy = 4, |
| 39 | Shift_Rot = 12, |
| 40 | Shift_Billboard = 16, |
| 41 | Shift_MirroringState = 20, |
| 42 | Shift_Transform = 23, |
| 43 | }; |
| 44 | |
| 45 | enum Flag { |
| 46 | Flag_Visibility = 0x1 << 0, |
| 47 | |
| 48 | Flag_BillboardNone = 0x0 << Shift_Billboard, |
| 49 | Flag_BillboardChild = 0x1 << Shift_Billboard, |
| 50 | Flag_BillboardWorldViewVector = 0x2 << Shift_Billboard, |
| 51 | Flag_BillboardWorldViewPoint = 0x3 << Shift_Billboard, |
| 52 | Flag_BillboardScreenViewVector = 0x4 << Shift_Billboard, |
| 53 | Flag_BillboardScreenViewPoint = 0x5 << Shift_Billboard, |
| 54 | Flag_BillboardYaxisViewVector = 0x6 << Shift_Billboard, |
| 55 | Flag_BillboardYaxisViewPoint = 0x7 << Shift_Billboard, |
| 56 | Flag_BillboardMax = Flag_BillboardYaxisViewPoint, |
| 57 | |
| 58 | Flag_SegmentScaleCompensate = 0x1 << Shift_Transform, |
| 59 | Flag_ScaleUniform = 0x2 << Shift_Transform, |
| 60 | Flag_ScaleVolumeOne = 0x4 << Shift_Transform, |
| 61 | Flag_RotateZero = 0x8 << Shift_Transform, |
| 62 | Flag_TranslateZero = 0x10 << Shift_Transform, |
| 63 | Flag_ScaleOne = Flag_ScaleVolumeOne | Flag_ScaleUniform, |
| 64 | Flag_RotTransZero = Flag_RotateZero | Flag_TranslateZero, |
| 65 | Flag_Identity = Flag_ScaleOne | Flag_RotateZero | Flag_TranslateZero, |
| 66 | Flag_HiScaleUniform = Flag_ScaleUniform << Shift_Hierarchy, |
| 67 | Flag_HiScaleVolumeOne = Flag_ScaleVolumeOne << Shift_Hierarchy, |
| 68 | Flag_HiRotateZero = Flag_RotateZero << Shift_Hierarchy, |
| 69 | Flag_HiTranslateZero = Flag_TranslateZero << Shift_Hierarchy, |
| 70 | Flag_HiScaleOne = Flag_ScaleOne << Shift_Hierarchy, |
| 71 | Flag_HiRotTransZero = Flag_RotTransZero << Shift_Hierarchy, |
| 72 | Flag_HiIdentity = Flag_Identity << Shift_Hierarchy, |
| 73 | Flag_BillboardIndexNone = 0xFFFF, |
| 74 | }; |
| 75 | |
| 76 | enum Mask { |
| 77 | Mask_Rot = 0x7 << Shift_Rot, |
| 78 | Mask_Billboard = 0x7 << Shift_Billboard, |
| 79 | Mask_MirroringState = 0x7 << Shift_MirroringState, |
| 80 | Mask_Transform = Flag_SegmentScaleCompensate | Flag_Identity |
| 81 | }; |
| 82 | |
| 83 | static constexpr int InvalidBoneIndex = 0xffff; |
| 84 | |
| 85 | ResBone(const ResBone&) = delete; |
| 86 | auto operator=(const nn::g3d::ResBone&) = delete; |
| 87 | |
| 88 | int GetIndex() const { return index; } |
| 89 | const char* GetName() const { return pName.Get()->GetData(); } |
| 90 | int GetSmoothMtxIndex() const { return smoothMtxIndex; } |
| 91 | int GetRigidMtxIndex() const { return rigidMtxIndex; } |
| 92 | int GetParentIndex() const { return parentIndex; } |
| 93 | nn::Bit32 GetRotateMode() const { return flag & Mask_Rot; } |
| 94 | nn::Bit32 GetBillboardMode() const { return flag & Mask_Billboard; } |
| 95 | nn::Bit32 GetMirroringState() const { return flag & Mask_MirroringState; } |
| 96 | bool IsVisible() const { return (flag & Flag_Visibility) != 0; } |
| 97 | nn::util::Float3& GetScale() { return scale; } |
| 98 | const nn::util::Float3& GetScale() const { return scale; } |
| 99 | nn::util::Float3& GetTranslate() { return translate; } |
| 100 | const nn::util::Float3& GetTranslate() const { return translate; } |
| 101 | nn::util::Float3& GetRotateEuler() { return rotate.euler; } |
| 102 | const nn::util::Float3& GetRotateEuler() const { return rotate.euler; } |
| 103 | nn::util::Float4& GetRotateQuat() { return rotate.quat; } |
| 104 | const nn::util::Float4& GetRotateQuat() const { return rotate.quat; } |
| 105 | int GetUserDataCount() const { return userDataCount; } |
| 106 | |
| 107 | nn::gfx::ResUserData* FindUserData(const char* key) { |
| 108 | int index = FindUserDataIndex(key); |
| 109 | if (index == util::ResDic::Npos) |
| 110 | return nullptr; |
| 111 | return GetUserData(index); |
| 112 | } |
| 113 | |
| 114 | const nn::gfx::ResUserData* FindUserData(const char* key) const { |
| 115 | int index = FindUserDataIndex(key); |
| 116 | if (index == util::ResDic::Npos) |
| 117 | return nullptr; |
| 118 | return GetUserData(index); |
| 119 | } |
| 120 | |
| 121 | int FindUserDataIndex(const char* key) const { |
| 122 | const util::ResDic* dict = pUserDataDic.Get(); |
| 123 | if (dict == nullptr) |
| 124 | return util::ResDic::Npos; |
| 125 | return dict->FindIndex(key); |
| 126 | } |
| 127 | |
| 128 | const char* GetUserDataName(int index) const { |
| 129 | const util::ResDic* dict = pUserDataDic.Get(); |
| 130 | if (dict == nullptr) |
| 131 | return nullptr; |
| 132 | return dict->GetKey(index).data(); |
| 133 | } |
| 134 | |
| 135 | nn::gfx::ResUserData* GetUserData(int index) { return &pUserDataArray.Get()[index]; } |
| 136 | |
| 137 | const nn::gfx::ResUserData* GetUserData(int index) const { |
| 138 | return &pUserDataArray.Get()[index]; |
| 139 | } |
| 140 | }; |
| 141 | |
| 142 | struct ResSkeletonData { |
| 143 | nn::util::BinaryBlockHeader ; |
| 144 | nn::util::BinTPtr<nn::util::ResDic> pBoneDic; |
| 145 | nn::util::BinTPtr<nn::g3d::ResBone> pBoneArray; |
| 146 | nn::util::BinTPtr<short> pMtxToBoneTable; |
| 147 | nn::util::BinTPtr<nn::util::FloatColumnMajor4x3> pInvModelMatrixArray; |
| 148 | nn::util::BinPtr pUserPtr; |
| 149 | nn::util::BinTPtr<short> pMirroringBoneTable; |
| 150 | uint8_t reserved1[8]; |
| 151 | nn::Bit32 flag; |
| 152 | uint16_t boneCount; |
| 153 | uint16_t smoothMtxCount; |
| 154 | uint16_t rigidMtxCount; |
| 155 | uint8_t reserved2[6]; |
| 156 | }; |
| 157 | |
| 158 | class ResSkeleton : public nn::util::AccessorBase<nn::g3d::ResSkeletonData> { |
| 159 | public: |
| 160 | static constexpr uint32_t Signature = util::MakeSignature(a: 'F', b: 'S', c: 'K', d: 'L'); |
| 161 | |
| 162 | enum Shift { |
| 163 | Shift_MirroringMode = 6, |
| 164 | Shift_Scale = 8, |
| 165 | Shift_Rot = 12, |
| 166 | }; |
| 167 | |
| 168 | enum Mask { |
| 169 | Mask_MirroringMode = 0x3 << Shift_MirroringMode, |
| 170 | Mask_Scale = 0x3 << Shift_Scale, |
| 171 | Mask_Rot = ResBone::Mask_Rot, |
| 172 | Mask_TransForm = Mask_Scale | Mask_Rot |
| 173 | }; |
| 174 | |
| 175 | enum ResetGuardFlag { |
| 176 | ResetGuardFlag_None = 0, |
| 177 | ResetGuardFlag_UserPtr = 1, |
| 178 | }; |
| 179 | |
| 180 | ResSkeleton(const ResSkeleton&) = delete; |
| 181 | auto operator=(const ResSkeleton&) = delete; |
| 182 | |
| 183 | void Reset(); |
| 184 | void Reset(nn::Bit32); |
| 185 | |
| 186 | int GetSmoothMtxCount() const { return smoothMtxCount; } |
| 187 | int GetRigidMtxCount() const { return rigidMtxCount; } |
| 188 | int GetMtxCount() const { return GetSmoothMtxCount() + GetRigidMtxCount(); } |
| 189 | nn::Bit32 GetScaleMode() const { return flag & Mask_Scale; } |
| 190 | nn::Bit32 GetRotateMode() const { return flag & Mask_Rot; } |
| 191 | nn::Bit32 GetMirroringMode() const { return flag & Mask_MirroringMode; } |
| 192 | bool HasMirroringInfo() const { return pMirroringBoneTable.Get() != nullptr; } |
| 193 | int GetBranchEndIndex(int) const; |
| 194 | void SetUserPtr(void* ptr) { pUserPtr.Set(ptr); } |
| 195 | void* GetUserPtr() { return pUserPtr.Get(); } |
| 196 | const void* GetUserPtr() const { return pUserPtr.Get(); } |
| 197 | void UpdateBillboardMode(); |
| 198 | |
| 199 | int GetBoneCount() const { return boneCount; } |
| 200 | |
| 201 | nn::g3d::ResBone* FindBone(const char* name) { |
| 202 | int index = FindBoneIndex(name); |
| 203 | if (index == util::ResDic::Npos) |
| 204 | return nullptr; |
| 205 | return GetBone(index); |
| 206 | } |
| 207 | |
| 208 | const nn::g3d::ResBone* FindBone(const char* name) const { |
| 209 | int index = FindBoneIndex(name); |
| 210 | if (index == util::ResDic::Npos) |
| 211 | return nullptr; |
| 212 | return GetBone(index); |
| 213 | } |
| 214 | |
| 215 | int FindBoneIndex(const char* name) const { |
| 216 | auto* dict = pBoneDic.Get(); |
| 217 | if (dict == nullptr) |
| 218 | return util::ResDic::Npos; |
| 219 | return dict->FindIndex(key: name); |
| 220 | } |
| 221 | |
| 222 | const char* GetBoneName(int index) const { |
| 223 | auto* dict = pBoneDic.Get(); |
| 224 | if (dict == nullptr) |
| 225 | return nullptr; |
| 226 | return dict->GetKey(index).data(); |
| 227 | } |
| 228 | |
| 229 | nn::g3d::ResBone* GetBone(int index) { return &pBoneArray.Get()[index]; } |
| 230 | const nn::g3d::ResBone* GetBone(int index) const { return &pBoneArray.Get()[index]; } |
| 231 | |
| 232 | int GetMirroredBoneIndex(int index) const { return pMirroringBoneTable.Get()[index]; } |
| 233 | }; |
| 234 | |
| 235 | } // namespace nn::g3d |
| 236 | |