Added support for loading meta-animations and meta-transitions (yay! animation exclusion doesn't crash anymore!)

This commit is contained in:
parax0 2016-10-27 07:18:59 -06:00
parent 595e4b931e
commit 040caca896
17 changed files with 692 additions and 284 deletions

View File

@ -211,7 +211,9 @@ HEADERS += \
Resource/Animation/CAnimEventData.h \
Resource/Animation/CAnimSet.h \
Resource/Animation/CSkeleton.h \
Resource/Animation/CSkin.h
Resource/Animation/CSkin.h \
Resource/Animation/IMetaTransition.h \
Resource/Animation/IMetaAnimation.h
# Source Files
SOURCES += \
@ -310,4 +312,6 @@ SOURCES += \
Resource/Factory/CAnimEventLoader.cpp \
Resource/Animation/CAnimation.cpp \
Resource/Animation/CAnimationParameters.cpp \
Resource/Animation/CSkeleton.cpp
Resource/Animation/CSkeleton.cpp \
Resource/Animation/IMetaAnimation.cpp \
Resource/Animation/IMetaTransition.cpp

View File

@ -186,7 +186,7 @@ EDependencyNodeType CSetCharacterDependency::Type() const
void CSetCharacterDependency::Serialize(IArchive& rArc)
{
rArc << SERIAL("SetIndex", mSetIndex)
rArc << SERIAL("CharSetIndex", mCharSetIndex)
<< SERIAL_ABSTRACT_CONTAINER("Children", mChildren, "Child", &gDependencyNodeFactory);
}
@ -234,8 +234,8 @@ void CSetAnimationDependency::Serialize(IArchive& rArc)
CSetAnimationDependency* CSetAnimationDependency::BuildTree(const CAnimSet *pkOwnerSet, u32 AnimIndex)
{
const SSetAnimation *pkAnim = pkOwnerSet->Animation(AnimIndex);
CSetAnimationDependency *pTree = new CSetAnimationDependency;
const SAnimation *pkAnim = pkOwnerSet->Animation(AnimIndex);
// Find relevant character indices
for (u32 iChar = 0; iChar < pkOwnerSet->NumCharacters(); iChar++)
@ -246,15 +246,21 @@ CSetAnimationDependency* CSetAnimationDependency::BuildTree(const CAnimSet *pkOw
pTree->mCharacterIndices.insert(iChar);
}
// Add dependencies. In MP2 animation event data is not a standalone resource.
pTree->AddDependency(pkAnim->pAnim);
// Add primitive dependencies. In MP2 animation event data is not a standalone resource.
std::set<CAnimPrimitive> UsedPrimitives;
pkAnim->pMetaAnim->GetUniquePrimitives(UsedPrimitives);
if (pkAnim->pEventData)
for (auto Iter = UsedPrimitives.begin(); Iter != UsedPrimitives.end(); Iter++)
{
if (pkAnim->pEventData->Entry())
pTree->AddDependency(pkAnim->pEventData);
else
pkAnim->pEventData->AddDependenciesToTree(pTree);
const CAnimPrimitive& rkPrim = *Iter;
pTree->AddDependency(rkPrim.Animation());
if (pkOwnerSet->Game() >= eEchoesDemo)
{
CAnimEventData *pEvents = pkOwnerSet->AnimationEventData(rkPrim.ID());
ASSERT(pEvents && !pEvents->Entry());
pEvents->AddDependenciesToTree(pTree);
}
}
return pTree;

View File

@ -11,7 +11,6 @@ class CScriptObject;
class CPropertyStruct;
class CAnimSet;
struct SSetCharacter;
struct SSetAnimation;
// Group of node classes forming a tree of cached resource dependencies.
enum EDependencyNodeType
@ -154,17 +153,17 @@ protected:
class CSetCharacterDependency : public CDependencyTree
{
protected:
u32 mSetIndex;
u32 mCharSetIndex;
public:
CSetCharacterDependency() : CDependencyTree() {}
CSetCharacterDependency(u32 SetIndex) : CDependencyTree(), mSetIndex(SetIndex) {}
CSetCharacterDependency(u32 SetIndex) : CDependencyTree(), mCharSetIndex(SetIndex) {}
virtual EDependencyNodeType Type() const;
virtual void Serialize(IArchive& rArc);
// Accessors
inline u32 SetIndex() const { return mSetIndex; }
inline u32 CharSetIndex() const { return mCharSetIndex; }
// Static
static CSetCharacterDependency* BuildTree(const CAnimSet *pkOwnerSet, u32 CharIndex);

View File

@ -268,7 +268,7 @@ void CPackageDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurE
else if (Type == eDNT_SetCharacter)
{
CSetCharacterDependency *pChar = static_cast<CSetCharacterDependency*>(pNode);
ParseChildren = mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, pChar->SetIndex()) || mIsPlayerActor;
ParseChildren = mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, pChar->CharSetIndex()) || mIsPlayerActor;
}
// Set animations should only be added if they're being used by at least one used character
@ -438,8 +438,8 @@ void CAreaDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntr
const u32 kEmptySuitIndex = (mGame >= eEchoesDemo ? 3 : 5);
CSetCharacterDependency *pChar = static_cast<CSetCharacterDependency*>(pNode);
u32 SetIndex = pChar->SetIndex();
ParseChildren = mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, pChar->SetIndex()) || (mIsPlayerActor && SetIndex == kEmptySuitIndex);
u32 SetIndex = pChar->CharSetIndex();
ParseChildren = mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, pChar->CharSetIndex()) || (mIsPlayerActor && SetIndex == kEmptySuitIndex);
}
else if (Type == eDNT_SetAnimation)

View File

@ -5,6 +5,8 @@
#include "CAnimEventData.h"
#include "CSkeleton.h"
#include "CSkin.h"
#include "IMetaAnimation.h"
#include "IMetaTransition.h"
#include "Core/Resource/CDependencyGroup.h"
#include "Core/Resource/CResource.h"
#include "Core/Resource/TResPtr.h"
@ -13,6 +15,34 @@
#include <vector>
// Animation structures
struct SAdditiveAnim
{
u32 AnimID;
float FadeInTime;
float FadeOutTime;
};
struct SAnimation
{
TString Name;
IMetaAnimation *pMetaAnim;
};
struct STransition
{
u32 Unknown;
u32 AnimIdA;
u32 AnimIdB;
IMetaTransition *pMetaTrans;
};
struct SHalfTransition
{
u32 AnimID;
IMetaTransition *pMetaTrans;
};
struct SSetCharacter
{
TString Name;
@ -30,36 +60,37 @@ struct SSetCharacter
std::set<u32> UsedAnimationIndices;
};
struct SSetAnimation
{
TString Name;
TResPtr<CAnimation> pAnim;
TResPtr<CAnimEventData> pEventData;
};
class CAnimSet : public CResource
{
DECLARE_RESOURCE_TYPE(eAnimSet)
friend class CAnimSetLoader;
// Character Set
std::vector<SSetCharacter> mCharacters;
std::vector<SSetAnimation> mAnimations;
// Animation Set
std::vector<CAnimPrimitive> mAnimPrimitives;
std::vector<SAnimation> mAnimations;
std::vector<STransition> mTransitions;
IMetaTransition *mpDefaultTransition;
std::vector<SAdditiveAnim> mAdditiveAnims;
float mDefaultAdditiveFadeIn;
float mDefaultAdditiveFadeOut;
std::vector<SHalfTransition> mHalfTransitions;
std::vector<CAnimEventData*> mAnimEvents; // note: these are for MP2, where event data isn't a standalone resource; these are owned by the animset
public:
CAnimSet(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
~CAnimSet()
{
// note: in MP2, event data isn't a standalone resource, so it's owned by the animset; therefore we need to delete it manually
// For MP2, anim events need to be cleaned up manually
if (Game() >= eEchoesDemo)
{
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
for (u32 iEvent = 0; iEvent < mAnimEvents.size(); iEvent++)
{
SSetAnimation& rAnim = mAnimations[iAnim];
CAnimEventData *pEvents = rAnim.pEventData;
ASSERT(pEvents && !pEvents->Entry());
rAnim.pEventData = nullptr; // make sure TResPtr destructor doesn't attempt to access
delete pEvents;
ASSERT(mAnimEvents[iEvent] && !mAnimEvents[iEvent]->Entry());
delete mAnimEvents[iEvent];
}
}
}
@ -86,11 +117,48 @@ public:
return pTree;
}
CAnimation* FindAnimationAsset(u32 AnimID) const
{
if (AnimID >= 0 && AnimID < mAnimPrimitives.size())
{
CAnimPrimitive Prim = mAnimPrimitives[AnimID];
return Prim.Animation();
}
return nullptr;
}
// Accessors
inline u32 NumCharacters() const { return mCharacters.size(); }
inline u32 NumAnimations() const { return mAnimations.size(); }
inline const SSetCharacter* Character(u32 Index) const { ASSERT(Index >= 0 && Index < NumCharacters()); return &mCharacters[Index]; }
inline const SSetAnimation* Animation(u32 Index) const { ASSERT(Index >= 0 && Index < NumAnimations()); return &mAnimations[Index]; }
inline u32 NumCharacters() const { return mCharacters.size(); }
inline u32 NumAnimations() const { return mAnimations.size(); }
inline const SSetCharacter* Character(u32 Index) const
{
ASSERT(Index >= 0 && Index < NumCharacters());
return &mCharacters[Index];
}
inline const SAnimation* Animation(u32 Index) const
{
ASSERT(Index >= 0 && Index < NumAnimations());
return &mAnimations[Index];
}
CAnimEventData* AnimationEventData(u32 Index) const
{
ASSERT(Index >= 0 && Index < NumAnimations());
if (Game() <= ePrime)
{
const CAnimPrimitive& rkPrim = mAnimPrimitives[Index];
return rkPrim.Animation() ? rkPrim.Animation()->EventData() : nullptr;
}
else
{
return (Index < mAnimEvents.size() ? mAnimEvents[Index] : nullptr);
}
}
};
#endif // CCHARACTERSET_H

View File

@ -19,7 +19,7 @@ CAnimation::CAnimation(CResourceEntry *pEntry /*= 0*/)
CDependencyTree* CAnimation::BuildDependencyTree() const
{
CDependencyTree *pTree = new CDependencyTree(ID());
pTree->AddDependency(mEventData);
pTree->AddDependency(mpEventData);
return pTree;
}

View File

@ -2,6 +2,8 @@
#define CANIMATION_H
#include "Core/Resource/CResource.h"
#include "Core/Resource/TResPtr.h"
#include "Core/Resource/Animation/CAnimEventData.h"
#include <Math/CQuaternion.h>
#include <Math/CVector3f.h>
#include <vector>
@ -31,7 +33,7 @@ class CAnimation : public CResource
};
SBoneChannelInfo mBoneInfo[100];
CAssetID mEventData;
TResPtr<CAnimEventData> mpEventData;
public:
CAnimation(CResourceEntry *pEntry = 0);
@ -39,9 +41,10 @@ public:
void EvaluateTransform(float Time, u32 BoneID, CVector3f *pOutTranslation, CQuaternion *pOutRotation, CVector3f *pOutScale) const;
bool HasTranslation(u32 BoneID) const;
inline float Duration() const { return mDuration; }
inline u32 NumKeys() const { return mNumKeys; }
inline float TickInterval() const { return mTickInterval; }
inline float Duration() const { return mDuration; }
inline u32 NumKeys() const { return mNumKeys; }
inline float TickInterval() const { return mTickInterval; }
inline CAnimEventData* EventData() const { return mpEventData; }
};
#endif // CANIMATION_H

View File

@ -0,0 +1,137 @@
#include "IMetaAnimation.h"
// ************ CMetaAnimFactory ************
CMetaAnimFactory gMetaAnimFactory;
IMetaAnimation* CMetaAnimFactory::LoadFromStream(IInputStream& rInput)
{
EMetaAnimationType Type = (EMetaAnimationType) rInput.ReadLong();
switch (Type)
{
case eMAT_Play:
return new CMetaAnimPlay(rInput);
case eMAT_Blend:
case eMAT_PhaseBlend:
return new CMetaAnimBlend(Type, rInput);
case eMAT_Random:
return new CMetaAnimRandom(rInput);
case eMAT_Sequence:
return new CMetaAnimSequence(rInput);
default:
Log::Error("Unrecognized meta-animation type: " + TString::FromInt32(Type, 0, 10));
return nullptr;
}
}
// ************ CMetaAnimationPlay ************
CMetaAnimPlay::CMetaAnimPlay(IInputStream& rInput)
{
mPrimitive = CAnimPrimitive(rInput);
mUnknownA = rInput.ReadFloat();
mUnknownB = rInput.ReadLong();
}
EMetaAnimationType CMetaAnimPlay::Type() const
{
return eMAT_Play;
}
void CMetaAnimPlay::GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
{
rPrimSet.insert(mPrimitive);
}
// ************ CMetaAnimBlend ************
CMetaAnimBlend::CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput)
{
ASSERT(Type == eMAT_Blend || Type == eMAT_PhaseBlend);
mType = Type;
mpMetaAnimA = gMetaAnimFactory.LoadFromStream(rInput);
mpMetaAnimB = gMetaAnimFactory.LoadFromStream(rInput);
mUnknownA = rInput.ReadFloat();
mUnknownB = rInput.ReadBool();
}
CMetaAnimBlend::~CMetaAnimBlend()
{
delete mpMetaAnimA;
delete mpMetaAnimB;
}
EMetaAnimationType CMetaAnimBlend::Type() const
{
return mType;
}
void CMetaAnimBlend::GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
{
mpMetaAnimA->GetUniquePrimitives(rPrimSet);
mpMetaAnimB->GetUniquePrimitives(rPrimSet);
}
// ************ CMetaAnimRandom ************
CMetaAnimRandom::CMetaAnimRandom(IInputStream& rInput)
{
u32 NumPairs = rInput.ReadLong();
mProbabilityPairs.reserve(NumPairs);
for (u32 iAnim = 0; iAnim < NumPairs; iAnim++)
{
SAnimProbabilityPair Pair;
Pair.pAnim = gMetaAnimFactory.LoadFromStream(rInput);
Pair.Probability = rInput.ReadLong();
mProbabilityPairs.push_back(Pair);
}
}
CMetaAnimRandom::~CMetaAnimRandom()
{
for (u32 iPair = 0; iPair < mProbabilityPairs.size(); iPair++)
delete mProbabilityPairs[iPair].pAnim;
}
EMetaAnimationType CMetaAnimRandom::Type() const
{
return eMAT_Random;
}
void CMetaAnimRandom::GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
{
for (u32 iPair = 0; iPair < mProbabilityPairs.size(); iPair++)
mProbabilityPairs[iPair].pAnim->GetUniquePrimitives(rPrimSet);
}
// ************ CMetaAnimSequence ************
CMetaAnimSequence::CMetaAnimSequence(IInputStream& rInput)
{
u32 NumAnims = rInput.ReadLong();
mAnimations.reserve(NumAnims);
for (u32 iAnim = 0; iAnim < NumAnims; iAnim++)
{
IMetaAnimation *pAnim = gMetaAnimFactory.LoadFromStream(rInput);
mAnimations.push_back(pAnim);
}
}
CMetaAnimSequence::~CMetaAnimSequence()
{
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
delete mAnimations[iAnim];
}
EMetaAnimationType CMetaAnimSequence::Type() const
{
return eMAT_Sequence;
}
void CMetaAnimSequence::GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
{
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
mAnimations[iAnim]->GetUniquePrimitives(rPrimSet);
}

View File

@ -9,62 +9,98 @@ enum EMetaAnimationType
{
eMAT_Play = 0,
eMAT_Blend = 1,
eMAT_PhaseBlend = 2, // note: structure shared with eMAT_Blend, differences are currently unknown
eMAT_PhaseBlend = 2, // note: structure shared with eMAT_Blend
eMAT_Random = 3,
eMAT_Sequence = 4
};
// Factory class
class CMetaAnimFactory
{
public:
class IMetaAnimation* LoadFromStream(IInputStream& rInput);
};
extern CMetaAnimFactory gMetaAnimFactory;
// Animation primitive class
class CAnimPrimitive
{
TResPtr<CAnimation> mpAnim;
u32 mID;
TString mName;
public:
CAnimPrimitive() : mID(0) {}
CAnimPrimitive(IInputStream& rInput)
{
mpAnim = gpResourceStore->LoadResource(rInput.ReadLong(), "ANIM");
mID = rInput.ReadLong();
mName = rInput.ReadString();
}
inline bool operator==(const CAnimPrimitive& rkRight) const { return mID == rkRight.mID; }
inline bool operator< (const CAnimPrimitive& rkRight) const { return mID < rkRight.mID; }
// Accessors
CAnimation* Animation() const { return mpAnim; }
u32 ID() const { return mID; }
TString Name() const { return mName; }
};
// Base MetaAnimation interface
class IMetaAnimation
{
public:
IMetaAnimation() {}
virtual ~IMetaAnimation() {}
virtual EMetaAnimationType Type() const = 0;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const = 0;
// Static
static IMetaAnimation* LoadFromStream(IInputStream& rInput);
};
// CMetaAnimPlay - plays an animation
class CMetaAnimPlay : public IMetaAnimation
{
protected:
TString mName;
EMetaAnimationType mType;
CAnimPrimitive mPrimitive;
float mUnknownA;
u32 mUnknownB;
public:
IMetaAnimation(EMetaAnimationType Type)
: mType(Type) {}
virtual ~IMetaAnimation() {}
CMetaAnimPlay(IInputStream& rInput);
virtual EMetaAnimationType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
// Accessors
inline void SetName(const TString& rkName) { mName = rkName; }
inline TString Name() const { return mName; }
inline EMetaAnimationType Type() const { return mType; }
inline CAnimPrimitive Primitive() const { return mPrimitive; }
inline float UnknownA() const { return mUnknownA; }
inline u32 UnknownB() const { return mUnknownB; }
};
// CMetaAnimationPlay - plays an animation
class CMetaAnimationPlay : public IMetaAnimation
// CMetaAnimBlend - blend between two animations
class CMetaAnimBlend : public IMetaAnimation
{
protected:
TResPtr<CAnimation> mpAnim;
EMetaAnimationType mType;
IMetaAnimation *mpMetaAnimA;
IMetaAnimation *mpMetaAnimB;
float mUnknownA;
bool mUnknownB;
public:
CMetaAnimationPlay(CAnimation *pAnim)
: IMetaAnimation(eMAT_Play), mpAnim(pAnim) {}
CMetaAnimBlend(EMetaAnimationType Type, IInputStream& rInput);
~CMetaAnimBlend();
virtual EMetaAnimationType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
inline CAnimation* GetPlayAnimation() const { return mpAnim; }
};
// CMetaAnimationBlend - blend between two animations
class CMetaAnimationBlend : public IMetaAnimation
{
protected:
IMetaAnimation *mpAnimA;
IMetaAnimation *mpAnimB;
public:
CMetaAnimationBlend(IMetaAnimation *pAnimA, IMetaAnimation *pAnimB)
: IMetaAnimation(eMAT_Blend), mpAnimA(pAnimA), mpAnimB(pAnimB) {}
~CMetaAnimationBlend()
{
delete mpAnimA;
delete mpAnimB;
}
inline IMetaAnimation* BlendAnimationA() const { return mpAnimA; }
inline IMetaAnimation* BlendAnimationB() const { return mpAnimB; }
// Accessors
inline IMetaAnimation* BlendAnimationA() const { return mpMetaAnimA; }
inline IMetaAnimation* BlendAnimationB() const { return mpMetaAnimB; }
inline float UnknownA() const { return mUnknownA; }
inline bool UnknownB() const { return mUnknownB; }
};
// SAnimProbabilityPair - structure used by CMetaAnimationRandom to associate an animation with a probability value
@ -74,14 +110,30 @@ struct SAnimProbabilityPair
u32 Probability;
};
// CMetaAnimationRandom - play random animation
class CMetaAnimationRandom : public IMetaAnimation
// CMetaAnimRandom - play random animation
class CMetaAnimRandom : public IMetaAnimation
{
protected:
std::vector<SAnimProbabilityPair> mProbabilityPairs;
public:
CMetaAnimRandom(IInputStream& rInput);
~CMetaAnimRandom();
virtual EMetaAnimationType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
};
// CMetaAnim - play a series of animations in sequence
class CMetaAnimSequence : public IMetaAnimation
{
protected:
std::vector<IMetaAnimation*> mAnimations;
public:
CMetaAnimSequence(IInputStream& rInput);
~CMetaAnimSequence();
virtual EMetaAnimationType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
};
#endif // IMETAANIMATION

View File

@ -0,0 +1,83 @@
#include "IMetaTransition.h"
#include "IMetaAnimation.h"
// ************ CMetaTransFactory ************
CMetaTransFactory gMetaTransFactory;
IMetaTransition* CMetaTransFactory::LoadFromStream(IInputStream& rInput)
{
EMetaTransitionType Type = (EMetaTransitionType) rInput.ReadLong();
switch (Type)
{
case eMTT_MetaAnim:
return new CMetaTransMetaAnim(rInput);
case eMTT_Trans:
case eMTT_PhaseTrans:
return new CMetaTransTrans(Type, rInput);
case eMTT_Snap:
return new CMetaTransSnap(rInput);
default:
Log::Error("Unrecognized meta-transition type: " + TString::FromInt32(Type, 0, 10));
return nullptr;
}
}
// ************ CMetaTransMetaAnim ************
CMetaTransMetaAnim::CMetaTransMetaAnim(IInputStream& rInput)
{
mpAnim = gMetaAnimFactory.LoadFromStream(rInput);
}
CMetaTransMetaAnim::~CMetaTransMetaAnim()
{
delete mpAnim;
}
EMetaTransitionType CMetaTransMetaAnim::Type() const
{
return eMTT_MetaAnim;
}
void CMetaTransMetaAnim::GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
{
mpAnim->GetUniquePrimitives(rPrimSet);
}
// ************ CMetaTransTrans ************
CMetaTransTrans::CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput)
{
ASSERT(Type == eMTT_Trans || Type == eMTT_PhaseTrans);
mType = Type;
mUnknownA = rInput.ReadFloat();
mUnknownB = rInput.ReadLong();
mUnknownC = rInput.ReadBool();
mUnknownD = rInput.ReadBool();
mUnknownE = rInput.ReadLong();
}
EMetaTransitionType CMetaTransTrans::Type() const
{
return mType;
}
void CMetaTransTrans::GetUniquePrimitives(std::set<CAnimPrimitive>&) const
{
}
// ************ CMetaTransSnap ************
CMetaTransSnap::CMetaTransSnap(IInputStream&)
{
}
EMetaTransitionType CMetaTransSnap::Type() const
{
return eMTT_Snap;
}
void CMetaTransSnap::GetUniquePrimitives(std::set<CAnimPrimitive>&) const
{
}

View File

@ -0,0 +1,72 @@
#ifndef IMETATRANSITION_H
#define IMETATRANSITION_H
#include "IMetaAnimation.h"
class IMetaAnimation;
class IMetaTransition;
enum EMetaTransitionType
{
eMTT_MetaAnim = 0,
eMTT_Trans = 1,
eMTT_PhaseTrans = 2, // note: structure shared with eMTT_Trans
eMTT_Snap = 3
};
// Factory class
class CMetaTransFactory
{
public:
class IMetaTransition* LoadFromStream(IInputStream& rInput);
};
extern CMetaTransFactory gMetaTransFactory;
// Base MetaTransition interface
class IMetaTransition
{
public:
IMetaTransition() {}
virtual ~IMetaTransition() {}
virtual EMetaTransitionType Type() const = 0;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const = 0;
};
// CMetaTransMetaAnim
class CMetaTransMetaAnim : public IMetaTransition
{
IMetaAnimation *mpAnim;
public:
CMetaTransMetaAnim(IInputStream& rInput);
~CMetaTransMetaAnim();
virtual EMetaTransitionType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
};
// CMetaTransTrans
class CMetaTransTrans : public IMetaTransition
{
EMetaTransitionType mType;
float mUnknownA;
u32 mUnknownB;
bool mUnknownC;
bool mUnknownD;
u32 mUnknownE;
public:
CMetaTransTrans(EMetaTransitionType Type, IInputStream& rInput);
virtual EMetaTransitionType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
};
// CMetaTransSnap
class CMetaTransSnap : public IMetaTransition
{
public:
CMetaTransSnap(IInputStream& rInput);
virtual EMetaTransitionType Type() const;
virtual void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const;
};
#endif // IMETATRANSITION_H

View File

@ -33,7 +33,7 @@ CAnimSet* CAnimSetLoader::LoadReturnsCHAR(IInputStream& rCHAR)
return pSet;
}
void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4)
void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4, SSetCharacter *pChar)
{
// For now, just parse the data; don't store it
rPAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC
@ -68,128 +68,195 @@ void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4)
}
for (u32 iInfo = 0; iInfo < AnimInfoCount; iInfo++)
rPAS4.Seek(0x4 + Skip, SEEK_CUR);
{
u32 MetaAnimID = rPAS4.ReadLong();
rPAS4.Seek(Skip, SEEK_CUR);
pChar->UsedAnimationIndices.insert(MetaAnimID);
}
}
}
void CAnimSetLoader::LoadAnimation(IInputStream& rANCS)
void CAnimSetLoader::LoadAnimationSet(IInputStream& rANCS)
{
TString Name = rANCS.ReadString();
Name = Name;
LoadMetaAnimation(rANCS);
}
u16 Version = rANCS.ReadShort();
void CAnimSetLoader::LoadMetaAnimation(IInputStream& rANCS)
{
u32 Type = rANCS.ReadLong();
switch (Type)
{
case 0:
LoadPrimitive(rANCS);
break;
case 1:
case 2:
LoadBlend(rANCS);
break;
case 3:
LoadRandom(rANCS);
break;
case 4:
LoadSequence(rANCS);
break;
}
}
void CAnimSetLoader::LoadPrimitive(IInputStream& rANCS)
{
u32 AnimID = rANCS.ReadLong();
u32 AnimIndex = rANCS.ReadLong();
TString AnimName = rANCS.ReadString();
rANCS.Seek(0x8, SEEK_CUR);
ASSERT(AnimIndex < mAnimPrimitives.size());
if (!mAnimPrimitives[AnimIndex].Loaded)
{
mAnimPrimitives[AnimIndex].Index = AnimIndex;
mAnimPrimitives[AnimIndex].AnimID = AnimID;
mAnimPrimitives[AnimIndex].Name = AnimName;
mAnimPrimitives[AnimIndex].Loaded = true;
}
}
void CAnimSetLoader::LoadBlend(IInputStream& rANCS)
{
LoadMetaAnimation(rANCS);
LoadMetaAnimation(rANCS);
rANCS.Seek(0x5, SEEK_CUR);
}
void CAnimSetLoader::LoadRandom(IInputStream& rANCS)
{
// Animations
u32 NumAnims = rANCS.ReadLong();
pSet->mAnimations.reserve(NumAnims);
for (u32 iAnim = 0; iAnim < NumAnims; iAnim++)
LoadAnimProbabilityPair(rANCS);
}
void CAnimSetLoader::LoadAnimProbabilityPair(IInputStream& rANCS)
{
LoadMetaAnimation(rANCS);
rANCS.Seek(0x4, SEEK_CUR);
}
void CAnimSetLoader::LoadSequence(IInputStream& rANCS)
{
u32 NumAnims = rANCS.ReadLong();
for (u32 iAnim = 0; iAnim < NumAnims; iAnim++)
LoadMetaAnimation(rANCS);
}
void CAnimSetLoader::LoadTransition(IInputStream& rANCS)
{
rANCS.Seek(0xC, SEEK_CUR);
LoadMetaTransition(rANCS);
}
void CAnimSetLoader::LoadMetaTransition(IInputStream& rANCS)
{
u32 Type = rANCS.ReadLong();
switch (Type)
{
case 0:
LoadTransAnimation(rANCS);
break;
case 1:
case 2:
LoadTransTransition(rANCS);
break;
case 3:
break;
SAnimation Anim;
Anim.Name = rANCS.ReadString();
Anim.pMetaAnim = gMetaAnimFactory.LoadFromStream(rANCS);
pSet->mAnimations.push_back(Anim);
}
// Transitions
u32 NumTransitions = rANCS.ReadLong();
pSet->mTransitions.reserve(NumTransitions);
for (u32 iTrans = 0; iTrans < NumTransitions; iTrans++)
{
STransition Trans;
Trans.Unknown = rANCS.ReadLong();
Trans.AnimIdA = rANCS.ReadLong();
Trans.AnimIdB = rANCS.ReadLong();
Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS);
pSet->mTransitions.push_back(Trans);
}
pSet->mpDefaultTransition = gMetaTransFactory.LoadFromStream(rANCS);
// Additive Animations
u32 NumAdditive = rANCS.ReadLong();
pSet->mAdditiveAnims.reserve(NumAdditive);
for (u32 iAdd = 0; iAdd < NumAdditive; iAdd++)
{
SAdditiveAnim Anim;
Anim.AnimID = rANCS.ReadLong();
Anim.FadeInTime = rANCS.ReadFloat();
Anim.FadeOutTime = rANCS.ReadFloat();
pSet->mAdditiveAnims.push_back(Anim);
}
pSet->mDefaultAdditiveFadeIn = rANCS.ReadFloat();
pSet->mDefaultAdditiveFadeOut = rANCS.ReadFloat();
// Half Transitions
if (Version > 2)
{
u32 NumHalfTransitions = rANCS.ReadLong();
pSet->mHalfTransitions.reserve(NumHalfTransitions);
for (u32 iTrans = 0; iTrans < NumHalfTransitions; iTrans++)
{
SHalfTransition Trans;
Trans.AnimID = rANCS.ReadLong();
Trans.pMetaTrans = gMetaTransFactory.LoadFromStream(rANCS);
pSet->mHalfTransitions.push_back(Trans);
}
}
// Skipping MP1 ANIM asset list
// Events
if (mVersion >= eEchoesDemo)
{
u32 EventDataCount = rANCS.ReadLong();
pSet->mAnimEvents.reserve(EventDataCount);
ASSERT(EventDataCount == NumAnims);
for (u32 iEvent = 0; iEvent < EventDataCount; iEvent++)
{
CAnimEventData *pData = CAnimEventLoader::LoadAnimSetEvents(rANCS);
pSet->mAnimEvents.push_back(pData);
}
}
}
void CAnimSetLoader::LoadTransAnimation(IInputStream& rANCS)
void CAnimSetLoader::ProcessPrimitives()
{
LoadMetaAnimation(rANCS);
}
// Find all unique anim primitives
std::set<CAnimPrimitive> UniquePrimitives;
void CAnimSetLoader::LoadTransTransition(IInputStream& rANCS)
{
rANCS.Seek(0xE, SEEK_CUR);
}
for (u32 iAnim = 0; iAnim < pSet->mAnimations.size(); iAnim++)
pSet->mAnimations[iAnim].pMetaAnim->GetUniquePrimitives(UniquePrimitives);
void CAnimSetLoader::LoadAdditiveAnimation(IInputStream& rANCS)
{
rANCS.Seek(0xC, SEEK_CUR);
}
for (u32 iTrans = 0; iTrans < pSet->mTransitions.size(); iTrans++)
pSet->mTransitions[iTrans].pMetaTrans->GetUniquePrimitives(UniquePrimitives);
void CAnimSetLoader::LoadHalfTransition(IInputStream& rANCS)
{
rANCS.Seek(0x4, SEEK_CUR);
LoadMetaTransition(rANCS);
pSet->mpDefaultTransition->GetUniquePrimitives(UniquePrimitives);
for (u32 iTrans = 0; iTrans < pSet->mHalfTransitions.size(); iTrans++)
pSet->mHalfTransitions[iTrans].pMetaTrans->GetUniquePrimitives(UniquePrimitives);
// Copy anim primitives into the animset
for (auto Iter = UniquePrimitives.begin(); Iter != UniquePrimitives.end(); Iter++)
{
const CAnimPrimitive& rkPrim = *Iter;
u32 ID = rkPrim.ID();
if (ID >= pSet->mAnimPrimitives.size())
pSet->mAnimPrimitives.resize(ID + 1);
pSet->mAnimPrimitives[ID] = rkPrim;
ASSERT(pSet->Animation(ID)->pMetaAnim->Type() == eMAT_Play);
}
// Add animations referenced by default transition
std::set<CAnimPrimitive> DefaultTransPrimitives;
pSet->mpDefaultTransition->GetUniquePrimitives(DefaultTransPrimitives);
for (u32 iChar = 0; iChar < pSet->mCharacters.size(); iChar++)
{
SSetCharacter& rChar = pSet->mCharacters[iChar];
for (auto Iter = DefaultTransPrimitives.begin(); Iter != DefaultTransPrimitives.end(); Iter++)
{
const CAnimPrimitive& rkPrim = *Iter;
rChar.UsedAnimationIndices.insert(rkPrim.ID());
}
}
// Add animations referenced by used transitions
for (u32 iChar = 0; iChar < pSet->mCharacters.size(); iChar++)
{
SSetCharacter& rChar = pSet->mCharacters[iChar];
bool AddedNewAnims = true;
// Loop this until we run out of new animations. This is in case any animations
// referenced by any transitions are also referenced by earlier transitions.
while (AddedNewAnims)
{
AddedNewAnims = false;
for (u32 iTrans = 0; iTrans < pSet->mTransitions.size(); iTrans++)
{
STransition& rTrans = pSet->mTransitions[iTrans];
if ( rChar.UsedAnimationIndices.find(rTrans.AnimIdA) != rChar.UsedAnimationIndices.end() &&
rChar.UsedAnimationIndices.find(rTrans.AnimIdB) != rChar.UsedAnimationIndices.end() )
{
std::set<CAnimPrimitive> Primitives;
rTrans.pMetaTrans->GetUniquePrimitives(Primitives);
for (auto Iter = Primitives.begin(); Iter != Primitives.end(); Iter++)
{
const CAnimPrimitive& rkPrim = *Iter;
if (rChar.UsedAnimationIndices.find(rkPrim.ID()) == rChar.UsedAnimationIndices.end())
{
rChar.UsedAnimationIndices.insert(rkPrim.ID());
AddedNewAnims = true;
}
}
}
}
for (u32 iHalf = 0; iHalf < pSet->mHalfTransitions.size(); iHalf++)
{
SHalfTransition& rTrans = pSet->mHalfTransitions[iHalf];
if (rChar.UsedAnimationIndices.find(rTrans.AnimID) != rChar.UsedAnimationIndices.end())
{
std::set<CAnimPrimitive> Primitives;
rTrans.pMetaTrans->GetUniquePrimitives(Primitives);
for (auto Iter = Primitives.begin(); Iter != Primitives.end(); Iter++)
{
const CAnimPrimitive& rkPrim = *Iter;
if (rChar.UsedAnimationIndices.find(rkPrim.ID()) == rChar.UsedAnimationIndices.end())
{
rChar.UsedAnimationIndices.insert(rkPrim.ID());
AddedNewAnims = true;
}
}
}
}
}
}
}
// ************ STATIC ************
@ -254,7 +321,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
}
// PAS Database
Loader.LoadPASDatabase(rANCS);
Loader.LoadPASDatabase(rANCS, pChar);
// Particles
u32 ParticleCount = rANCS.ReadLong();
@ -334,61 +401,8 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
}
// Load Animation Set
u32 SetStart = rANCS.Tell();
SetStart = SetStart;
u16 InfoCount = rANCS.ReadShort();
u32 NumAnims = rANCS.ReadLong();
Loader.mAnimPrimitives.resize(NumAnims);
for (u32 iAnim = 0; iAnim < NumAnims; iAnim++)
Loader.LoadAnimation(rANCS);
u32 NumTransitions = rANCS.ReadLong();
for (u32 iTrans = 0; iTrans < NumTransitions; iTrans++)
Loader.LoadTransition(rANCS);
Loader.LoadMetaTransition(rANCS);
u32 NumAdditiveAnims = rANCS.ReadLong();
for (u32 iAnim = 0; iAnim < NumAdditiveAnims; iAnim++)
Loader.LoadAdditiveAnimation(rANCS);
rANCS.Seek(0x8, SEEK_CUR);
if (InfoCount > 2)
{
u32 NumHalfTransitions = rANCS.ReadLong();
for (u32 iHalf = 0; iHalf < NumHalfTransitions; iHalf++)
Loader.LoadHalfTransition(rANCS);
}
// Add anims to set
Loader.pSet->mAnimations.resize(Loader.mAnimPrimitives.size());
for (u32 iPrim = 0; iPrim < Loader.mAnimPrimitives.size(); iPrim++)
{
SPrimitive& rPrim = Loader.mAnimPrimitives[iPrim];
if (rPrim.Loaded)
{
SSetAnimation Anim;
Anim.Name = rPrim.Name;
Anim.pAnim = gpResourceStore->LoadResource(rPrim.AnimID, "ANIM");
Loader.pSet->mAnimations[iPrim] = Anim;
}
}
// Parse anim event data
if (Loader.pSet->Game() >= eEchoesDemo)
{
u32 EventDataCount = rANCS.ReadLong();
ASSERT(EventDataCount == NumAnims);
for (u32 iEvnt = 0; iEvnt < EventDataCount; iEvnt++)
Loader.pSet->mAnimations[iEvnt].pEventData = CAnimEventLoader::LoadAnimSetEvents(rANCS);
}
Loader.LoadAnimationSet(rANCS);
Loader.ProcessPrimitives();
return Loader.pSet;
}

View File

@ -12,33 +12,10 @@ class CAnimSetLoader
CAnimSetLoader();
CAnimSet* LoadCorruptionCHAR(IInputStream& rCHAR);
CAnimSet* LoadReturnsCHAR(IInputStream& rCHAR);
void LoadPASDatabase(IInputStream& rPAS4);
void LoadPASDatabase(IInputStream& rPAS4, SSetCharacter *pChar);
// Animation Set load functions
struct SPrimitive
{
u32 Index;
u32 AnimID;
TString Name;
bool Loaded;
SPrimitive() : Loaded(false) {}
};
std::vector<SPrimitive> mAnimPrimitives;
void LoadAnimation(IInputStream& rANCS);
void LoadMetaAnimation(IInputStream& rANCS);
void LoadPrimitive(IInputStream& rANCS);
void LoadBlend(IInputStream& rANCS);
void LoadRandom(IInputStream& rANCS);
void LoadAnimProbabilityPair(IInputStream& rANCS);
void LoadSequence(IInputStream& rANCS);
void LoadTransition(IInputStream& rANCS);
void LoadMetaTransition(IInputStream& rANCS);
void LoadTransAnimation(IInputStream& rANCS);
void LoadTransTransition(IInputStream& rANCS);
void LoadAdditiveAnimation(IInputStream& rANCS);
void LoadHalfTransition(IInputStream& rANCS);
void LoadAnimationSet(IInputStream& rANCS);
void ProcessPrimitives();
public:
static CAnimSet* LoadANCSOrCHAR(IInputStream& rFile, CResourceEntry *pEntry);

View File

@ -202,7 +202,7 @@ void CAnimationLoader::ReadUncompressedANIM()
if (mGame == ePrime)
{
mpAnim->mEventData = mpInput->ReadLong();
mpAnim->mpEventData = gpResourceStore->LoadResource(mpInput->ReadLong(), "EVNT");
}
}
@ -219,7 +219,7 @@ void CAnimationLoader::ReadCompressedANIM()
if (mGame == ePrime)
{
mpAnim->mEventData = mpInput->ReadLong();
mpAnim->mpEventData = gpResourceStore->LoadResource(mpInput->ReadLong(), "EVNT");
mpInput->Seek(0x4, SEEK_CUR); // Skip unknown
}
else mpInput->Seek(0x2, SEEK_CUR); // Skip unknowns

View File

@ -33,7 +33,7 @@ public:
inline CAnimSet* Character() const { return mpCharacter; }
inline u32 ActiveCharIndex() const { return mActiveCharSet; }
inline u32 ActiveAnimIndex() const { return mActiveAnim; }
inline CAnimation* CurrentAnim() const { return (mAnimated && mpCharacter ? mpCharacter->Animation(mActiveAnim)->pAnim : nullptr); }
inline CAnimation* CurrentAnim() const { return (mAnimated && mpCharacter ? mpCharacter->FindAnimationAsset(mActiveAnim) : nullptr); }
inline bool IsAnimated() const { return (mAnimated && CurrentAnim() != nullptr); }
void SetAnimated(bool Animated) { mAnimated = Animated; SetDirty(); }

View File

@ -679,8 +679,7 @@ CSkeleton* CScriptNode::ActiveSkeleton() const
CAnimation* CScriptNode::ActiveAnimation() const
{
CAnimSet *pSet = ActiveAnimSet();
if (pSet) return pSet->Animation(mAnimIndex)->pAnim;
else return nullptr;
return pSet ? pSet->FindAnimationAsset(mAnimIndex) : nullptr;
}
CTexture* CScriptNode::ActiveBillboard() const

View File

@ -160,18 +160,12 @@ void CCharacterEditor::UpdateCameraOrbit()
CSkeleton* CCharacterEditor::CurrentSkeleton() const
{
if (mpSet)
return mpSet->Character(mCurrentChar)->pSkeleton;
else
return nullptr;
return mpSet ? mpSet->Character(mCurrentChar)->pSkeleton : nullptr;
}
CAnimation* CCharacterEditor::CurrentAnimation() const
{
if (mpSet)
return mpSet->Animation(mCurrentAnim)->pAnim;
else
return nullptr;
return mpSet ? mpSet->FindAnimationAsset(mCurrentAnim) : nullptr;
}
void CCharacterEditor::SetActiveAnimSet(CAnimSet *pSet)
@ -385,7 +379,7 @@ void CCharacterEditor::SetAnimTime(float Time)
mpCharNode->SetAnimTime(Time);
CAnimation *pAnim = (mpSet ? mpSet->Animation(mCurrentAnim)->pAnim : nullptr);
CAnimation *pAnim = CurrentAnimation();
u32 NumKeys = 1, CurKey = 0;
if (pAnim)