mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-21 02:39:17 +00:00
Added support for loading uncompressed animations and playing them back
This commit is contained in:
@@ -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
|
||||
|
||||
37
src/Core/Resource/CAnimation.cpp
Normal file
37
src/Core/Resource/CAnimation.cpp
Normal 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) );
|
||||
}
|
||||
}
|
||||
36
src/Core/Resource/CAnimation.h
Normal file
36
src/Core/Resource/CAnimation.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
105
src/Core/Resource/Factory/CAnimationLoader.cpp
Normal file
105
src/Core/Resource/Factory/CAnimationLoader.cpp
Normal 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;
|
||||
}
|
||||
19
src/Core/Resource/Factory/CAnimationLoader.h
Normal file
19
src/Core/Resource/Factory/CAnimationLoader.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -10,7 +10,8 @@ class CSkeletonLoader
|
||||
TResPtr<CSkeleton> mpSkeleton;
|
||||
EGame mVersion;
|
||||
|
||||
CSkeletonLoader();
|
||||
CSkeletonLoader() {}
|
||||
void SetLocalBoneCoords(CBone *pBone);
|
||||
|
||||
public:
|
||||
static CSkeleton* LoadCINF(IInputStream& rCINF);
|
||||
|
||||
Reference in New Issue
Block a user