diff --git a/src/Core/Core.pro b/src/Core/Core.pro index b6a0ac53..fb197912 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -228,8 +228,8 @@ HEADERS += \ Resource/CResTypeInfo.h \ Resource/Cooker/CResourceCooker.h \ Resource/CAudioMacro.h \ - Resource/Animation/CCharacter.h \ - CompressionUtil.h + CompressionUtil.h \ + Resource/Animation/CSourceAnimData.h # Source Files SOURCES += \ diff --git a/src/Core/GameProject/AssetNameGeneration.cpp b/src/Core/GameProject/AssetNameGeneration.cpp index df2e97c9..e753f300 100644 --- a/src/Core/GameProject/AssetNameGeneration.cpp +++ b/src/Core/GameProject/AssetNameGeneration.cpp @@ -5,6 +5,7 @@ #include "Core/Resource/CFont.h" #include "Core/Resource/CScan.h" #include "Core/Resource/CWorld.h" +#include "Core/Resource/Animation/CAnimSet.h" #include "Core/Resource/Script/CScriptLayer.h" #include @@ -398,19 +399,44 @@ void GenerateAssetNames(CGameProject *pProj) if (pkChar->pSkeleton) ApplyGeneratedName(pkChar->pSkeleton->Entry(), SetDir, CharName); if (pkChar->pSkin) ApplyGeneratedName(pkChar->pSkin->Entry(), SetDir, CharName); - if (pkChar->IceModel.IsValid() || pkChar->IceSkin.IsValid()) + if (pProj->Game() >= eCorruptionProto && pProj->Game() <= eCorruption && pkChar->ID == 0) { - TWideString IceName = TWideString::Format(L"%s_frozen", *CharName); + CResourceEntry *pAnimDataEntry = gpResourceStore->FindEntry( pkChar->AnimDataID ); - if (pkChar->IceModel.IsValid()) + if (pAnimDataEntry) { - CResourceEntry *pIceModelEntry = pStore->FindEntry(pkChar->IceModel); - ApplyGeneratedName(pIceModelEntry, SetDir, IceName); + TWideString AnimDataName = TString::Format(L"%s_animdata", *CharName); + ApplyGeneratedName(pAnimDataEntry, SetDir, AnimDataName); } - if (pkChar->IceSkin.IsValid()) + } + + for (u32 iOverlay = 0; iOverlay < pkChar->OverlayModels.size(); iOverlay++) + { + const SOverlayModel& rkOverlay = pkChar->OverlayModels[iOverlay]; + + if (rkOverlay.ModelID.IsValid() || rkOverlay.SkinID.IsValid()) { - CResourceEntry *pIceSkinEntry = pStore->FindEntry(pkChar->IceSkin); - ApplyGeneratedName(pIceSkinEntry, SetDir, IceName); + TWideString TypeName = ( + rkOverlay.Type == eOT_Frozen ? L"frozen" : + rkOverlay.Type == eOT_Acid ? L"acid" : + rkOverlay.Type == eOT_Hypermode ? L"hypermode" : + rkOverlay.Type == eOT_XRay ? L"xray" : + L"" + ); + ASSERT(TypeName != L""); + + TWideString OverlayName = TWideString::Format(L"%s_%s", *CharName, *TypeName); + + if (rkOverlay.ModelID.IsValid()) + { + CResourceEntry *pModelEntry = pStore->FindEntry(rkOverlay.ModelID); + ApplyGeneratedName(pModelEntry, SetDir, OverlayName); + } + if (rkOverlay.SkinID.IsValid()) + { + CResourceEntry *pSkinEntry = pStore->FindEntry(rkOverlay.SkinID); + ApplyGeneratedName(pSkinEntry, SetDir, OverlayName); + } } } } diff --git a/src/Core/GameProject/CDependencyTree.cpp b/src/Core/GameProject/CDependencyTree.cpp index 520689ee..c1a3b2c5 100644 --- a/src/Core/GameProject/CDependencyTree.cpp +++ b/src/Core/GameProject/CDependencyTree.cpp @@ -1,5 +1,6 @@ #include "CDependencyTree.h" #include "Core/GameProject/CGameProject.h" +#include "Core/Resource/Animation/CAnimSet.h" #include "Core/Resource/Script/CMasterTemplate.h" #include "Core/Resource/Script/CScriptLayer.h" #include "Core/Resource/Script/CScriptObject.h" @@ -199,34 +200,35 @@ void CSetCharacterDependency::Serialize(IArchive& rArc) << SERIAL_ABSTRACT_CONTAINER("Children", mChildren, "Child", &gDependencyNodeFactory); } -CSetCharacterDependency* CSetCharacterDependency::BuildTree(const CAnimSet *pkOwnerSet, u32 CharIndex) +CSetCharacterDependency* CSetCharacterDependency::BuildTree(const SSetCharacter& rkChar) { - CSetCharacterDependency *pTree = new CSetCharacterDependency(CharIndex); - const SSetCharacter *pkChar = pkOwnerSet->Character(CharIndex); + CSetCharacterDependency *pTree = new CSetCharacterDependency(rkChar.ID); + pTree->AddDependency(rkChar.pModel); + pTree->AddDependency(rkChar.pSkeleton); + pTree->AddDependency(rkChar.pSkin); + pTree->AddDependency(rkChar.AnimDataID); - if (pkChar) + const std::vector *pkParticleVectors[5] = { + &rkChar.GenericParticles, &rkChar.ElectricParticles, + &rkChar.SwooshParticles, &rkChar.SpawnParticles, + &rkChar.EffectParticles + }; + + for (u32 iVec = 0; iVec < 5; iVec++) { - pTree->AddDependency(pkChar->pModel); - pTree->AddDependency(pkChar->pSkeleton); - pTree->AddDependency(pkChar->pSkin); - - const std::vector *pkParticleVectors[5] = { - &pkChar->GenericParticles, &pkChar->ElectricParticles, - &pkChar->SwooshParticles, &pkChar->SpawnParticles, - &pkChar->EffectParticles - }; - - for (u32 iVec = 0; iVec < 5; iVec++) - { - for (u32 iPart = 0; iPart < pkParticleVectors[iVec]->size(); iPart++) - pTree->AddDependency(pkParticleVectors[iVec]->at(iPart)); - } - - pTree->AddDependency(pkChar->IceModel); - pTree->AddDependency(pkChar->IceSkin); - pTree->AddDependency(pkChar->SpatialPrimitives); + for (u32 iPart = 0; iPart < pkParticleVectors[iVec]->size(); iPart++) + pTree->AddDependency(pkParticleVectors[iVec]->at(iPart)); } + for (u32 iOverlay = 0; iOverlay < rkChar.OverlayModels.size(); iOverlay++) + { + const SOverlayModel& rkOverlay = rkChar.OverlayModels[iOverlay]; + pTree->AddDependency(rkOverlay.ModelID); + pTree->AddDependency(rkOverlay.SkinID); + } + + pTree->AddDependency(rkChar.SpatialPrimitives); + return pTree; } diff --git a/src/Core/GameProject/CDependencyTree.h b/src/Core/GameProject/CDependencyTree.h index d21db94c..6c04e638 100644 --- a/src/Core/GameProject/CDependencyTree.h +++ b/src/Core/GameProject/CDependencyTree.h @@ -160,7 +160,7 @@ public: inline u32 CharSetIndex() const { return mCharSetIndex; } // Static - static CSetCharacterDependency* BuildTree(const CAnimSet *pkOwnerSet, u32 CharIndex); + static CSetCharacterDependency* BuildTree(const SSetCharacter& rkChar); }; // Node representing a character animation. Indicates which character indices use this animation. diff --git a/src/Core/GameProject/DependencyListBuilders.cpp b/src/Core/GameProject/DependencyListBuilders.cpp index fc94fa62..666d5ed6 100644 --- a/src/Core/GameProject/DependencyListBuilders.cpp +++ b/src/Core/GameProject/DependencyListBuilders.cpp @@ -154,12 +154,9 @@ void CCharacterUsageMap::ParseDependencyNode(IDependencyNode *pNode) CResourceDependency *pDep = static_cast(pNode); CResourceEntry *pEntry = mpStore->FindEntry(pDep->ID()); - if (pEntry) + if (pEntry && pEntry->ResourceType() == eScan) { - EResType ResType = pEntry->ResourceType(); - - if (ResType == eScan) - ParseDependencyNode(pEntry->Dependencies()); + ParseDependencyNode(pEntry->Dependencies()); } } diff --git a/src/Core/Render/CRenderer.cpp b/src/Core/Render/CRenderer.cpp index bcb4a696..0967f695 100644 --- a/src/Core/Render/CRenderer.cpp +++ b/src/Core/Render/CRenderer.cpp @@ -144,8 +144,8 @@ void CRenderer::RenderBuckets(const SViewInfo& rkViewInfo) void CRenderer::RenderBloom() { - // Check to ensure bloom is enabled - if (mBloomMode == eNoBloom) return; + // Check to ensure bloom is enabled. Also don't render bloom in unlit mode. + if (mBloomMode == eNoBloom || CGraphics::sLightMode != CGraphics::eWorldLighting) return; // Setup static const float skHOffset[6] = { -0.008595f, -0.005470f, -0.002345f, diff --git a/src/Core/Resource/Animation/CAnimSet.h b/src/Core/Resource/Animation/CAnimSet.h index 5e6db8b9..3dc66586 100644 --- a/src/Core/Resource/Animation/CAnimSet.h +++ b/src/Core/Resource/Animation/CAnimSet.h @@ -5,6 +5,7 @@ #include "CAnimEventData.h" #include "CSkeleton.h" #include "CSkin.h" +#include "CSourceAnimData.h" #include "IMetaAnimation.h" #include "IMetaTransition.h" #include "Core/Resource/CDependencyGroup.h" @@ -43,20 +44,38 @@ struct SHalfTransition IMetaTransition *pMetaTrans; }; +// Character structures +enum EOverlayType +{ + eOT_Frozen = FOURCC('FRZN'), + eOT_Hypermode = FOURCC('HYPR'), + eOT_Acid = FOURCC('ACID'), + eOT_XRay = FOURCC('XRAY') +}; + +struct SOverlayModel +{ + EOverlayType Type; + CAssetID ModelID; + CAssetID SkinID; +}; + struct SSetCharacter { + u32 ID; TString Name; TResPtr pModel; TResPtr pSkin; TResPtr pSkeleton; + std::vector OverlayModels; + CAssetID AnimDataID; std::vector GenericParticles; std::vector ElectricParticles; std::vector SwooshParticles; std::vector SpawnParticles; std::vector EffectParticles; - CAssetID IceModel; - CAssetID IceSkin; + std::vector SoundEffects; CAssetID SpatialPrimitives; std::set UsedAnimationIndices; }; @@ -114,16 +133,49 @@ public: // Character dependencies for (u32 iChar = 0; iChar < mCharacters.size(); iChar++) { - CSetCharacterDependency *pCharTree = CSetCharacterDependency::BuildTree(this, iChar); + CSetCharacterDependency *pCharTree = CSetCharacterDependency::BuildTree( mCharacters[iChar] ); ASSERT(pCharTree); pTree->AddChild(pCharTree); } - for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++) + // Animation dependencies + if (Game() <= eEchoes) { - CSetAnimationDependency *pAnimTree = CSetAnimationDependency::BuildTree(this, iAnim); - ASSERT(pAnimTree); - pTree->AddChild(pAnimTree); + for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++) + { + CSetAnimationDependency *pAnimTree = CSetAnimationDependency::BuildTree(this, iAnim); + ASSERT(pAnimTree); + pTree->AddChild(pAnimTree); + } + } + + else + { + const SSetCharacter& rkChar = mCharacters[0]; + std::set PrimitiveSet; + + // Animations + for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++) + { + const SAnimation& rkAnim = mAnimations[iAnim]; + rkAnim.pMetaAnim->GetUniquePrimitives(PrimitiveSet); + } + + CSourceAnimData *pAnimData = (CSourceAnimData*) gpResourceStore->LoadResource(rkChar.AnimDataID, "SAND"); + if (pAnimData) + pAnimData->AddTransitionDependencies(pTree); + + // Event particles/sounds + for (auto Iter = PrimitiveSet.begin(); Iter != PrimitiveSet.end(); Iter++) + { + const CAnimPrimitive& rkPrim = *Iter; + pTree->AddDependency(rkPrim.Animation()); + } + + for (u32 iSound = 0; iSound < rkChar.SoundEffects.size(); iSound++) + { + pTree->AddDependency(rkChar.SoundEffects[iSound]); + } } return pTree; diff --git a/src/Core/Resource/Animation/CAnimationParameters.cpp b/src/Core/Resource/Animation/CAnimationParameters.cpp index f4cc6d98..d3369474 100644 --- a/src/Core/Resource/Animation/CAnimationParameters.cpp +++ b/src/Core/Resource/Animation/CAnimationParameters.cpp @@ -136,30 +136,32 @@ void CAnimationParameters::Write(IOutputStream& rSCLY) } } +const SSetCharacter* CAnimationParameters::GetCurrentSetCharacter(s32 NodeIndex /*= -1*/) +{ + CAnimSet *pSet = AnimSet(); + + if (pSet && pSet->Type() == eAnimSet) + { + if (NodeIndex == -1) + NodeIndex = mCharIndex; + + if (mCharIndex != -1 && pSet->NumCharacters() > (u32) NodeIndex) + return pSet->Character(NodeIndex); + } + + return nullptr; +} + CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/) { - if (!mCharacterID.IsValid()) return nullptr; - - CAnimSet *pSet = AnimSet(); - if (!pSet) return nullptr; - if (pSet->Type() != eAnimSet) return nullptr; - if (NodeIndex == -1) NodeIndex = mCharIndex; - - if (pSet->NumCharacters() <= (u32) NodeIndex) return nullptr; - return pSet->Character(NodeIndex)->pModel; + const SSetCharacter *pkChar = GetCurrentSetCharacter(NodeIndex); + return pkChar ? pkChar->pModel : nullptr; } TString CAnimationParameters::GetCurrentCharacterName(s32 NodeIndex /*= -1*/) { - if (!mCharacterID.IsValid()) return ""; - - CAnimSet *pSet = AnimSet(); - if (!pSet) return ""; - if (pSet->Type() != eAnimSet) return ""; - if (NodeIndex == -1) NodeIndex = mCharIndex; - - if (pSet->NumCharacters() <= (u32) NodeIndex) return ""; - return pSet->Character(NodeIndex)->Name; + const SSetCharacter *pkChar = GetCurrentSetCharacter(NodeIndex); + return pkChar ? pkChar->Name : ""; } // ************ ACCESSORS ************ diff --git a/src/Core/Resource/Animation/CAnimationParameters.h b/src/Core/Resource/Animation/CAnimationParameters.h index ff5f3768..1fb0b344 100644 --- a/src/Core/Resource/Animation/CAnimationParameters.h +++ b/src/Core/Resource/Animation/CAnimationParameters.h @@ -1,7 +1,6 @@ #ifndef CANIMATIONPARAMETERS_H #define CANIMATIONPARAMETERS_H -#include "CAnimSet.h" #include "Core/Resource/TResPtr.h" #include "Core/Resource/Model/CModel.h" #include @@ -22,6 +21,7 @@ public: CAnimationParameters(IInputStream& rSCLY, EGame Game); void Write(IOutputStream& rSCLY); + const SSetCharacter* GetCurrentSetCharacter(s32 NodeIndex = -1); CModel* GetCurrentModel(s32 NodeIndex = -1); TString GetCurrentCharacterName(s32 NodeIndex = -1); diff --git a/src/Core/Resource/Animation/CSourceAnimData.h b/src/Core/Resource/Animation/CSourceAnimData.h new file mode 100644 index 00000000..a55bbc3a --- /dev/null +++ b/src/Core/Resource/Animation/CSourceAnimData.h @@ -0,0 +1,109 @@ +#ifndef CSOURCEANIMDATA_H +#define CSOURCEANIMDATA_H + +#include "Core/Resource/CResource.h" +#include "IMetaTransition.h" + +class CSourceAnimData : public CResource +{ + DECLARE_RESOURCE_TYPE(eSourceAnimData) + friend class CAnimSetLoader; + + struct STransition + { + CAssetID AnimA; + CAssetID AnimB; + IMetaTransition *pTransition; + }; + + struct SHalfTransition + { + CAssetID Anim; + IMetaTransition *pTransition; + }; + + std::vector mTransitions; + std::vector mHalfTransitions; + IMetaTransition *mpDefaultTransition; + +public: + CSourceAnimData(CResourceEntry *pEntry = 0) + : CResource(pEntry) + , mpDefaultTransition(nullptr) + {} + + ~CSourceAnimData() + { + for (u32 TransIdx = 0; TransIdx < mTransitions.size(); TransIdx++) + delete mTransitions[TransIdx].pTransition; + + for (u32 HalfIdx = 0; HalfIdx < mHalfTransitions.size(); HalfIdx++) + delete mHalfTransitions[HalfIdx].pTransition; + + delete mpDefaultTransition; + } + + CDependencyTree* BuildDependencyTree() const + { + // SAND normally has dependencies from meta-transitions and events + // However, all of these can be character-specific. To simplify things, all SAND + // dependencies are being added to the CHAR dependency tree instead. Therefore the + // SAND dependency tree is left empty. + return new CDependencyTree(); + } + + void AddTransitionDependencies(CDependencyTree *pTree) + { + // Note: All CHAR animations must have been added to the tree before this function is run + std::set UsedTransitions; + + while (true) + { + // Find all relevant primitives + std::set PrimSet; + + if (UsedTransitions.find(mpDefaultTransition) == UsedTransitions.end()) + { + mpDefaultTransition->GetUniquePrimitives(PrimSet); + UsedTransitions.insert(mpDefaultTransition); + } + + for (u32 TransitionIdx = 0; TransitionIdx < mTransitions.size(); TransitionIdx++) + { + const STransition& rkTransition = mTransitions[TransitionIdx]; + IMetaTransition *pTransition = rkTransition.pTransition; + + if ( pTree->HasDependency(rkTransition.AnimA) && + pTree->HasDependency(rkTransition.AnimB) && + UsedTransitions.find(pTransition) == UsedTransitions.end() ) + { + pTransition->GetUniquePrimitives(PrimSet); + UsedTransitions.insert(pTransition); + } + } + + for (u32 HalfIdx = 0; HalfIdx < mHalfTransitions.size(); HalfIdx++) + { + const SHalfTransition& rkHalfTrans = mHalfTransitions[HalfIdx]; + IMetaTransition *pTransition = rkHalfTrans.pTransition; + + if ( pTree->HasDependency(rkHalfTrans.Anim) && + UsedTransitions.find(pTransition) == UsedTransitions.end() ) + { + pTransition->GetUniquePrimitives(PrimSet); + UsedTransitions.insert(pTransition); + } + } + + // If we have no new primitives then we've exhausted all usable transitions; break out + if (PrimSet.empty()) + break; + + // Add all transition primitives to the tree + for (auto Iter = PrimSet.begin(); Iter != PrimSet.end(); Iter++) + pTree->AddDependency(Iter->Animation()); + } + } +}; + +#endif // CSOURCEANIMDATA_H diff --git a/src/Core/Resource/Animation/IMetaAnimation.cpp b/src/Core/Resource/Animation/IMetaAnimation.cpp index d7adbec9..e7752cdf 100644 --- a/src/Core/Resource/Animation/IMetaAnimation.cpp +++ b/src/Core/Resource/Animation/IMetaAnimation.cpp @@ -3,24 +3,24 @@ // ************ CMetaAnimFactory ************ CMetaAnimFactory gMetaAnimFactory; -IMetaAnimation* CMetaAnimFactory::LoadFromStream(IInputStream& rInput) +IMetaAnimation* CMetaAnimFactory::LoadFromStream(IInputStream& rInput, EGame Game) { EMetaAnimationType Type = (EMetaAnimationType) rInput.ReadLong(); switch (Type) { case eMAT_Play: - return new CMetaAnimPlay(rInput); + return new CMetaAnimPlay(rInput, Game); case eMAT_Blend: case eMAT_PhaseBlend: - return new CMetaAnimBlend(Type, rInput); + return new CMetaAnimBlend(Type, rInput, Game); case eMAT_Random: - return new CMetaAnimRandom(rInput); + return new CMetaAnimRandom(rInput, Game); case eMAT_Sequence: - return new CMetaAnimSequence(rInput); + return new CMetaAnimSequence(rInput, Game); default: Log::Error("Unrecognized meta-animation type: " + TString::FromInt32(Type, 0, 10)); @@ -29,9 +29,9 @@ IMetaAnimation* CMetaAnimFactory::LoadFromStream(IInputStream& rInput) } // ************ CMetaAnimationPlay ************ -CMetaAnimPlay::CMetaAnimPlay(IInputStream& rInput) +CMetaAnimPlay::CMetaAnimPlay(IInputStream& rInput, EGame Game) { - mPrimitive = CAnimPrimitive(rInput); + mPrimitive = CAnimPrimitive(rInput, Game); mUnknownA = rInput.ReadFloat(); mUnknownB = rInput.ReadLong(); } @@ -47,12 +47,12 @@ void CMetaAnimPlay::GetUniquePrimitives(std::set& rPrimSet) cons } // ************ CMetaAnimBlend ************ -CMetaAnimBlend::CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput) +CMetaAnimBlend::CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput, EGame Game) { ASSERT(Type == eMAT_Blend || Type == eMAT_PhaseBlend); mType = Type; - mpMetaAnimA = gMetaAnimFactory.LoadFromStream(rInput); - mpMetaAnimB = gMetaAnimFactory.LoadFromStream(rInput); + mpMetaAnimA = gMetaAnimFactory.LoadFromStream(rInput, Game); + mpMetaAnimB = gMetaAnimFactory.LoadFromStream(rInput, Game); mUnknownA = rInput.ReadFloat(); mUnknownB = rInput.ReadBool(); } @@ -75,7 +75,7 @@ void CMetaAnimBlend::GetUniquePrimitives(std::set& rPrimSet) con } // ************ CMetaAnimRandom ************ -CMetaAnimRandom::CMetaAnimRandom(IInputStream& rInput) +CMetaAnimRandom::CMetaAnimRandom(IInputStream& rInput, EGame Game) { u32 NumPairs = rInput.ReadLong(); mProbabilityPairs.reserve(NumPairs); @@ -83,7 +83,7 @@ CMetaAnimRandom::CMetaAnimRandom(IInputStream& rInput) for (u32 iAnim = 0; iAnim < NumPairs; iAnim++) { SAnimProbabilityPair Pair; - Pair.pAnim = gMetaAnimFactory.LoadFromStream(rInput); + Pair.pAnim = gMetaAnimFactory.LoadFromStream(rInput, Game); Pair.Probability = rInput.ReadLong(); mProbabilityPairs.push_back(Pair); } @@ -107,14 +107,14 @@ void CMetaAnimRandom::GetUniquePrimitives(std::set& rPrimSet) co } // ************ CMetaAnimSequence ************ -CMetaAnimSequence::CMetaAnimSequence(IInputStream& rInput) +CMetaAnimSequence::CMetaAnimSequence(IInputStream& rInput, EGame Game) { u32 NumAnims = rInput.ReadLong(); mAnimations.reserve(NumAnims); for (u32 iAnim = 0; iAnim < NumAnims; iAnim++) { - IMetaAnimation *pAnim = gMetaAnimFactory.LoadFromStream(rInput); + IMetaAnimation *pAnim = gMetaAnimFactory.LoadFromStream(rInput, Game); mAnimations.push_back(pAnim); } } diff --git a/src/Core/Resource/Animation/IMetaAnimation.h b/src/Core/Resource/Animation/IMetaAnimation.h index ab80584e..0ffb4b52 100644 --- a/src/Core/Resource/Animation/IMetaAnimation.h +++ b/src/Core/Resource/Animation/IMetaAnimation.h @@ -18,7 +18,7 @@ enum EMetaAnimationType class CMetaAnimFactory { public: - class IMetaAnimation* LoadFromStream(IInputStream& rInput); + class IMetaAnimation* LoadFromStream(IInputStream& rInput, EGame Game); }; extern CMetaAnimFactory gMetaAnimFactory; @@ -32,9 +32,9 @@ class CAnimPrimitive public: CAnimPrimitive() : mID(0) {} - CAnimPrimitive(IInputStream& rInput) + CAnimPrimitive(IInputStream& rInput, EGame Game) { - mpAnim = gpResourceStore->LoadResource(rInput.ReadLong(), "ANIM"); + mpAnim = gpResourceStore->LoadResource( CAssetID(rInput, Game), "ANIM" ); mID = rInput.ReadLong(); mName = rInput.ReadString(); } @@ -58,7 +58,7 @@ public: virtual void GetUniquePrimitives(std::set& rPrimSet) const = 0; // Static - static IMetaAnimation* LoadFromStream(IInputStream& rInput); + static IMetaAnimation* LoadFromStream(IInputStream& rInput, EGame Game); }; // CMetaAnimPlay - plays an animation @@ -70,7 +70,7 @@ protected: u32 mUnknownB; public: - CMetaAnimPlay(IInputStream& rInput); + CMetaAnimPlay(IInputStream& rInput, EGame Game); virtual EMetaAnimationType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; @@ -91,7 +91,7 @@ protected: bool mUnknownB; public: - CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput); + CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput, EGame Game); ~CMetaAnimBlend(); virtual EMetaAnimationType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; @@ -117,7 +117,7 @@ protected: std::vector mProbabilityPairs; public: - CMetaAnimRandom(IInputStream& rInput); + CMetaAnimRandom(IInputStream& rInput, EGame Game); ~CMetaAnimRandom(); virtual EMetaAnimationType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; @@ -130,7 +130,7 @@ protected: std::vector mAnimations; public: - CMetaAnimSequence(IInputStream& rInput); + CMetaAnimSequence(IInputStream& rInput, EGame Game); ~CMetaAnimSequence(); virtual EMetaAnimationType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; diff --git a/src/Core/Resource/Animation/IMetaTransition.cpp b/src/Core/Resource/Animation/IMetaTransition.cpp index 3a983fc8..03ccc383 100644 --- a/src/Core/Resource/Animation/IMetaTransition.cpp +++ b/src/Core/Resource/Animation/IMetaTransition.cpp @@ -4,21 +4,24 @@ // ************ CMetaTransFactory ************ CMetaTransFactory gMetaTransFactory; -IMetaTransition* CMetaTransFactory::LoadFromStream(IInputStream& rInput) +IMetaTransition* CMetaTransFactory::LoadFromStream(IInputStream& rInput, EGame Game) { EMetaTransitionType Type = (EMetaTransitionType) rInput.ReadLong(); switch (Type) { case eMTT_MetaAnim: - return new CMetaTransMetaAnim(rInput); + return new CMetaTransMetaAnim(rInput, Game); case eMTT_Trans: case eMTT_PhaseTrans: - return new CMetaTransTrans(Type, rInput); + return new CMetaTransTrans(Type, rInput, Game); case eMTT_Snap: - return new CMetaTransSnap(rInput); + return new CMetaTransSnap(rInput, Game); + + case eMTT_Type4: + return new CMetaTransType4(rInput, Game); default: Log::Error("Unrecognized meta-transition type: " + TString::FromInt32(Type, 0, 10)); @@ -27,9 +30,9 @@ IMetaTransition* CMetaTransFactory::LoadFromStream(IInputStream& rInput) } // ************ CMetaTransMetaAnim ************ -CMetaTransMetaAnim::CMetaTransMetaAnim(IInputStream& rInput) +CMetaTransMetaAnim::CMetaTransMetaAnim(IInputStream& rInput, EGame Game) { - mpAnim = gMetaAnimFactory.LoadFromStream(rInput); + mpAnim = gMetaAnimFactory.LoadFromStream(rInput, Game); } CMetaTransMetaAnim::~CMetaTransMetaAnim() @@ -48,15 +51,23 @@ void CMetaTransMetaAnim::GetUniquePrimitives(std::set& rPrimSet) } // ************ CMetaTransTrans ************ -CMetaTransTrans::CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput) +CMetaTransTrans::CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput, EGame Game) { ASSERT(Type == eMTT_Trans || Type == eMTT_PhaseTrans); mType = Type; - mUnknownA = rInput.ReadFloat(); - mUnknownB = rInput.ReadLong(); - mUnknownC = rInput.ReadBool(); - mUnknownD = rInput.ReadBool(); - mUnknownE = rInput.ReadLong(); + + if (Game <= eEchoes) + { + mUnknownA = rInput.ReadFloat(); + mUnknownB = rInput.ReadLong(); + mUnknownC = rInput.ReadBool(); + mUnknownD = rInput.ReadBool(); + mUnknownE = rInput.ReadLong(); + } + else + { + rInput.Skip(0x13); + } } EMetaTransitionType CMetaTransTrans::Type() const @@ -69,7 +80,7 @@ void CMetaTransTrans::GetUniquePrimitives(std::set&) const } // ************ CMetaTransSnap ************ -CMetaTransSnap::CMetaTransSnap(IInputStream&) +CMetaTransSnap::CMetaTransSnap(IInputStream&, EGame) { } @@ -81,3 +92,18 @@ EMetaTransitionType CMetaTransSnap::Type() const void CMetaTransSnap::GetUniquePrimitives(std::set&) const { } + +// ************ CMetaTransType4 ************ +CMetaTransType4::CMetaTransType4(IInputStream& rInput, EGame) +{ + rInput.Skip(0x14); +} + +EMetaTransitionType CMetaTransType4::Type() const +{ + return eMTT_Type4; +} + +void CMetaTransType4::GetUniquePrimitives(std::set&) const +{ +} diff --git a/src/Core/Resource/Animation/IMetaTransition.h b/src/Core/Resource/Animation/IMetaTransition.h index 0c94c46d..ff82d580 100644 --- a/src/Core/Resource/Animation/IMetaTransition.h +++ b/src/Core/Resource/Animation/IMetaTransition.h @@ -11,14 +11,15 @@ enum EMetaTransitionType eMTT_MetaAnim = 0, eMTT_Trans = 1, eMTT_PhaseTrans = 2, // note: structure shared with eMTT_Trans - eMTT_Snap = 3 + eMTT_Snap = 3, + eMTT_Type4 = 4 // MP3 only }; // Factory class class CMetaTransFactory { public: - class IMetaTransition* LoadFromStream(IInputStream& rInput); + class IMetaTransition* LoadFromStream(IInputStream& rInput, EGame Game); }; extern CMetaTransFactory gMetaTransFactory; @@ -38,7 +39,7 @@ class CMetaTransMetaAnim : public IMetaTransition IMetaAnimation *mpAnim; public: - CMetaTransMetaAnim(IInputStream& rInput); + CMetaTransMetaAnim(IInputStream& rInput, EGame Game); ~CMetaTransMetaAnim(); virtual EMetaTransitionType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; @@ -55,7 +56,7 @@ class CMetaTransTrans : public IMetaTransition u32 mUnknownE; public: - CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput); + CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput, EGame Game); virtual EMetaTransitionType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; }; @@ -64,7 +65,16 @@ public: class CMetaTransSnap : public IMetaTransition { public: - CMetaTransSnap(IInputStream& rInput); + CMetaTransSnap(IInputStream& rInput, EGame Game); + virtual EMetaTransitionType Type() const; + virtual void GetUniquePrimitives(std::set& rPrimSet) const; +}; + +// CMetaTransType4 +class CMetaTransType4 : public IMetaTransition +{ +public: + CMetaTransType4(IInputStream& rInput, EGame Game); virtual EMetaTransitionType Type() const; virtual void GetUniquePrimitives(std::set& rPrimSet) const; }; diff --git a/src/Core/Resource/CResTypeInfo.cpp b/src/Core/Resource/CResTypeInfo.cpp index b9e91d90..02a56709 100644 --- a/src/Core/Resource/CResTypeInfo.cpp +++ b/src/Core/Resource/CResTypeInfo.cpp @@ -351,6 +351,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes() CResTypeInfo *pType = new CResTypeInfo(eSourceAnimData, "Source Animation Data"); AddExtension(pType, "SAND", eCorruptionProto, eCorruption); pType->mHidden = true; + pType->mCanHaveDependencies = false; // all dependencies are added to the CHAR dependency tree } { CResTypeInfo *pType = new CResTypeInfo(eSpatialPrimitive, "Spatial Primitive"); diff --git a/src/Core/Resource/CScan.h b/src/Core/Resource/CScan.h index 5b54af24..04523621 100644 --- a/src/Core/Resource/CScan.h +++ b/src/Core/Resource/CScan.h @@ -52,6 +52,9 @@ private: CAnimationParameters mUnknownAnimParams; std::vector mSecondaryModels; + // MP3 + std::vector mDependencyList; + public: CScan(CResourceEntry *pEntry = 0) : CResource(pEntry) @@ -63,12 +66,20 @@ public: CDependencyTree* BuildDependencyTree() const { - // note: not handling Corruption yet - if (Game() >= eCorruptionProto) - Log::Warning("CScan::BuildDependencyTree not handling Corruption dependencies"); - CDependencyTree *pTree = new CDependencyTree(); + // Corruption's SCAN has a list of all assets - just grab that + if (Game() >= eCorruptionProto) + { + for (u32 iDep = 0; iDep < mDependencyList.size(); iDep++) + { + pTree->AddDependency(mDependencyList[iDep]); + } + + return pTree; + } + + // Otherwise add all the dependencies we need from the properties if (Game() <= ePrime) pTree->AddDependency(mFrameID); diff --git a/src/Core/Resource/Factory/CAnimEventLoader.cpp b/src/Core/Resource/Factory/CAnimEventLoader.cpp index 3b2f9f33..00c2a9f4 100644 --- a/src/Core/Resource/Factory/CAnimEventLoader.cpp +++ b/src/Core/Resource/Factory/CAnimEventLoader.cpp @@ -2,7 +2,7 @@ #include "Core/CAudioManager.h" #include "Core/GameProject/CGameProject.h" -void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes) +void CAnimEventLoader::LoadEvents(IInputStream& rEVNT) { u32 Version = rEVNT.ReadLong(); ASSERT(Version == 1 || Version == 2); @@ -12,9 +12,7 @@ void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes) for (u32 iLoop = 0; iLoop < NumLoopEvents; iLoop++) { - rEVNT.Seek(0x2, SEEK_CUR); - rEVNT.ReadString(); - rEVNT.Seek(0x1C, SEEK_CUR); + LoadLoopEvent(rEVNT); } // User Events @@ -22,10 +20,7 @@ void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes) for (u32 iUser = 0; iUser < NumUserEvents; iUser++) { - rEVNT.Seek(0x2, SEEK_CUR); - rEVNT.ReadString(); - rEVNT.Seek(0x1F, SEEK_CUR); - rEVNT.ReadString(); + LoadUserEvent(rEVNT); } // Effect Events @@ -33,20 +28,7 @@ void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes) for (u32 iFX = 0; iFX < NumEffectEvents; iFX++) { - rEVNT.Seek(0x2, SEEK_CUR); - rEVNT.ReadString(); - rEVNT.Seek(0x13, SEEK_CUR); - u32 CharIndex = rEVNT.ReadLong(); - rEVNT.Seek(0xC, SEEK_CUR); - CAssetID ParticleID = rEVNT.ReadLong(); - mpEventData->AddEvent(CharIndex, ParticleID); - - if (IsEchoes) - rEVNT.Seek(0x4, SEEK_CUR); - else - rEVNT.ReadString(); - - rEVNT.Seek(0x8, SEEK_CUR); + LoadEffectEvent(rEVNT); } // Sound Events @@ -56,21 +38,92 @@ void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes) for (u32 iSound = 0; iSound < NumSoundEvents; iSound++) { - rEVNT.Seek(0x2, SEEK_CUR); - rEVNT.ReadString(); - rEVNT.Seek(0x13, SEEK_CUR); - u32 CharIndex = rEVNT.ReadLong(); - rEVNT.Seek(0x4, SEEK_CUR); - u32 SoundID = rEVNT.ReadLong() & 0xFFFF; - rEVNT.Seek(0x8, SEEK_CUR); - if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR); + LoadSoundEvent(rEVNT); + } + } +} - if (SoundID != 0xFFFF) +s32 CAnimEventLoader::LoadEventBase(IInputStream& rEVNT) +{ + rEVNT.Skip(0x2); + rEVNT.ReadString(); + rEVNT.Skip(mGame < eCorruptionProto ? 0x13 : 0x17); + s32 CharacterIndex = rEVNT.ReadLong(); + rEVNT.Skip(mGame < eCorruptionProto ? 0x4 : 0x18); + return CharacterIndex; +} + +void CAnimEventLoader::LoadLoopEvent(IInputStream& rEVNT) +{ + LoadEventBase(rEVNT); + rEVNT.Skip(0x1); +} + +void CAnimEventLoader::LoadUserEvent(IInputStream& rEVNT) +{ + LoadEventBase(rEVNT); + rEVNT.Skip(0x4); + rEVNT.ReadString(); +} + +void CAnimEventLoader::LoadEffectEvent(IInputStream& rEVNT) +{ + s32 CharIndex = LoadEventBase(rEVNT); + rEVNT.Skip(mGame < eCorruptionProto ? 0x8 : 0x4); + CAssetID ParticleID(rEVNT, mGame); + mpEventData->AddEvent(CharIndex, ParticleID); + + if (mGame <= ePrime) + rEVNT.ReadString(); + else if (mGame <= eEchoes) + rEVNT.Skip(0x4); + + rEVNT.Skip(0x8); +} + +void CAnimEventLoader::LoadSoundEvent(IInputStream& rEVNT) +{ + s32 CharIndex = LoadEventBase(rEVNT); + + // Metroid Prime 1/2 + if (mGame <= eEchoes) + { + u32 SoundID = rEVNT.ReadLong() & 0xFFFF; + rEVNT.Skip(0x8); + if (mGame >= eEchoes) rEVNT.Skip(0xC); + + if (SoundID != 0xFFFF) + { + SSoundInfo SoundInfo = gpResourceStore->Project()->AudioManager()->GetSoundInfo(SoundID); + + if (SoundInfo.pAudioGroup) + mpEventData->AddEvent(CharIndex, SoundInfo.pAudioGroup->ID()); + } + } + + // Metroid Prime 3 + else + { + CAssetID SoundID(rEVNT, mGame); + mpEventData->AddEvent(CharIndex, SoundID); + rEVNT.Skip(0x8); + + for (u32 StructIdx = 0; StructIdx < 2; StructIdx++) + { + u32 StructType = rEVNT.ReadLong(); + ASSERT(StructType <= 2); + + if (StructType == 1) { - SSoundInfo SoundInfo = gpResourceStore->Project()->AudioManager()->GetSoundInfo(SoundID); - - if (SoundInfo.pAudioGroup) - mpEventData->AddEvent(CharIndex, SoundInfo.pAudioGroup->ID()); + rEVNT.Skip(4); + } + else if (StructType == 2) + { + // This is a maya spline + rEVNT.Skip(2); + u32 KnotCount = rEVNT.ReadLong(); + rEVNT.Skip(0xA * KnotCount); + rEVNT.Skip(9); } } } @@ -81,7 +134,8 @@ CAnimEventData* CAnimEventLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry * { CAnimEventLoader Loader; Loader.mpEventData = new CAnimEventData(pEntry); - Loader.LoadEvents(rEVNT, false); + Loader.mGame = ePrime; + Loader.LoadEvents(rEVNT); return Loader.mpEventData; } @@ -89,6 +143,38 @@ CAnimEventData* CAnimEventLoader::LoadAnimSetEvents(IInputStream& rANCS) { CAnimEventLoader Loader; Loader.mpEventData = new CAnimEventData(); - Loader.LoadEvents(rANCS, true); + Loader.mGame = eEchoes; + Loader.LoadEvents(rANCS); + return Loader.mpEventData; +} + +CAnimEventData* CAnimEventLoader::LoadCorruptionCharacterEventSet(IInputStream& rCHAR) +{ + CAnimEventLoader Loader; + Loader.mpEventData = new CAnimEventData(); + Loader.mGame = eCorruption; + + // Read event set header + rCHAR.Skip(0x4); // Skip animation ID + rCHAR.ReadString(); // Skip set name + + // Read effect events + u32 NumEffectEvents = rCHAR.ReadLong(); + + for (u32 EventIdx = 0; EventIdx < NumEffectEvents; EventIdx++) + { + rCHAR.ReadString(); + Loader.LoadEffectEvent(rCHAR); + } + + // Read sound events + u32 NumSoundEvents = rCHAR.ReadLong(); + + for (u32 EventIdx = 0; EventIdx < NumSoundEvents; EventIdx++) + { + rCHAR.ReadString(); + Loader.LoadSoundEvent(rCHAR); + } + return Loader.mpEventData; } diff --git a/src/Core/Resource/Factory/CAnimEventLoader.h b/src/Core/Resource/Factory/CAnimEventLoader.h index 38f4e539..91a9c7ad 100644 --- a/src/Core/Resource/Factory/CAnimEventLoader.h +++ b/src/Core/Resource/Factory/CAnimEventLoader.h @@ -7,13 +7,20 @@ class CAnimEventLoader { TResPtr mpEventData; + EGame mGame; CAnimEventLoader() {} - void LoadEvents(IInputStream& rEVNT, bool IsEchoes); + void LoadEvents(IInputStream& rEVNT); + s32 LoadEventBase(IInputStream& rEVNT); + void LoadLoopEvent(IInputStream& rEVNT); + void LoadUserEvent(IInputStream& rEVNT); + void LoadEffectEvent(IInputStream& rEVNT); + void LoadSoundEvent(IInputStream& rEVNT); public: static CAnimEventData* LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry); static CAnimEventData* LoadAnimSetEvents(IInputStream& rANCS); + static CAnimEventData* LoadCorruptionCharacterEventSet(IInputStream& rCHAR); }; #endif // CANIMEVENTLOADER_H diff --git a/src/Core/Resource/Factory/CAnimSetLoader.cpp b/src/Core/Resource/Factory/CAnimSetLoader.cpp index 007d8da7..50bbf7c1 100644 --- a/src/Core/Resource/Factory/CAnimSetLoader.cpp +++ b/src/Core/Resource/Factory/CAnimSetLoader.cpp @@ -9,13 +9,89 @@ CAnimSetLoader::CAnimSetLoader() CAnimSet* CAnimSetLoader::LoadCorruptionCHAR(IInputStream& rCHAR) { - // For now, we only read enough to fetch the model - rCHAR.Seek(0x1, SEEK_CUR); - pSet->mCharacters.resize(1); - SSetCharacter& rNode = pSet->mCharacters[0]; + pSet->mCharacters.emplace_back( SSetCharacter() );; + SSetCharacter& rChar = pSet->mCharacters.back(); + + // Character Header + rChar.ID = rCHAR.ReadByte(); + rChar.Name = rCHAR.ReadString(); + rChar.pModel = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CMDL"); + rChar.pSkin = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CSKR"); + + u32 NumOverlays = rCHAR.ReadLong(); + + for (u32 iOverlay = 0; iOverlay < NumOverlays; iOverlay++) + { + SOverlayModel Overlay; + Overlay.Type = (EOverlayType) rCHAR.ReadLong(); + Overlay.ModelID = CAssetID(rCHAR, e64Bit); + Overlay.SkinID = CAssetID(rCHAR, e64Bit); + rChar.OverlayModels.push_back(Overlay); + } + + rChar.pSkeleton = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CINF"); + rChar.AnimDataID = CAssetID(rCHAR, e64Bit); + + // PAS Database + LoadPASDatabase(rCHAR); + + // Particle Resource Data + LoadParticleResourceData(rCHAR, &rChar, 10); + + // Events + u32 NumEventSets = rCHAR.ReadLong(); + + for (u32 iSet = 0; iSet < NumEventSets; iSet++) + { + CAnimEventData *pEvents = CAnimEventLoader::LoadCorruptionCharacterEventSet(rCHAR); + pSet->mAnimEvents.push_back(pEvents); + } + + // Animations + u32 NumAnimations = rCHAR.ReadLong(); + + for (u32 AnimIdx = 0; AnimIdx < NumAnimations; AnimIdx++) + { + SAnimation Anim; + Anim.Name = rCHAR.ReadString(); + Anim.pMetaAnim = gMetaAnimFactory.LoadFromStream(rCHAR, mGame); + pSet->mAnimations.push_back(Anim); + } + + // Animation Bounds + u32 NumAnimationBounds = rCHAR.ReadLong(); + rCHAR.Skip(NumAnimationBounds * 0x20); + rCHAR.Skip(1); + + // Bool Array + u32 BoolArraySize = rCHAR.ReadLong(); + rCHAR.Skip(BoolArraySize); + + // Collision Primitives + u32 NumPrimitiveSets = rCHAR.ReadLong(); + + for (u32 SetIdx = 0; SetIdx < NumPrimitiveSets; SetIdx++) + { + rCHAR.ReadString(); + u32 NumPrimitives = rCHAR.ReadLong(); + + for (u32 PrimIdx = 0; PrimIdx < NumPrimitives; PrimIdx++) + { + rCHAR.Skip(0x34); + rCHAR.ReadString(); + rCHAR.Skip(4); + } + } + + // Sound Resources + u32 NumSounds = rCHAR.ReadLong(); + + for (u32 SoundIdx = 0; SoundIdx < NumSounds; SoundIdx++) + { + CAssetID SoundID(rCHAR, e64Bit); + rChar.SoundEffects.push_back(SoundID); + } - rNode.Name = rCHAR.ReadString(); - rNode.pModel = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CMDL"); return pSet; } @@ -23,22 +99,25 @@ CAnimSet* CAnimSetLoader::LoadReturnsCHAR(IInputStream& rCHAR) { // For now, we only read enough to fetch the model rCHAR.Seek(0x16, SEEK_CUR); - pSet->mCharacters.resize(1); - SSetCharacter& rNode = pSet->mCharacters[0]; - rNode.Name = rCHAR.ReadString(); + pSet->mCharacters.emplace_back( SSetCharacter() );; + SSetCharacter& rChar = pSet->mCharacters.back(); + + rChar.ID = 0; + rChar.Name = rCHAR.ReadString(); rCHAR.Seek(0x14, SEEK_CUR); rCHAR.ReadString(); - rNode.pModel = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CMDL"); + rChar.pModel = gpResourceStore->LoadResource(rCHAR.ReadLongLong(), "CMDL"); return pSet; } void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4) { // For now, just parse the data; don't store it - rPAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC + u32 Magic = rPAS4.ReadLong(); u32 AnimStateCount = rPAS4.ReadLong(); rPAS4.Seek(0x4, SEEK_CUR); // Skipping default anim state + ASSERT(Magic == FOURCC('PAS4')); for (u32 iState = 0; iState < AnimStateCount; iState++) { @@ -72,6 +151,41 @@ void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4) } } +void CAnimSetLoader::LoadParticleResourceData(IInputStream& rFile, SSetCharacter *pChar, u16 CharVersion) +{ + u32 ParticleCount = rFile.ReadLong(); + pChar->GenericParticles.reserve(ParticleCount); + + for (u32 iPart = 0; iPart < ParticleCount; iPart++) + pChar->GenericParticles.push_back( CAssetID(rFile, mGame) ); + + u32 SwooshCount = rFile.ReadLong(); + pChar->SwooshParticles.reserve(SwooshCount); + + for (u32 iSwoosh = 0; iSwoosh < SwooshCount; iSwoosh++) + pChar->SwooshParticles.push_back( CAssetID(rFile, mGame) ); + + if (CharVersion >= 6 && mGame <= eEchoes) rFile.Seek(0x4, SEEK_CUR); + + u32 ElectricCount = rFile.ReadLong(); + pChar->ElectricParticles.reserve(ElectricCount); + + for (u32 iElec = 0; iElec < ElectricCount; iElec++) + pChar->ElectricParticles.push_back( CAssetID(rFile, mGame) ); + + if (mGame >= eEchoes) + { + u32 SpawnCount = rFile.ReadLong(); + pChar->SpawnParticles.reserve(SpawnCount); + + for (u32 iSpawn = 0; iSpawn < SpawnCount; iSpawn++) + pChar->SpawnParticles.push_back( CAssetID(rFile, mGame) ); + } + + rFile.Seek(0x4, SEEK_CUR); + if (mGame >= eEchoes) rFile.Seek(0x4, SEEK_CUR); +} + void CAnimSetLoader::LoadAnimationSet(IInputStream& rANCS) { u16 Version = rANCS.ReadShort(); @@ -84,7 +198,7 @@ void CAnimSetLoader::LoadAnimationSet(IInputStream& rANCS) { SAnimation Anim; Anim.Name = rANCS.ReadString(); - Anim.pMetaAnim = gMetaAnimFactory.LoadFromStream(rANCS); + Anim.pMetaAnim = gMetaAnimFactory.LoadFromStream(rANCS, mGame); pSet->mAnimations.push_back(Anim); } @@ -98,11 +212,11 @@ void CAnimSetLoader::LoadAnimationSet(IInputStream& rANCS) Trans.Unknown = rANCS.ReadLong(); Trans.AnimIdA = rANCS.ReadLong(); Trans.AnimIdB = rANCS.ReadLong(); - Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS); + Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS, mGame); pSet->mTransitions.push_back(Trans); } - pSet->mpDefaultTransition = gMetaTransFactory.LoadFromStream(rANCS); + pSet->mpDefaultTransition = gMetaTransFactory.LoadFromStream(rANCS, mGame); // Additive Animations u32 NumAdditive = rANCS.ReadLong(); @@ -130,14 +244,14 @@ void CAnimSetLoader::LoadAnimationSet(IInputStream& rANCS) { SHalfTransition Trans; Trans.AnimID = rANCS.ReadLong(); - Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS); + Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS, mGame); pSet->mHalfTransitions.push_back(Trans); } } // Skipping MP1 ANIM asset list // Events - if (mVersion >= eEchoesDemo) + if (mGame >= eEchoesDemo) { u32 EventDataCount = rANCS.ReadLong(); pSet->mAnimEvents.reserve(EventDataCount); @@ -260,20 +374,6 @@ void CAnimSetLoader::ProcessPrimitives() } // ************ STATIC ************ -CAnimSet* CAnimSetLoader::LoadANCSOrCHAR(IInputStream& rFile, CResourceEntry *pEntry) -{ - if (!rFile.IsValid()) return nullptr; - u8 Test = rFile.PeekByte(); - - if (Test == 0x3 || Test == 0x5 || Test == 0x59) - return LoadCHAR(rFile, pEntry); - else if (Test == 0x0) - return LoadANCS(rFile, pEntry); - - Log::Error("Failed to determine animset format for " + rFile.GetSourceString() + "; first byte is " + TString::HexString(Test, 2)); - return nullptr; -} - CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) { if (!rANCS.IsValid()) return nullptr; @@ -287,7 +387,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) CAnimSetLoader Loader; Loader.pSet = new CAnimSet(pEntry); - Loader.mVersion = pEntry->Game(); + Loader.mGame = pEntry->Game(); u32 NodeCount = rANCS.ReadLong(); Loader.pSet->mCharacters.resize(NodeCount); @@ -296,11 +396,11 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) { SSetCharacter *pChar = &Loader.pSet->mCharacters[iNode]; - rANCS.Seek(0x4, SEEK_CUR); // Skipping node self-index - u16 Unknown1 = rANCS.ReadShort(); - if (iNode == 0 && Loader.mVersion == eUnknownGame) + pChar->ID = rANCS.ReadLong(); + u16 CharVersion = rANCS.ReadShort(); + if (iNode == 0 && Loader.mGame == eUnknownGame) { - Loader.mVersion = (Unknown1 == 0xA) ? eEchoes : ePrime; // Best version indicator we know of unfortunately + Loader.mGame = (CharVersion == 0xA) ? eEchoes : ePrime; } pChar->Name = rANCS.ReadString(); pChar->pModel = gpResourceStore->LoadResource(rANCS.ReadLong(), "CMDL"); @@ -315,7 +415,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) for (u32 iAnim = 0; iAnim < AnimCount; iAnim++) { rANCS.Seek(0x4, SEEK_CUR); - if (Loader.mVersion == ePrime) rANCS.Seek(0x1, SEEK_CUR); + if (Loader.mGame == ePrime) rANCS.Seek(0x1, SEEK_CUR); rANCS.ReadString(); } @@ -323,37 +423,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) Loader.LoadPASDatabase(rANCS); // Particles - u32 ParticleCount = rANCS.ReadLong(); - pChar->GenericParticles.reserve(ParticleCount); - - for (u32 iPart = 0; iPart < ParticleCount; iPart++) - pChar->GenericParticles.push_back( CAssetID(rANCS, e32Bit) ); - - u32 SwooshCount = rANCS.ReadLong(); - pChar->SwooshParticles.reserve(SwooshCount); - - for (u32 iSwoosh = 0; iSwoosh < SwooshCount; iSwoosh++) - pChar->SwooshParticles.push_back( CAssetID(rANCS, e32Bit) ); - - if (Unknown1 != 5) rANCS.Seek(0x4, SEEK_CUR); - - u32 ElectricCount = rANCS.ReadLong(); - pChar->ElectricParticles.reserve(ElectricCount); - - for (u32 iElec = 0; iElec < ElectricCount; iElec++) - pChar->ElectricParticles.push_back( CAssetID(rANCS, e32Bit) ); - - if (Loader.mVersion == eEchoes) - { - u32 SpawnCount = rANCS.ReadLong(); - pChar->SpawnParticles.reserve(SpawnCount); - - for (u32 iSpawn = 0; iSpawn < SpawnCount; iSpawn++) - pChar->SpawnParticles.push_back( CAssetID(rANCS, e32Bit) ); - } - - rANCS.Seek(0x4, SEEK_CUR); - if (Loader.mVersion == eEchoes) rANCS.Seek(0x4, SEEK_CUR); + Loader.LoadParticleResourceData(rANCS, pChar, CharVersion); u32 AnimCount2 = rANCS.ReadLong(); for (u32 iAnim = 0; iAnim < AnimCount2; iAnim++) @@ -375,13 +445,17 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) CAssetID ParticleID(rANCS, e32Bit); if (ParticleID.IsValid()) pChar->EffectParticles.push_back(ParticleID); - if (Loader.mVersion == ePrime) rANCS.ReadString(); - if (Loader.mVersion == eEchoes) rANCS.Seek(0x4, SEEK_CUR); + if (Loader.mGame == ePrime) rANCS.ReadString(); + if (Loader.mGame == eEchoes) rANCS.Seek(0x4, SEEK_CUR); rANCS.Seek(0xC, SEEK_CUR); } } - pChar->IceModel = CAssetID(rANCS, e32Bit); - pChar->IceSkin = CAssetID(rANCS, e32Bit); + + SOverlayModel Overlay; + Overlay.Type = eOT_Frozen; + Overlay.ModelID = CAssetID(rANCS, e32Bit); + Overlay.SkinID = CAssetID(rANCS, e32Bit); + pChar->OverlayModels.push_back(Overlay); u32 AnimIndexCount = rANCS.ReadLong(); @@ -391,7 +465,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry) pChar->UsedAnimationIndices.insert(AnimIndex); } - if (Loader.mVersion == eEchoes) + if (Loader.mGame == eEchoes) { pChar->SpatialPrimitives = rANCS.ReadLong(); rANCS.Seek(0x1, SEEK_CUR); @@ -416,14 +490,14 @@ CAnimSet* CAnimSetLoader::LoadCHAR(IInputStream& rCHAR, CResourceEntry *pEntry) if (Check == 0x5 || Check == 0x3) { - Loader.mVersion = eCorruption; + Loader.mGame = eCorruption; Loader.pSet = new CAnimSet(pEntry); return Loader.LoadCorruptionCHAR(rCHAR); } if (Check == 0x59) { - Loader.mVersion = eReturns; + Loader.mGame = eReturns; Loader.pSet = new CAnimSet(pEntry); return Loader.LoadReturnsCHAR(rCHAR); } @@ -431,3 +505,48 @@ CAnimSet* CAnimSetLoader::LoadCHAR(IInputStream& rCHAR, CResourceEntry *pEntry) Log::FileError(rCHAR.GetSourceString(), "CHAR has invalid first byte: " + TString::HexString(Check, 2)); return nullptr; } + +CSourceAnimData* CAnimSetLoader::LoadSAND(IInputStream& rSAND, CResourceEntry *pEntry) +{ + if (!rSAND.IsValid()) return nullptr; + + // We only care about the transitions right now + CSourceAnimData *pData = new CSourceAnimData(pEntry); + + u16 Unknown = rSAND.ReadShort(); // probably version + ASSERT(Unknown == 0); + + // Transitions + u32 NumTransitions = rSAND.ReadLong(); + + for (u32 TransitionIdx = 0; TransitionIdx < NumTransitions; TransitionIdx++) + { + u8 UnkByte = rSAND.ReadByte(); + ASSERT(UnkByte == 0); + + CSourceAnimData::STransition Transition; + Transition.AnimA = CAssetID(rSAND, e64Bit); + Transition.AnimB = CAssetID(rSAND, e64Bit); + Transition.pTransition = gMetaTransFactory.LoadFromStream(rSAND, pEntry->Game()); + pData->mTransitions.push_back(Transition); + } + + // Half Transitions + u32 NumHalfTransitions = rSAND.ReadLong(); + + for (u32 HalfIdx = 0; HalfIdx < NumHalfTransitions; HalfIdx++) + { + u8 UnkByte = rSAND.ReadByte(); + ASSERT(UnkByte == 0); + + CSourceAnimData::SHalfTransition HalfTrans; + HalfTrans.Anim = CAssetID(rSAND, e64Bit); + HalfTrans.pTransition = gMetaTransFactory.LoadFromStream(rSAND, pEntry->Game()); + pData->mHalfTransitions.push_back(HalfTrans); + } + + // Default Transition + pData->mpDefaultTransition = gMetaTransFactory.LoadFromStream(rSAND, pEntry->Game()); + + return pData; +} diff --git a/src/Core/Resource/Factory/CAnimSetLoader.h b/src/Core/Resource/Factory/CAnimSetLoader.h index 7ee1d9f1..296cdb6e 100644 --- a/src/Core/Resource/Factory/CAnimSetLoader.h +++ b/src/Core/Resource/Factory/CAnimSetLoader.h @@ -2,25 +2,27 @@ #define CCHARACTERLOADER_H #include "Core/Resource/Animation/CAnimSet.h" +#include "Core/Resource/Animation/CSourceAnimData.h" #include class CAnimSetLoader { TResPtr pSet; - EGame mVersion; + EGame mGame; CAnimSetLoader(); CAnimSet* LoadCorruptionCHAR(IInputStream& rCHAR); CAnimSet* LoadReturnsCHAR(IInputStream& rCHAR); void LoadPASDatabase(IInputStream& rPAS4); + void LoadParticleResourceData(IInputStream& rFile, SSetCharacter *pChar, u16 Version); void LoadAnimationSet(IInputStream& rANCS); void ProcessPrimitives(); public: - static CAnimSet* LoadANCSOrCHAR(IInputStream& rFile, CResourceEntry *pEntry); static CAnimSet* LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry); static CAnimSet* LoadCHAR(IInputStream& rCHAR, CResourceEntry *pEntry); + static CSourceAnimData* LoadSAND(IInputStream& rSAND, CResourceEntry *pEntry); }; #endif // CCHARACTERLOADER_H diff --git a/src/Core/Resource/Factory/CAnimationLoader.cpp b/src/Core/Resource/Factory/CAnimationLoader.cpp index afa673eb..d249b3fd 100644 --- a/src/Core/Resource/Factory/CAnimationLoader.cpp +++ b/src/Core/Resource/Factory/CAnimationLoader.cpp @@ -466,7 +466,7 @@ CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM, CResourceEntry *pEnt { // MP3/DKCR unsupported if (pEntry->Game() > eEchoes) - return nullptr; + return new CAnimation(pEntry); u32 CompressionType = rANIM.ReadLong(); diff --git a/src/Core/Resource/Factory/CResourceFactory.h b/src/Core/Resource/Factory/CResourceFactory.h index 7dae470d..bc264b80 100644 --- a/src/Core/Resource/Factory/CResourceFactory.h +++ b/src/Core/Resource/Factory/CResourceFactory.h @@ -49,6 +49,7 @@ public: case eScan: return new CScan(pEntry); case eSkeleton: return new CSkeleton(pEntry); case eSkin: return new CSkin(pEntry); + case eSourceAnimData: return new CSourceAnimData(pEntry); case eStaticGeometryMap: return new CPoiToWorld(pEntry); case eStringList: return new CStringList(pEntry); case eStringTable: return new CStringTable(pEntry); @@ -88,6 +89,7 @@ public: case eScan: pRes = CScanLoader::LoadSCAN(rInput, pEntry); break; case eSkeleton: pRes = CSkeletonLoader::LoadCINF(rInput, pEntry); break; case eSkin: pRes = CSkinLoader::LoadCSKR(rInput, pEntry); break; + case eSourceAnimData: pRes = CAnimSetLoader::LoadSAND(rInput, pEntry); break; case eStateMachine2: pRes = CUnsupportedFormatLoader::LoadFSM2(rInput, pEntry); break; case eStaticGeometryMap: pRes = CPoiToWorldLoader::LoadEGMC(rInput, pEntry); break; case eStringList: pRes = CAudioGroupLoader::LoadSTLC(rInput, pEntry); break; diff --git a/src/Core/Resource/Factory/CScanLoader.cpp b/src/Core/Resource/Factory/CScanLoader.cpp index e25bb736..f932407b 100644 --- a/src/Core/Resource/Factory/CScanLoader.cpp +++ b/src/Core/Resource/Factory/CScanLoader.cpp @@ -44,7 +44,10 @@ CScan* CScanLoader::LoadScanMP2(IInputStream& rSCAN) return nullptr; } - rSCAN.Seek(0x6, SEEK_CUR); + u16 InstanceSize = rSCAN.ReadShort(); + u32 InstanceEnd = rSCAN.Tell() + InstanceSize; + rSCAN.Seek(0x4, SEEK_CUR); + u16 NumConnections = rSCAN.ReadShort(); if (NumConnections > 0) { Log::FileWarning(rSCAN.GetSourceString(), ScanInfoStart, "SNFO object in SCAN has connections"); @@ -68,6 +71,7 @@ CScan* CScanLoader::LoadScanMP2(IInputStream& rSCAN) LoadParamsMP2(rSCAN, NumProperties); break; case 0x12: + case 0x15: case 0x16: mpScan = new CScan(mpEntry); LoadParamsMP3(rSCAN, NumProperties); @@ -77,6 +81,20 @@ CScan* CScanLoader::LoadScanMP2(IInputStream& rSCAN) return nullptr; } + // Load MP3 dependency list + if (mpScan->Game() == eCorruption) + { + rSCAN.GoTo(InstanceEnd); + u32 NumDeps = rSCAN.ReadLong(); + + for (u32 DepIdx = 0; DepIdx < NumDeps; DepIdx++) + { + rSCAN.Skip(4); + CAssetID ID(rSCAN, mpScan->Game()); + mpScan->mDependencyList.push_back(ID); + } + } + return mpScan; } diff --git a/src/Core/Resource/Factory/CSkeletonLoader.cpp b/src/Core/Resource/Factory/CSkeletonLoader.cpp index 80cf0374..24599778 100644 --- a/src/Core/Resource/Factory/CSkeletonLoader.cpp +++ b/src/Core/Resource/Factory/CSkeletonLoader.cpp @@ -32,6 +32,10 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF, CResourceEntry *pEntry Loader.mpSkeleton = pSkel; EGame Game = pEntry->Game(); + // We don't support DKCR CINF right now + if (rCINF.PeekLong() == 0x9E220006) + return pSkel; + u32 NumBones = rCINF.ReadLong(); pSkel->mBones.reserve(NumBones); @@ -61,7 +65,7 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF, CResourceEntry *pEntry u32 Check = rCINF.PeekLong(); Game = ((Check > 100 || Check == 0) ? eEchoes : ePrime); } - if (Game == eEchoes) + if (Game >= eEchoes) { pBone->mRotation = CQuaternion(rCINF); pBone->mLocalRotation = CQuaternion(rCINF); diff --git a/src/Core/Resource/Factory/CSkinLoader.cpp b/src/Core/Resource/Factory/CSkinLoader.cpp index f8153c69..9d8152f2 100644 --- a/src/Core/Resource/Factory/CSkinLoader.cpp +++ b/src/Core/Resource/Factory/CSkinLoader.cpp @@ -5,9 +5,13 @@ CSkin* CSkinLoader::LoadCSKR(IInputStream& rCSKR, CResourceEntry *pEntry) { if (!rCSKR.IsValid()) return nullptr; + CSkin *pSkin = new CSkin(pEntry); + + // We don't support MP3/DKCR CSKR yet + if (rCSKR.PeekLong() == FOURCC('SKIN')) + return pSkin; u32 NumVertexGroups = rCSKR.ReadLong(); - CSkin *pSkin = new CSkin(pEntry); pSkin->mVertGroups.resize(NumVertexGroups); for (u32 iGrp = 0; iGrp < NumVertexGroups; iGrp++) diff --git a/src/Core/Resource/Script/CScriptTemplate.cpp b/src/Core/Resource/Script/CScriptTemplate.cpp index 79d318d0..ea887af7 100644 --- a/src/Core/Resource/Script/CScriptTemplate.cpp +++ b/src/Core/Resource/Script/CScriptTemplate.cpp @@ -179,7 +179,8 @@ CResource* CScriptTemplate::FindDisplayAsset(CPropertyStruct *pProperties, u32& if (pRes) { - rOutCharIndex = (it->ForceNodeIndex >= 0 && it->ForceNodeIndex < (s32) static_cast(pRes)->NumCharacters() ? it->ForceNodeIndex : pChar->Get().CharacterIndex()); + u32 MaxNumChars = static_cast(pRes)->NumCharacters(); + rOutCharIndex = (it->ForceNodeIndex >= 0 && it->ForceNodeIndex < (s32) MaxNumChars ? it->ForceNodeIndex : pChar->Get().CharacterIndex()); rOutAnimIndex = pChar->Get().AnimIndex(); } } diff --git a/src/Core/Scene/CScriptNode.cpp b/src/Core/Scene/CScriptNode.cpp index fd05821d..07ddfc9d 100644 --- a/src/Core/Scene/CScriptNode.cpp +++ b/src/Core/Scene/CScriptNode.cpp @@ -4,6 +4,7 @@ #include "Core/Render/CDrawUtil.h" #include "Core/Render/CGraphics.h" #include "Core/Render/CRenderer.h" +#include "Core/Resource/Animation/CAnimSet.h" #include "Core/Resource/Script/CMasterTemplate.h" #include "Core/Resource/Script/CScriptLayer.h" #include "Core/ScriptExtra/CScriptExtra.h" diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index f8e39ed4..ca754e0c 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -9,6 +9,7 @@ #include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WIntegralSpinBox.h" +#include #include #include diff --git a/src/FileIO/IOutputStream.cpp b/src/FileIO/IOutputStream.cpp index c08c9cb1..bc870ca1 100644 --- a/src/FileIO/IOutputStream.cpp +++ b/src/FileIO/IOutputStream.cpp @@ -56,7 +56,7 @@ void IOutputStream::WriteString(const std::string& rkVal) for (unsigned int i = 0; i < rkVal.size(); i++) WriteByte(rkVal[i]); - if ((rkVal.empty()) || (rkVal.back() != '\0')) + if (rkVal.empty() || rkVal.back() != '\0') WriteByte(0); } @@ -65,7 +65,7 @@ void IOutputStream::WriteString(const std::string& rkVal, unsigned long Count, b for (unsigned int iChr = 0; iChr < Count; iChr++) WriteByte(rkVal[iChr]); - if (Terminate && (rkVal[Count-1] != '\0')) + if (Terminate && (Count == 0 || rkVal[Count-1] != '\0')) WriteByte(0); } @@ -80,7 +80,7 @@ void IOutputStream::WriteWideString(const std::wstring& rkVal) for (unsigned int iChr = 0; iChr < rkVal.size(); iChr++) WriteShort(rkVal[iChr]); - if ((!rkVal.empty()) && (rkVal.back() != '\0')) + if (rkVal.empty() || rkVal.back() != '\0') WriteShort(0); } @@ -89,7 +89,7 @@ void IOutputStream::WriteWideString(const std::wstring& rkVal, unsigned long Cou for (unsigned int iChr = 0; iChr < Count; iChr++) WriteShort(rkVal[iChr]); - if (Terminate && (rkVal[Count-1] != 0)) + if (Terminate && (Count == 0 || rkVal[Count-1] != 0)) WriteShort(0); } @@ -99,7 +99,6 @@ void IOutputStream::WriteSizedWideString(const std::wstring& rkVal) WriteBytes(rkVal.data(), rkVal.size() * 2); } - bool IOutputStream::GoTo(long Address) { return Seek(Address, SEEK_SET);