Added support for loading uncompressed animations and playing them back

This commit is contained in:
parax0
2016-04-07 12:15:24 -06:00
parent 2376a36f0b
commit ef523c2339
22 changed files with 399 additions and 18 deletions

View File

@@ -2,6 +2,7 @@
#define CANIMSET_H
#include "TResPtr.h"
#include "CAnimation.h"
#include "CResource.h"
#include "CSkeleton.h"
#include "Core/Resource/Model/CModel.h"
@@ -26,6 +27,13 @@ class CAnimSet : public CResource
};
std::vector<SNode> mNodes;
struct SAnimation
{
TString Name;
TResPtr<CAnimation> pAnim;
};
std::vector<SAnimation> mAnims;
public:
CAnimSet() : CResource() {}
@@ -33,6 +41,10 @@ public:
TString NodeName(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].Name; }
CModel* NodeModel(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pModel; }
CSkeleton* NodeSkeleton(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pSkeleton; }
u32 NumAnims() const { return mAnims.size(); }
CAnimation* Animation(u32 Index) { if (Index >= mAnims.size()) Index = 0; return mAnims[Index].pAnim; }
TString AnimName(u32 Index) { if (Index >= mAnims.size()) Index = 0; return mAnims[Index].Name; }
};
#endif // CCHARACTERSET_H

View File

@@ -0,0 +1,37 @@
#include "CAnimation.h"
#include <Math/CTransform4f.h>
#include <Math/MathUtil.h>
CAnimation::CAnimation()
: mDuration(0.f)
, mTickInterval(0.0333333f)
, mNumKeys(0)
{
}
void CAnimation::EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const
{
if (mDuration == 0.f) return;
Time = fmodf(Time, mDuration);
if (Time >= FLT_EPSILON) Time -= FLT_EPSILON;
float t = fmodf(Time, mTickInterval) / mTickInterval;
u32 LowKey = (u32) (Time / mTickInterval);
u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx;
u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx;
if (RotChannel != 0xFF)
{
const CQuaternion& rkLow = mRotationChannels[RotChannel][LowKey];
const CQuaternion& rkHigh = mRotationChannels[RotChannel][LowKey + 1];
rOut.Rotate( rkLow.Slerp(rkHigh, t) );
}
if (TransChannel != 0xFF)
{
const CVector3f& rkLow = mTranslationChannels[TransChannel][LowKey];
const CVector3f& rkHigh = mTranslationChannels[TransChannel][LowKey + 1];
rOut.Translate( Math::Lerp<CVector3f>(rkLow, rkHigh, t) );
}
}

View File

@@ -0,0 +1,36 @@
#ifndef CANIMATION_H
#define CANIMATION_H
#include "CResource.h"
#include <Math/CQuaternion.h>
#include <Math/CVector3f.h>
#include <vector>
class CAnimation : public CResource
{
DECLARE_RESOURCE_TYPE(eAnimation)
friend class CAnimationLoader;
typedef std::vector<CQuaternion> TRotationChannel;
typedef std::vector<CVector3f> TTranslationChannel;
float mDuration;
float mTickInterval;
u32 mNumKeys;
std::vector<TRotationChannel> mRotationChannels;
std::vector<TTranslationChannel> mTranslationChannels;
struct SBoneChannelInfo
{
u8 RotationChannelIdx;
u8 TranslationChannelIdx;
};
std::vector<SBoneChannelInfo> mBoneInfo;
public:
CAnimation();
void EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const;
};
#endif // CANIMATION_H

View File

@@ -1,5 +1,6 @@
#include "CResCache.h"
#include "Core/Resource/Factory/CAreaLoader.h"
#include "Core/Resource/Factory/CAnimationLoader.h"
#include "Core/Resource/Factory/CAnimSetLoader.h"
#include "Core/Resource/Factory/CCollisionLoader.h"
#include "Core/Resource/Factory/CFontLoader.h"
@@ -151,6 +152,7 @@ CResource* CResCache::GetResource(CUniqueID ResID, CFourCC Type)
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(Mem);
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(Mem);
else SupportedFormat = false;
// Log errors
@@ -206,6 +208,7 @@ CResource* CResCache::GetResource(const TString& rkResPath)
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(File);
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(File);
else SupportedFormat = false;
if (!pRes) pRes = new CResource(); // Default for unsupported formats

View File

@@ -8,6 +8,43 @@ CBone::CBone(CSkeleton *pSkel)
{
}
void CBone::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot)
{
mAnimTransform = CTransform4f::skIdentity;
if (pAnim)
pAnim->EvaluateTransform(Time, mID, mAnimTransform);
mAnimTransform.Translate(mPosition);
if (mpParent)
mAnimTransform = mpParent->AnimTransform() * mAnimTransform;
if (AnchorRoot && IsRoot())
mAnimTransform.ZeroTranslation();
mAbsPosDirty = true;
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
mChildren[iChild]->UpdateTransform(pAnim, Time, AnchorRoot);
}
bool CBone::IsRoot() const
{
return (mpParent == nullptr);
}
CVector3f CBone::AbsolutePosition() const
{
if (mAbsPosDirty)
{
mAbsolutePosition = (mAnimTransform * CVector3f::skZero);
mAbsPosDirty = false;
}
return mAbsolutePosition;
}
// ************ CSkeleton ************
CSkeleton::CSkeleton()
: mpRootBone(nullptr)
@@ -31,6 +68,11 @@ CBone* CSkeleton::BoneByID(u32 BoneID) const
return nullptr;
}
void CSkeleton::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot)
{
mpRootBone->UpdateTransform(pAnim, Time, AnchorRoot);
}
void CSkeleton::Draw(FRenderOptions /*Options*/)
{
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
@@ -39,8 +81,8 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
// Draw bone
CTransform4f Transform;
Transform.Scale(0.01f);
Transform.Translate(pBone->Position());
Transform.Scale(0.025f);
Transform.Translate(pBone->AbsolutePosition());
CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f();
CGraphics::UpdateMVPBlock();
CDrawUtil::DrawSphere(CColor::skWhite);
@@ -50,6 +92,6 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
CGraphics::UpdateMVPBlock();
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
CDrawUtil::DrawLine(pBone->Position(), pBone->ChildByIndex(iChild)->Position());
CDrawUtil::DrawLine(pBone->AbsolutePosition(), pBone->ChildByIndex(iChild)->AbsolutePosition());
}
}

View File

@@ -1,10 +1,12 @@
#ifndef CSKELETON_H
#define CSKELETON_H
#include "CAnimation.h"
#include "CResource.h"
#include "Core/Render/FRenderOptions.h"
#include <Common/TString.h>
#include <Common/types.h>
#include <Math/CTransform4f.h>
#include <Math/CVector3f.h>
class CSkeleton;
@@ -20,14 +22,24 @@ class CBone
CVector3f mPosition;
TString mName;
CTransform4f mAnimTransform;
mutable bool mAbsPosDirty;
mutable CVector3f mAbsolutePosition;
public:
CBone(CSkeleton *pSkel);
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
bool IsRoot() const;
// Accessors
inline u32 ID() const { return mID; }
inline CVector3f Position() const { return mPosition; }
inline u32 NumChildren() const { return mChildren.size(); }
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
inline u32 ID() const { return mID; }
inline CVector3f Position() const { return mPosition; }
inline CBone* Parent() const { return mpParent; }
inline u32 NumChildren() const { return mChildren.size(); }
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
inline const CTransform4f& AnimTransform() const { return mAnimTransform; }
CVector3f AbsolutePosition() const;
};
class CSkeleton : public CResource
@@ -41,7 +53,7 @@ class CSkeleton : public CResource
public:
CSkeleton();
~CSkeleton();
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
CBone* BoneByID(u32 BoneID) const;
void Draw(FRenderOptions Options);
};

View File

@@ -104,11 +104,15 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS)
// Unfortunately that's all that's actually supported at the moment. Hope to expand later.
// Since there's no size value I have to actually read the rest of the node to reach the next one
u32 AnimCount = rANCS.ReadLong();
bool ReadAnimNames = Loader.pSet->mAnims.empty();
if (ReadAnimNames) Loader.pSet->mAnims.resize(AnimCount);
for (u32 iAnim = 0; iAnim < AnimCount; iAnim++)
{
rANCS.Seek(0x4, SEEK_CUR);
if (Loader.mVersion == ePrime) rANCS.Seek(0x1, SEEK_CUR);
rANCS.ReadString();
TString AnimName = rANCS.ReadString();
if (ReadAnimNames) Loader.pSet->mAnims[iAnim].Name = AnimName;
}
// PAS Database
@@ -165,7 +169,6 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS)
u32 UnknownCount2 = rANCS.ReadLong();
rANCS.Seek(UnknownCount2 * 0x1C, SEEK_CUR);
}
// Lots of work for data I'm not even using x.x
}
return Loader.pSet;

View File

@@ -0,0 +1,105 @@
#include "CAnimationLoader.h"
#include <Common/Log.h>
void CAnimationLoader::ReadUncompressedANIM()
{
mpAnim->mDuration = mpInput->ReadFloat();
mpInput->Seek(0x4, SEEK_CUR); // Skip unknown
mpAnim->mTickInterval = mpInput->ReadFloat();
mpInput->Seek(0x4, SEEK_CUR); // Skip unknown
mpAnim->mNumKeys = mpInput->ReadLong();
mpInput->Seek(0x4, SEEK_CUR); // Skip unknown
// Read bone channel info
u32 NumRotationChannels = 0;
u32 NumTranslationChannels = 0;
u32 NumRotIndices = mpInput->ReadLong();
std::vector<u8> RotIndices(NumRotIndices);
for (u32 iRot = 0; iRot < NumRotIndices; iRot++)
{
RotIndices[iRot] = mpInput->ReadByte();
if (RotIndices[iRot] != 0xFF)
NumRotationChannels++;
}
u32 NumTransIndices = mpInput->ReadLong();
std::vector<u8> TransIndices(NumTransIndices);
for (u32 iTrans = 0; iTrans < NumTransIndices; iTrans++)
{
TransIndices[iTrans] = mpInput->ReadByte();
if (TransIndices[iTrans] != 0xFF)
NumTranslationChannels++;
}
// Set up bone channel info
mpAnim->mBoneInfo.resize(NumRotIndices);
for (u32 iRot = 0, iTrans = 0; iRot < NumRotIndices; iRot++)
{
u8 RotIdx = RotIndices[iRot];
mpAnim->mBoneInfo[iRot].RotationChannelIdx = RotIdx;
if (RotIdx != 0xFF)
{
mpAnim->mBoneInfo[iRot].TranslationChannelIdx = TransIndices[iTrans];
iTrans++;
}
else
mpAnim->mBoneInfo[iRot].TranslationChannelIdx = 0xFF;
}
// Read bone transforms
mpInput->Seek(0x4, SEEK_CUR); // Skipping quaternion count
mpAnim->mRotationChannels.resize(NumRotationChannels);
for (u32 iRot = 0; iRot < NumRotationChannels; iRot++)
{
mpAnim->mRotationChannels[iRot].resize(mpAnim->mNumKeys);
for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++)
mpAnim->mRotationChannels[iRot][iKey] = CQuaternion(*mpInput);
}
mpInput->Seek(0x4, SEEK_CUR); // Skipping vector3f count
mpAnim->mTranslationChannels.resize(NumTranslationChannels);
for (u32 iTrans = 0; iTrans < NumTranslationChannels; iTrans++)
{
mpAnim->mTranslationChannels[iTrans].resize(mpAnim->mNumKeys);
for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++)
mpAnim->mTranslationChannels[iTrans][iKey] = CVector3f(*mpInput);
}
// Skip EVNT file
}
// ************ STATIC ************
CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM)
{
u32 CompressionType = rANIM.ReadLong();
if (CompressionType != 0 && CompressionType != 2)
{
Log::FileError(rANIM.GetSourceString(), "Unknown ANIM compression type: " + TString::HexString(CompressionType, 2));
return nullptr;
}
if (CompressionType == 2)
{
Log::FileError(rANIM.GetSourceString(), "Compressed ANIMs not supported yet");
return nullptr;
}
CAnimationLoader Loader;
Loader.mpAnim = new CAnimation();
Loader.mpInput = &rANIM;
Loader.ReadUncompressedANIM();
return Loader.mpAnim;
}

View File

@@ -0,0 +1,19 @@
#ifndef CANIMATIONLOADER_H
#define CANIMATIONLOADER_H
#include "Core/Resource/TResPtr.h"
#include "Core/Resource/CAnimation.h"
class CAnimationLoader
{
TResPtr<CAnimation> mpAnim;
IInputStream *mpInput;
CAnimationLoader() {}
void ReadUncompressedANIM();
public:
static CAnimation* LoadANIM(IInputStream& rANIM);
};
#endif // CANIMATIONLOADER_H

View File

@@ -3,13 +3,19 @@
#include <vector>
CSkeletonLoader::CSkeletonLoader()
void CSkeletonLoader::SetLocalBoneCoords(CBone *pBone)
{
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
SetLocalBoneCoords(pBone->ChildByIndex(iChild));
if (pBone->mpParent)
pBone->mPosition -= pBone->mpParent->mPosition;
}
// ************ STATIC ************
CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
{
CSkeletonLoader Loader;
CSkeleton *pSkel = new CSkeleton();
u32 NumBones = rCINF.ReadLong();
@@ -71,6 +77,8 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF)
}
}
Loader.SetLocalBoneCoords(pSkel->mpRootBone);
// Skip bone ID array
u32 NumBoneIDs = rCINF.ReadLong();
rCINF.Seek(NumBoneIDs * 4, SEEK_CUR);

View File

@@ -10,7 +10,8 @@ class CSkeletonLoader
TResPtr<CSkeleton> mpSkeleton;
EGame mVersion;
CSkeletonLoader();
CSkeletonLoader() {}
void SetLocalBoneCoords(CBone *pBone);
public:
static CSkeleton* LoadCINF(IInputStream& rCINF);