diff --git a/Runtime/Character/CAllFormatsAnimSource.cpp b/Runtime/Character/CAllFormatsAnimSource.cpp index 35ac80f3b..9ad297151 100644 --- a/Runtime/Character/CAllFormatsAnimSource.cpp +++ b/Runtime/Character/CAllFormatsAnimSource.cpp @@ -51,7 +51,16 @@ std::shared_ptr CAllFormatsAnimSource::GetNewReader(const TLockedToken& tok, const CCharAnimTime& startTime) { - return std::make_shared(tok, startTime); + switch (tok->x0_format) + { + case EAnimFormat::Uncompressed: + return std::make_shared(tok, startTime); + case EAnimFormat::BitstreamCompressed: + case EAnimFormat::BitstreamCompressed24: + return std::make_shared(tok, startTime); + default: break; + } + return {}; } CAllFormatsAnimSource::CAllFormatsAnimSource(CInputStream& in, diff --git a/Runtime/Character/CAllFormatsAnimSource.hpp b/Runtime/Character/CAllFormatsAnimSource.hpp index e4106ecbc..82a1a83c7 100644 --- a/Runtime/Character/CAllFormatsAnimSource.hpp +++ b/Runtime/Character/CAllFormatsAnimSource.hpp @@ -22,6 +22,7 @@ enum class EAnimFormat class CAnimFormatUnion { + friend class CAllFormatsAnimSource; union { EAnimFormat x0_format; diff --git a/Runtime/Character/CFBStreamedAnimReader.cpp b/Runtime/Character/CFBStreamedAnimReader.cpp index e9cceb386..57dec8114 100644 --- a/Runtime/Character/CFBStreamedAnimReader.cpp +++ b/Runtime/Character/CFBStreamedAnimReader.cpp @@ -1,4 +1,6 @@ #include "CFBStreamedAnimReader.hpp" +#include "CSegIdList.hpp" +#include "CSegStatementSet.hpp" namespace urde { @@ -95,6 +97,8 @@ 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; @@ -151,6 +155,8 @@ void CFBStreamedAnimReaderTotals::IncrementInto(CBitLevelLoader& loader, } } } + + dest.x1c_curKey = x1c_curKey + 1; } void CFBStreamedAnimReaderTotals::CalculateDown() @@ -178,13 +184,77 @@ void CFBStreamedAnimReaderTotals::CalculateDown() compOut[6] = cumulativesIn[6] * x18_transMult; } } + x20_calculated = true; } CFBStreamedPairOfTotals::CFBStreamedPairOfTotals(const TSubAnimTypeToken& source) -: x0_source(source), xc_rotsAndOffs(source->xc_rotsAndOffs.get()), x14_(*source), x3c_(*source) +: x0_source(source), xc_rotsAndOffs(source->xc_rotsAndOffs.get()), x14_a(*source), x3c_b(*source) { } +void CFBStreamedPairOfTotals::SetTime(CBitLevelLoader& loader, const CCharAnimTime& time) +{ + 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 (int b=0 ; b> 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 && prior < Prior().x1c_curKey) + { + Prior().Initialize(*x0_source); + loader.Reset(); + } + + if (next != -1) + while (next > Next().x1c_curKey) + DoIncrement(loader); + + if (!Prior().IsCalculated()) + Prior().CalculateDown(); + if (!Next().IsCalculated()) + Next().CalculateDown(); +} + +void CFBStreamedPairOfTotals::DoIncrement(CBitLevelLoader& loader) +{ + Prior().IncrementInto(loader, *x0_source, Next()); + x10_nextSel ^= 1; +} + u32 CBitLevelLoader::LoadUnsigned(u8 q) { u32 byteCur = (m_bitIdx / 32) * 4; @@ -270,65 +340,196 @@ CSegIdToIndexConverter::CSegIdToIndexConverter(const CFBStreamedAnimReaderTotals CFBStreamedAnimReader::CFBStreamedAnimReader(const TSubAnimTypeToken& source, const CCharAnimTime& time) : CAnimSourceReaderBase(std::make_unique>(source), time), x54_source(source), x64_steadyStateInfo(source->IsLooping(), source->GetAnimationDuration(), source->GetRootOffset()), - x7c_totals(source), x88_bitstreamData(source->GetBitstreamPointer()), x8c_bitLoader(x88_bitstreamData), - x114_segIdToIndex(x7c_totals.x10_ ? x7c_totals.x14_ : x7c_totals.x3c_) + 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) { PostConstruct(time); } +bool CFBStreamedAnimReader::HasOffset(const CSegId& seg) const +{ + return x7c_totals.Prior().x8_hasTrans1[x114_segIdToIndex.SegIdToIndex(seg)]; +} + +zeus::CVector3f CFBStreamedAnimReader::GetOffset(const CSegId& seg) const +{ + const float* af = x7c_totals.Prior().GetFloats(x114_segIdToIndex.SegIdToIndex(seg)); + const float* bf = x7c_totals.Next().GetFloats(x114_segIdToIndex.SegIdToIndex(seg)); + 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 +{ + const float* af = x7c_totals.Prior().GetFloats(x114_segIdToIndex.SegIdToIndex(seg)); + const float* bf = x7c_totals.Next().GetFloats(x114_segIdToIndex.SegIdToIndex(seg)); + 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) +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::shared_ptr CFBStreamedAnimReader::VClone() const { + return std::make_shared(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& a) +SAdvancementResults CFBStreamedAnimReader::VAdvanceView(const CCharAnimTime& dt) { + SAdvancementResults res = {}; + + CCharAnimTime animDur = x54_source->GetAnimationDuration(); + if (xc_curTime >= animDur || dt.EqualsZero()) + { + xc_curTime = CCharAnimTime(0); + x7c_totals.SetTime(x108_bitLoader, xc_curTime); + return res; + } + + zeus::CQuaternion priorQ = GetRotation(3); + zeus::CVector3f priorV = GetOffset(3); + + CCharAnimTime nextTime = xc_curTime + dt; + if (nextTime > animDur) + { + nextTime = animDur; + res.x0_remTime = nextTime - animDur; + } + + x7c_totals.SetTime(x108_bitLoader, nextTime); + UpdatePOIStates(); + 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; } 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; diff --git a/Runtime/Character/CFBStreamedAnimReader.hpp b/Runtime/Character/CFBStreamedAnimReader.hpp index 47101f843..22cfae744 100644 --- a/Runtime/Character/CFBStreamedAnimReader.hpp +++ b/Runtime/Character/CFBStreamedAnimReader.hpp @@ -13,7 +13,7 @@ class TAnimSourceInfo : public IAnimSourceInfo { TSubAnimTypeToken x4_token; public: - TAnimSourceInfo(const TSubAnimTypeToken& token); + TAnimSourceInfo(const TSubAnimTypeToken& token) : x4_token(token) {} bool HasPOIData() const { return x4_token->HasPOIData(); } const std::vector& GetBoolPOIStream() const { return x4_token->GetBoolPOIStream(); } const std::vector& GetInt32POIStream() const { return x4_token->GetInt32POIStream(); } @@ -25,6 +25,8 @@ public: class CFBStreamedAnimReaderTotals { friend class CSegIdToIndexConverter; + friend class CFBStreamedPairOfTotals; + friend class CFBStreamedAnimReader; std::unique_ptr x0_buffer; s32* x4_cumulativeInts32; /* Used to be 16 per channel */ u8* x8_hasTrans1; @@ -33,15 +35,17 @@ class CFBStreamedAnimReaderTotals u32 x14_rotDiv; float x18_transMult; u32 x1c_curKey = 0; - bool x20_ = false; + bool x20_calculated = false; u32 x24_boneChanCount; void Allocate(u32 chanCount); - void Initialize(const CFBStreamedCompression& source); public: CFBStreamedAnimReaderTotals(const CFBStreamedCompression& source); + void Initialize(const CFBStreamedCompression& source); void IncrementInto(CBitLevelLoader& loader, const CFBStreamedCompression& source, CFBStreamedAnimReaderTotals& dest); void CalculateDown(); + bool IsCalculated() const { return x20_calculated; } + const float* GetFloats(int chanIdx) const { return &x10_computedFloats32[chanIdx*8]; } }; class CFBStreamedPairOfTotals @@ -50,11 +54,19 @@ class CFBStreamedPairOfTotals TSubAnimTypeToken x0_source; u32* xc_rotsAndOffs; - bool x10_ = true; - CFBStreamedAnimReaderTotals x14_; - CFBStreamedAnimReaderTotals x3c_; + bool x10_nextSel = true; + CFBStreamedAnimReaderTotals x14_a; + CFBStreamedAnimReaderTotals x3c_b; + float x78_t = 0.f; public: CFBStreamedPairOfTotals(const TSubAnimTypeToken& source); + void SetTime(CBitLevelLoader& loader, const CCharAnimTime& time); + void DoIncrement(CBitLevelLoader& loader); + float GetT() const { return x78_t; } + CFBStreamedAnimReaderTotals& Next() { return x10_nextSel ? x3c_b : x14_a; } + CFBStreamedAnimReaderTotals& Prior() { return x10_nextSel ? x14_a : x3c_b; } + const CFBStreamedAnimReaderTotals& Next() const { return x10_nextSel ? x3c_b : x14_a; } + const CFBStreamedAnimReaderTotals& Prior() const { return x10_nextSel ? x14_a : x3c_b; } }; class CBitLevelLoader @@ -64,6 +76,7 @@ class CBitLevelLoader public: CBitLevelLoader(const void* data) : m_data(reinterpret_cast(data)) {} + void Reset() { m_bitIdx = 0; } u32 LoadUnsigned(u8 q); s32 LoadSigned(u8 q); bool LoadBool(); @@ -82,9 +95,12 @@ class CFBStreamedAnimReader : public CAnimSourceReaderBase TSubAnimTypeToken x54_source; CSteadyStateAnimInfo x64_steadyStateInfo; CFBStreamedPairOfTotals x7c_totals; - const u8* x88_bitstreamData; - CBitLevelLoader x8c_bitLoader; + const u8* x104_bitstreamData; + CBitLevelLoader x108_bitLoader; CSegIdToIndexConverter x114_segIdToIndex; + bool HasOffset(const CSegId& seg) const; + zeus::CVector3f GetOffset(const CSegId& seg) const; + zeus::CQuaternion GetRotation(const CSegId& seg) const; public: CFBStreamedAnimReader(const TSubAnimTypeToken& source, const CCharAnimTime& time); diff --git a/Runtime/Character/CFBStreamedCompression.cpp b/Runtime/Character/CFBStreamedCompression.cpp index 5da775581..4e374059b 100644 --- a/Runtime/Character/CFBStreamedCompression.cpp +++ b/Runtime/Character/CFBStreamedCompression.cpp @@ -15,6 +15,11 @@ CFBStreamedCompression::CFBStreamedCompression(CInputStream& in, IObjectStore& o x8_evntToken = objStore.GetObj(SObjectTag{FOURCC('EVNT'), x4_evnt}); } +const u32* CFBStreamedCompression::GetTimes() const +{ + return reinterpret_cast(xc_rotsAndOffs.get() + 9); +} + const u8* CFBStreamedCompression::GetPerChannelHeaders() const { const u32* bitmap = reinterpret_cast(xc_rotsAndOffs.get() + 9); diff --git a/Runtime/Character/CFBStreamedCompression.hpp b/Runtime/Character/CFBStreamedCompression.hpp index 3e587c8b8..a96e9d12a 100644 --- a/Runtime/Character/CFBStreamedCompression.hpp +++ b/Runtime/Character/CFBStreamedCompression.hpp @@ -67,6 +67,7 @@ private: public: CFBStreamedCompression(CInputStream& in, IObjectStore& objStore, bool pc); const Header& MainHeader() const { return *reinterpret_cast(xc_rotsAndOffs.get()); } + const u32* GetTimes() const; const u8* GetPerChannelHeaders() const; const u8* GetBitstreamPointer() const; bool IsLooping() const { return MainHeader().looping; } diff --git a/Runtime/Character/CSegStatementSet.hpp b/Runtime/Character/CSegStatementSet.hpp index bfc5feba9..0288abf03 100644 --- a/Runtime/Character/CSegStatementSet.hpp +++ b/Runtime/Character/CSegStatementSet.hpp @@ -2,6 +2,7 @@ #define __URDE_CSEGSTATEMENTSET_HPP__ #include "CAnimPerSegmentData.hpp" +#include "CSegId.hpp" namespace urde { @@ -17,7 +18,7 @@ public: void Add(const CSegIdList& list, const CCharLayoutInfo& layout, const CSegStatementSet& other, float weight); - CAnimPerSegmentData& operator[](size_t idx) { return x4_segData[idx]; } + CAnimPerSegmentData& operator[](const CSegId& idx) { return x4_segData[idx]; } }; }