#include "Runtime/Character/CFBStreamedAnimReader.hpp" #include #include #include #include #include "Runtime/Character/CSegIdList.hpp" #include "Runtime/Character/CSegStatementSet.hpp" namespace metaforce { void CFBStreamedAnimReaderTotals::Allocate(u32 chanCount) { const u32 chan2 = chanCount * 2; const u32 chan32 = chanCount * 32; const size_t sz = chan32 + chanCount + chan2 + chan32; x0_buffer = std::make_unique(sz); x4_cumulativeInts32 = reinterpret_cast(x0_buffer.get()); x8_hasTrans1 = reinterpret_cast(x4_cumulativeInts32 + chanCount * 8); xc_segIds2 = reinterpret_cast(x8_hasTrans1 + chanCount); x10_computedFloats32 = reinterpret_cast(xc_segIds2 + chanCount); } void CFBStreamedAnimReaderTotals::Initialize(const CFBStreamedCompression& source) { x1c_curKey = 0; x20_calculated = false; const u8* chans = source.GetPerChannelHeaders(); u32 boneChanCount = *reinterpret_cast(chans); chans += 4; if (source.m_pc) { for (unsigned b = 0; b < boneChanCount; ++b) { xc_segIds2[b] = *reinterpret_cast(chans); chans += 8; s32* cumulativesOut = &x4_cumulativeInts32[8 * b]; const s32* cumulativesIn = reinterpret_cast(chans); cumulativesOut[0] = 0; cumulativesOut[1] = cumulativesIn[0] >> 8; cumulativesOut[2] = cumulativesIn[1] >> 8; cumulativesOut[3] = cumulativesIn[2] >> 8; chans += 12; u32 tCount = *reinterpret_cast(chans); chans += 4; if (tCount) { x8_hasTrans1[b] = true; const s32* cumulativesIn = reinterpret_cast(chans); cumulativesOut[4] = cumulativesIn[0] >> 8; cumulativesOut[5] = cumulativesIn[1] >> 8; cumulativesOut[6] = cumulativesIn[2] >> 8; chans += 12; } else x8_hasTrans1[b] = false; } } else { for (unsigned b = 0; b < boneChanCount; ++b) { xc_segIds2[b] = *reinterpret_cast(chans); chans += 6; s32* cumulativesOut = &x4_cumulativeInts32[8 * b]; cumulativesOut[0] = 0; cumulativesOut[1] = *reinterpret_cast(chans); cumulativesOut[2] = *reinterpret_cast(chans + 3); cumulativesOut[3] = *reinterpret_cast(chans + 6); chans += 9; u16 tCount = *reinterpret_cast(chans); chans += 2; if (tCount) { x8_hasTrans1[b] = true; cumulativesOut[4] = *reinterpret_cast(chans); cumulativesOut[5] = *reinterpret_cast(chans + 3); cumulativesOut[6] = *reinterpret_cast(chans + 6); chans += 9; } else x8_hasTrans1[b] = false; } } } CFBStreamedAnimReaderTotals::CFBStreamedAnimReaderTotals(const CFBStreamedCompression& source) { const CFBStreamedCompression::Header& header = source.MainHeader(); x14_rotDiv = header.rotDiv; x18_transMult = header.translationMult; const u8* chans = source.GetPerChannelHeaders(); x24_boneChanCount = *reinterpret_cast(chans); Allocate(x24_boneChanCount); Initialize(source); } void CFBStreamedAnimReaderTotals::IncrementInto(CBitLevelLoader& loader, const CFBStreamedCompression& source, CFBStreamedAnimReaderTotals& dest) { dest.x20_calculated = false; const u8* chans = source.GetPerChannelHeaders(); u32 boneChanCount = *reinterpret_cast(chans); chans += 4; if (source.m_pc) { for (unsigned b = 0; b < boneChanCount; ++b) { chans += 8; const s32* cumulativesIn = &x4_cumulativeInts32[8 * b]; s32* cumulativesOut = &dest.x4_cumulativeInts32[8 * b]; const s32* qsIn = reinterpret_cast(chans); cumulativesOut[0] = loader.LoadBool(); cumulativesOut[1] = cumulativesIn[1] + loader.LoadSigned(qsIn[0] & 0xff); cumulativesOut[2] = cumulativesIn[2] + loader.LoadSigned(qsIn[1] & 0xff); cumulativesOut[3] = cumulativesIn[3] + loader.LoadSigned(qsIn[2] & 0xff); chans += 12; u32 tCount = *reinterpret_cast(chans); chans += 4; if (tCount) { const s32* qsIn = reinterpret_cast(chans); cumulativesOut[4] = cumulativesIn[4] + loader.LoadSigned(qsIn[0] & 0xff); cumulativesOut[5] = cumulativesIn[5] + loader.LoadSigned(qsIn[1] & 0xff); cumulativesOut[6] = cumulativesIn[6] + loader.LoadSigned(qsIn[2] & 0xff); chans += 12; } } } else { for (unsigned b = 0; b < boneChanCount; ++b) { chans += 6; const s32* cumulativesIn = &x4_cumulativeInts32[8 * b]; s32* cumulativesOut = &dest.x4_cumulativeInts32[8 * b]; cumulativesOut[0] = loader.LoadBool(); cumulativesOut[1] = cumulativesIn[1] + loader.LoadSigned(*reinterpret_cast(chans + 2)); cumulativesOut[2] = cumulativesIn[2] + loader.LoadSigned(*reinterpret_cast(chans + 5)); cumulativesOut[3] = cumulativesIn[3] + loader.LoadSigned(*reinterpret_cast(chans + 8)); chans += 9; u16 tCount = *reinterpret_cast(chans); chans += 2; if (tCount) { cumulativesOut[4] = cumulativesIn[4] + loader.LoadSigned(*reinterpret_cast(chans + 2)); cumulativesOut[5] = cumulativesIn[5] + loader.LoadSigned(*reinterpret_cast(chans + 5)); cumulativesOut[6] = cumulativesIn[5] + loader.LoadSigned(*reinterpret_cast(chans + 8)); chans += 9; } } } dest.x1c_curKey = x1c_curKey + 1; } void CFBStreamedAnimReaderTotals::CalculateDown() { for (unsigned b = 0; b < x24_boneChanCount; ++b) { const s32* cumulativesIn = &x4_cumulativeInts32[8 * b]; float* compOut = &x10_computedFloats32[8 * b]; float q = M_PIF / 2.f / float(x14_rotDiv); compOut[1] = std::sin(cumulativesIn[1] * q); compOut[2] = std::sin(cumulativesIn[2] * q); compOut[3] = std::sin(cumulativesIn[3] * q); compOut[0] = std::sqrt(std::max(1.f - (compOut[1] * compOut[1] + compOut[2] * compOut[2] + compOut[3] * compOut[3]), 0.f)); if (cumulativesIn[0]) compOut[0] = -compOut[0]; if (x8_hasTrans1[b]) { compOut[4] = cumulativesIn[4] * x18_transMult; compOut[5] = cumulativesIn[5] * x18_transMult; compOut[6] = cumulativesIn[6] * x18_transMult; } } x20_calculated = true; } CFBStreamedPairOfTotals::CFBStreamedPairOfTotals(const TSubAnimTypeToken& source) : x0_source(source), xc_rotsAndOffs(source->xc_rotsAndOffs.get()), x14_a(*source), x3c_b(*source) {} void CFBStreamedPairOfTotals::SetTime(CBitLevelLoader& loader, const CCharAnimTime& time) { /* Implementation is a bit different than original; * T evaluated pre-emptively with key indices. * CalculateDown is also called here as needed. */ const CFBStreamedCompression::Header& header = x0_source->MainHeader(); CCharAnimTime interval(header.interval); const u32* timeBitmap = x0_source->GetTimes(); CCharAnimTime priorTime(0); CCharAnimTime curTime(0); int prior = -1; int next = -1; int cur = 0; for (unsigned b = 0; b < timeBitmap[0]; ++b) { int word = b / 32; int bit = b % 32; if ((timeBitmap[word + 1] >> bit) & 1) { if (curTime <= time) { prior = cur; priorTime = curTime; } else if (curTime > time) { next = cur; if (prior == -1) { prior = cur; priorTime = curTime; x78_t = 0.f; } else { x78_t = (time - priorTime) / (curTime - priorTime); } break; } ++cur; } curTime += interval; } if (prior != -1 && u32(prior) < Prior().x1c_curKey) { Prior().Initialize(*x0_source); Next().Initialize(*x0_source); loader.Reset(); } if (prior != -1 && next == -1) { next = prior; x78_t = 1.f; } if (next != -1) { while (u32(next) > Next().x1c_curKey) { DoIncrement(loader); } } if (!Prior().IsCalculated()) { Prior().CalculateDown(); } if (!Next().IsCalculated()) { Next().CalculateDown(); } } void CFBStreamedPairOfTotals::DoIncrement(CBitLevelLoader& loader) { x10_nextSel ^= 1; Prior().IncrementInto(loader, *x0_source, Next()); } u32 CBitLevelLoader::LoadUnsigned(u8 q) { u32 byteCur = (m_bitIdx / 32) * 4; u32 bitRem = m_bitIdx % 32; /* Fill 32 bit buffer with region containing bits */ /* Make them least significant */ u32 tempBuf = *reinterpret_cast(m_data + byteCur) >> bitRem; /* If this shift underflows the value, buffer the next 32 bits */ /* And tack onto shifted buffer */ if ((bitRem + q) > 32) { u32 tempBuf2 = *reinterpret_cast(m_data + byteCur + 4); tempBuf |= (tempBuf2 << (32 - bitRem)); } /* Mask it */ u32 mask = (1 << q) - 1; tempBuf &= mask; /* Return delta value */ m_bitIdx += q; return tempBuf; } s32 CBitLevelLoader::LoadSigned(u8 q) { u32 byteCur = (m_bitIdx / 32) * 4; u32 bitRem = m_bitIdx % 32; /* Fill 32 bit buffer with region containing bits */ /* Make them least significant */ u32 tempBuf = *reinterpret_cast(m_data + byteCur) >> bitRem; /* If this shift underflows the value, buffer the next 32 bits */ /* And tack onto shifted buffer */ if ((bitRem + q) > 32) { u32 tempBuf2 = *reinterpret_cast(m_data + byteCur + 4); tempBuf |= (tempBuf2 << (32 - bitRem)); } /* Mask it */ u32 mask = (1 << q) - 1; tempBuf &= mask; /* Sign extend */ u32 sign = (tempBuf >> (q - 1)) & 0x1; if (sign) tempBuf |= ~0u << q; /* Return delta value */ m_bitIdx += q; return s32(tempBuf); } bool CBitLevelLoader::LoadBool() { u32 byteCur = (m_bitIdx / 32) * 4; u32 bitRem = m_bitIdx % 32; /* Fill 32 bit buffer with region containing bits */ /* Make them least significant */ u32 tempBuf = *reinterpret_cast(m_data + byteCur) >> bitRem; /* That's it */ m_bitIdx += 1; return tempBuf & 0x1; } CSegIdToIndexConverter::CSegIdToIndexConverter(const CFBStreamedAnimReaderTotals& totals) { std::fill(std::begin(x0_indices), std::end(x0_indices), -1); for (u32 b = 0; b < totals.x24_boneChanCount; ++b) { u16 segId = totals.xc_segIds2[b]; if (segId >= 100) continue; x0_indices[segId] = b; } } CFBStreamedAnimReader::CFBStreamedAnimReader(const TSubAnimTypeToken& source, const CCharAnimTime& time) : CAnimSourceReaderBase(std::make_unique>(source), {}) , x54_source(source) , x64_steadyStateInfo(source->IsLooping(), source->GetAnimationDuration(), source->GetRootOffset()) , x7c_totals(source) , x104_bitstreamData(source->GetBitstreamPointer()) , x108_bitLoader(x104_bitstreamData) , x114_segIdToIndex(x7c_totals.x10_nextSel ? x7c_totals.x14_a : x7c_totals.x3c_b) { x7c_totals.SetTime(x108_bitLoader, CCharAnimTime()); PostConstruct(time); } bool CFBStreamedAnimReader::HasOffset(const CSegId& seg) const { s32 idx = x114_segIdToIndex.SegIdToIndex(seg); if (idx == -1) return false; return x7c_totals.Prior().x8_hasTrans1[idx]; } zeus::CVector3f CFBStreamedAnimReader::GetOffset(const CSegId& seg) const { s32 idx = x114_segIdToIndex.SegIdToIndex(seg); if (idx == -1) return {}; const float* af = x7c_totals.Prior().GetFloats(idx); const float* bf = x7c_totals.Next().GetFloats(idx); zeus::CVector3f a(af[4], af[5], af[6]); zeus::CVector3f b(bf[4], bf[5], bf[6]); return zeus::CVector3f::lerp(a, b, x7c_totals.GetT()); } zeus::CQuaternion CFBStreamedAnimReader::GetRotation(const CSegId& seg) const { s32 idx = x114_segIdToIndex.SegIdToIndex(seg); if (idx == -1) return {}; const float* af = x7c_totals.Prior().GetFloats(idx); const float* bf = x7c_totals.Next().GetFloats(idx); zeus::CQuaternion a(af[0], af[1], af[2], af[3]); zeus::CQuaternion b(bf[0], bf[1], bf[2], bf[3]); return zeus::CQuaternion::slerp(a, b, x7c_totals.GetT()); } SAdvancementResults CFBStreamedAnimReader::VGetAdvancementResults(const CCharAnimTime& dt, const CCharAnimTime& startOff) const { SAdvancementResults res = {}; CCharAnimTime resolveTime = xc_curTime + startOff; CCharAnimTime animDur = x54_source->GetAnimationDuration(); if (resolveTime >= animDur || dt.EqualsZero()) return res; const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, resolveTime); zeus::CQuaternion priorQ = GetRotation(3); zeus::CVector3f priorV = GetOffset(3); CCharAnimTime nextTime = resolveTime + dt; if (nextTime > animDur) { nextTime = animDur; res.x0_remTime = nextTime - animDur; } const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, nextTime); zeus::CQuaternion nextQ = GetRotation(3); zeus::CVector3f nextV = GetOffset(3); res.x8_deltas.xc_rotDelta = priorQ.inverse() * nextQ; if (HasOffset(3)) res.x8_deltas.x0_posDelta = res.x8_deltas.xc_rotDelta.transform(nextV - priorV); return res; } void CFBStreamedAnimReader::VSetPhase(float ph) { xc_curTime = x64_steadyStateInfo.GetDuration() * ph; x7c_totals.SetTime(x108_bitLoader, xc_curTime); if (x54_source->HasPOIData()) { UpdatePOIStates(); if (!xc_curTime.GreaterThanZero()) { x14_passedBoolCount = 0; x18_passedIntCount = 0; x1c_passedParticleCount = 0; x20_passedSoundCount = 0; } } } SAdvancementResults CFBStreamedAnimReader::VReverseView(const CCharAnimTime& time) { return {}; } std::unique_ptr CFBStreamedAnimReader::VClone() const { return std::make_unique(x54_source, xc_curTime); } void CFBStreamedAnimReader::VGetSegStatementSet(const CSegIdList& list, CSegStatementSet& setOut) const { const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, xc_curTime); for (const CSegId& id : list.GetList()) { CAnimPerSegmentData& out = setOut[id]; out.x0_rotation = GetRotation(id); out.x1c_hasOffset = HasOffset(id); if (out.x1c_hasOffset) out.x10_offset = GetOffset(id); } } void CFBStreamedAnimReader::VGetSegStatementSet(const CSegIdList& list, CSegStatementSet& setOut, const CCharAnimTime& time) const { const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, time); for (const CSegId& id : list.GetList()) { CAnimPerSegmentData& out = setOut[id]; out.x0_rotation = GetRotation(id); out.x1c_hasOffset = HasOffset(id); if (out.x1c_hasOffset) out.x10_offset = GetOffset(id); } } SAdvancementResults CFBStreamedAnimReader::VAdvanceView(const CCharAnimTime& dt) { SAdvancementResults res = {}; CCharAnimTime animDur = x54_source->GetAnimationDuration(); if (xc_curTime == animDur) { xc_curTime = CCharAnimTime(); x7c_totals.SetTime(x108_bitLoader, xc_curTime); x14_passedBoolCount = 0; x18_passedIntCount = 0; x1c_passedParticleCount = 0; x20_passedSoundCount = 0; res.x0_remTime = dt; return res; } else if (dt.EqualsZero()) { return res; } zeus::CQuaternion priorQ = GetRotation(3); zeus::CVector3f priorV = GetOffset(3); xc_curTime += dt; CCharAnimTime overTime; if (xc_curTime > animDur) { overTime = xc_curTime - animDur; xc_curTime = animDur; } x7c_totals.SetTime(x108_bitLoader, xc_curTime); if (x54_source->HasPOIData()) UpdatePOIStates(); zeus::CQuaternion nextQ = GetRotation(3); zeus::CVector3f nextV = GetOffset(3); res.x0_remTime = overTime; res.x8_deltas.xc_rotDelta = nextQ * priorQ.inverse(); if (HasOffset(3)) res.x8_deltas.x0_posDelta = nextQ.inverse().transform(nextV - priorV); return res; } CCharAnimTime CFBStreamedAnimReader::VGetTimeRemaining() const { return x54_source->GetAnimationDuration() - xc_curTime; } CSteadyStateAnimInfo CFBStreamedAnimReader::VGetSteadyStateAnimInfo() const { return x64_steadyStateInfo; } bool CFBStreamedAnimReader::VHasOffset(const CSegId& seg) const { return HasOffset(seg); } zeus::CVector3f CFBStreamedAnimReader::VGetOffset(const CSegId& seg) const { const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, xc_curTime); return GetOffset(seg); } zeus::CVector3f CFBStreamedAnimReader::VGetOffset(const CSegId& seg, const CCharAnimTime& time) const { const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, time); return GetOffset(seg); } zeus::CQuaternion CFBStreamedAnimReader::VGetRotation(const CSegId& seg) const { const_cast(this)->x7c_totals.SetTime(const_cast(this)->x108_bitLoader, xc_curTime); return GetRotation(seg); } template class TAnimSourceInfo; } // namespace metaforce