#include "Runtime/Character/CPASAnimState.hpp" #include "Runtime/CRandom16.hpp" #include "Runtime/rstl.hpp" #include "Runtime/Character/CharacterCommon.hpp" #include "Runtime/Character/CPASAnimParmData.hpp" #include #include #include #include namespace metaforce { CPASAnimState::CPASAnimState(CInputStream& in) { x0_id = static_cast(in.readUint32Big()); u32 parmCount = in.readUint32Big(); u32 animCount = in.readUint32Big(); x4_parms.reserve(parmCount); x14_anims.reserve(animCount); x24_selectionCache.reserve(animCount); for (u32 i = 0; i < parmCount; ++i) x4_parms.emplace_back(in); for (u32 i = 0; i < animCount; ++i) { s32 id = in.readUint32Big(); rstl::reserved_vector parms; for (const CPASParmInfo& parm : x4_parms) { CPASAnimParm::UParmValue val = {}; switch (parm.GetParameterType()) { case CPASAnimParm::EParmType::Int32: val.m_int = in.readInt32Big(); break; case CPASAnimParm::EParmType::UInt32: val.m_uint = in.readUint32Big(); break; case CPASAnimParm::EParmType::Float: val.m_float = in.readFloatBig(); break; case CPASAnimParm::EParmType::Bool: val.m_bool = in.readBool(); break; case CPASAnimParm::EParmType::Enum: val.m_int = in.readInt32Big(); break; default: break; } parms.push_back(val); } auto search = std::lower_bound(x14_anims.begin(), x14_anims.end(), id, [](const CPASAnimInfo& item, const u32& testId) -> bool { return item.GetAnimId() < testId; }); x14_anims.emplace(search, id, std::move(parms)); } } CPASAnimState::CPASAnimState(pas::EAnimationState stateId) : x0_id(stateId) {} CPASAnimParm CPASAnimState::GetAnimParmData(s32 animId, size_t parmIdx) const { const auto search = rstl::binary_find(x14_anims.cbegin(), x14_anims.cend(), animId, [](const CPASAnimInfo& item) { return item.GetAnimId(); }); if (search == x14_anims.cend()) { return CPASAnimParm::NoParameter(); } const CPASParmInfo& parm = x4_parms.at(parmIdx); return search->GetAnimParmData(parmIdx, parm.GetParameterType()); } s32 CPASAnimState::PickRandomAnimation(CRandom16& rand) const { if (x24_selectionCache.size() == 1) return x24_selectionCache[0]; if (x24_selectionCache.size() > 1) { u32 idx = u32(floor(rand.Float())); if (idx == x24_selectionCache.size()) idx--; return x24_selectionCache[idx]; } return -1; } std::pair CPASAnimState::FindBestAnimation(const rstl::reserved_vector& parms, CRandom16& rand, s32 ignoreAnim) const { x24_selectionCache.clear(); float weight = -1.f; for (const CPASAnimInfo& info : x14_anims) { if (info.GetAnimId() == ignoreAnim) continue; float calcWeight = 1.f; if (x4_parms.size() > 0) calcWeight = 0.f; u32 unweightedCount = 0; for (size_t i = 0; i < x4_parms.size(); ++i) { CPASAnimParm::UParmValue val = info.GetAnimParmValue(i); const CPASParmInfo& parmInfo = x4_parms[i]; float parmWeight = parmInfo.GetParameterWeight(); float computedWeight = 0.f; switch (parmInfo.GetWeightFunction()) { case CPASParmInfo::EWeightFunction::AngularPercent: computedWeight = ComputeAngularPercentErrorWeight(i, parms[i], val); break; case CPASParmInfo::EWeightFunction::ExactMatch: computedWeight = ComputeExactMatchWeight(i, parms[i], val); break; case CPASParmInfo::EWeightFunction::PercentError: computedWeight = ComputePercentErrorWeight(i, parms[i], val); break; case CPASParmInfo::EWeightFunction::NoWeight: unweightedCount++; break; default: break; } calcWeight += parmWeight * computedWeight; } if (unweightedCount == x4_parms.size()) calcWeight = 1.0f; if (calcWeight > weight) { x24_selectionCache.clear(); x24_selectionCache.push_back(info.GetAnimId()); weight = calcWeight; } else if (weight == calcWeight) { x24_selectionCache.push_back(info.GetAnimId()); weight = calcWeight; } } return {weight, PickRandomAnimation(rand)}; } float CPASAnimState::ComputeExactMatchWeight(size_t, const CPASAnimParm& parm, CPASAnimParm::UParmValue parmVal) const { switch (parm.GetParameterType()) { case CPASAnimParm::EParmType::Int32: return (parm.GetInt32Value() == parmVal.m_int ? 1.f : 0.f); case CPASAnimParm::EParmType::UInt32: return (parm.GetUint32Value() == parmVal.m_uint ? 1.f : 0.f); case CPASAnimParm::EParmType::Float: return ((parmVal.m_float - parm.GetReal32Value()) < FLT_EPSILON ? 1.f : 0.f); case CPASAnimParm::EParmType::Bool: return (parm.GetBoolValue() == parmVal.m_bool ? 1.f : 0.f); case CPASAnimParm::EParmType::Enum: return (parm.GetEnumValue() == parmVal.m_int ? 1.f : 0.f); default: break; } return 0.f; } float CPASAnimState::ComputePercentErrorWeight(size_t idx, const CPASAnimParm& parm, CPASAnimParm::UParmValue parmVal) const { float range = 0.f; float val = 0.f; switch (parm.GetParameterType()) { case CPASAnimParm::EParmType::Int32: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_int - info.GetWeightMinValue().m_int; val = std::fabs(parm.GetInt32Value() - parmVal.m_int); break; } case CPASAnimParm::EParmType::UInt32: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_uint - info.GetWeightMinValue().m_uint; val = std::fabs(int(parmVal.m_uint) - int(parm.GetUint32Value())); break; } case CPASAnimParm::EParmType::Float: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_float - info.GetWeightMinValue().m_float; val = std::fabs(parm.GetReal32Value() - parmVal.m_float); break; } case CPASAnimParm::EParmType::Bool: { val = parm.GetBoolValue() == parmVal.m_bool ? 0.f : 1.f; break; } case CPASAnimParm::EParmType::Enum: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_int - info.GetWeightMinValue().m_int; val = std::fabs(parm.GetEnumValue() - parmVal.m_int); break; } default: break; } if (range > FLT_EPSILON) return 1.f - val / range; return (val < FLT_EPSILON ? 1.f : 0.f); } float CPASAnimState::ComputeAngularPercentErrorWeight(size_t idx, const CPASAnimParm& parm, CPASAnimParm::UParmValue parmVal) const { float range = 0.f; float val = 0.f; switch (parm.GetParameterType()) { case CPASAnimParm::EParmType::Int32: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_int - info.GetWeightMinValue().m_int; val = std::fabs(parmVal.m_int - parm.GetInt32Value()); break; } case CPASAnimParm::EParmType::UInt32: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_uint - info.GetWeightMinValue().m_uint; val = std::fabs(int(parmVal.m_uint) - int(parm.GetUint32Value())); break; } case CPASAnimParm::EParmType::Float: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_float - info.GetWeightMinValue().m_float; val = std::fabs(parm.GetReal32Value() - parmVal.m_float); break; } case CPASAnimParm::EParmType::Bool: { val = parm.GetBoolValue() == parmVal.m_bool ? 0.f : 1.f; break; } case CPASAnimParm::EParmType::Enum: { const CPASParmInfo& info = x4_parms[idx]; range = info.GetWeightMaxValue().m_int - info.GetWeightMinValue().m_int + 1; val = std::fabs(parm.GetEnumValue() - parmVal.m_int); break; } default: break; } if (range > FLT_EPSILON) { float mid = 0.5f * range; float offset = 1.f - ((val > mid ? range - val : val) / mid); return zeus::clamp(0.f, offset, 1.f); } return (val >= FLT_EPSILON ? 0.f : 1.f); } } // namespace metaforce